Skip to content
This repository was archived by the owner on Jun 1, 2025. It is now read-only.

Commit 43db316

Browse files
authored
Merge pull request #205 from ghiscoding/feat/cell-copy-events
feat(copy): add ExcelCopyBufferOptions to grid option, ref #198
2 parents bd92b34 + dee9e04 commit 43db316

File tree

6 files changed

+174
-43
lines changed

6 files changed

+174
-43
lines changed

src/app/examples/grid-formatter.component.ts

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { Component, OnInit } from '@angular/core';
2-
import { Column, FieldType, Formatter, Formatters, GridOption } from './../modules/angular-slickgrid';
2+
import { Column, FieldType, Formatter, Formatters, GridOption, SelectedRange } from './../modules/angular-slickgrid';
33

44
// create my custom Formatter with the Formatter type
55
const myCustomCheckmarkFormatter: Formatter = (row, cell, value, columnDef, dataContext) => {
@@ -46,13 +46,21 @@ export class GridFormatterComponent implements OnInit {
4646
},
4747
enableAutoResize: true,
4848
enableCellNavigation: true,
49-
enableExcelCopyBuffer: true,
49+
5050
// you customize the date separator through "formatterOptions"
5151
/*
5252
formatterOptions: {
5353
dateSeparator: '.'
5454
},
5555
*/
56+
57+
// when using the ExcelCopyBuffer, you can see what the selection range is
58+
enableExcelCopyBuffer: true,
59+
excelCopyBufferOptions: {
60+
onCopyCells: (e, args: { ranges: SelectedRange[] }) => console.log('onCopyCells', args.ranges),
61+
onPasteCells: (e, args: { ranges: SelectedRange[] }) => console.log('onPasteCells', args.ranges),
62+
onCopyCancelled: (e, args: { ranges: SelectedRange[] }) => console.log('onCopyCancelled', args.ranges),
63+
}
5664
};
5765

5866
// mock a dataset

src/app/modules/angular-slickgrid/extensions/cellExternalCopyManagerExtension.ts

Lines changed: 76 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { Injectable } from '@angular/core';
2-
import { Column, Extension, ExtensionName } from '../models/index';
2+
import { Column, ExcelCopyBufferOption, Extension, ExtensionName, SelectedRange } from '../models/index';
33
import { ExtensionUtility } from './extensionUtility';
44
import { sanitizeHtmlToText } from '../services/utilities';
55
import { SharedService } from '../services/shared.service';
@@ -10,12 +10,15 @@ declare var $: any;
1010

1111
@Injectable()
1212
export class CellExternalCopyManagerExtension implements Extension {
13+
private _eventHandler: any = new Slick.EventHandler();
1314
private _extension: any;
1415
private _undoRedoBuffer: any;
1516

1617
constructor(private extensionUtility: ExtensionUtility, private sharedService: SharedService) { }
1718

1819
dispose() {
20+
// unsubscribe all SlickGrid events
21+
this._eventHandler.unsubscribeAll();
1922
if (this._extension && this._extension.destroy) {
2023
this._extension.destroy();
2124
}
@@ -27,49 +30,34 @@ export class CellExternalCopyManagerExtension implements Extension {
2730
this.extensionUtility.loadExtensionDynamically(ExtensionName.cellExternalCopyManager);
2831
this.createUndoRedoBuffer();
2932
this.hookUndoShortcutKey();
30-
let newRowIds = 0;
31-
const pluginOptions = {
32-
clipboardCommandHandler: (editCommand: any) => {
33-
this._undoRedoBuffer.queueAndExecuteCommand.call(this._undoRedoBuffer, editCommand);
34-
},
35-
dataItemColumnValueExtractor: (item: any, columnDef: Column) => {
36-
// when grid or cell is not editable, we will possibly evaluate the Formatter if it was passed
37-
// to decide if we evaluate the Formatter, we will use the same flag from Export which is "exportWithFormatter"
38-
if (!this.sharedService.gridOptions.editable || !columnDef.editor) {
39-
const isEvaluatingFormatter = (columnDef.exportWithFormatter !== undefined) ? columnDef.exportWithFormatter : (this.sharedService.gridOptions.exportOptions && this.sharedService.gridOptions.exportOptions.exportWithFormatter);
40-
if (columnDef.formatter && isEvaluatingFormatter) {
41-
const formattedOutput = columnDef.formatter(0, 0, item[columnDef.field], columnDef, item, this.sharedService.grid);
42-
if (columnDef.sanitizeDataExport || (this.sharedService.gridOptions.exportOptions && this.sharedService.gridOptions.exportOptions.sanitizeDataExport)) {
43-
let outputString = formattedOutput as string;
44-
if (formattedOutput && typeof formattedOutput === 'object' && formattedOutput.hasOwnProperty('text')) {
45-
outputString = formattedOutput.text;
46-
}
47-
if (outputString === null) {
48-
outputString = '';
49-
}
50-
return sanitizeHtmlToText(outputString);
51-
}
52-
return formattedOutput;
53-
}
54-
}
55-
// else use the default "dataItemColumnValueExtractor" from the plugin itself
56-
// we can do that by setting back the getter with null
57-
return null;
58-
},
59-
readOnlyMode: false,
60-
includeHeaderWhenCopying: false,
61-
newRowCreator: (count: number) => {
62-
for (let i = 0; i < count; i++) {
63-
const item = {
64-
id: 'newRow_' + newRowIds++
65-
};
66-
this.sharedService.grid.getData().addItem(item);
67-
}
68-
}
69-
};
33+
34+
const pluginOptions = { ...this.getDefaultOptions(), ...this.sharedService.gridOptions.excelCopyBufferOptions } as ExcelCopyBufferOption;
7035
this.sharedService.grid.setSelectionModel(new Slick.CellSelectionModel());
7136
this._extension = new Slick.CellExternalCopyManager(pluginOptions);
7237
this.sharedService.grid.registerPlugin(this._extension);
38+
39+
// hook to all possible events
40+
if (this.sharedService.grid && this.sharedService.gridOptions.excelCopyBufferOptions) {
41+
if (this.sharedService.gridOptions.excelCopyBufferOptions.onExtensionRegistered) {
42+
this.sharedService.gridOptions.excelCopyBufferOptions.onExtensionRegistered(this._extension);
43+
}
44+
this._eventHandler.subscribe(this._extension.onCopyCells, (e: any, args: { ranges: SelectedRange[] }) => {
45+
if (this.sharedService.gridOptions.excelCopyBufferOptions && typeof this.sharedService.gridOptions.excelCopyBufferOptions.onCopyCells === 'function') {
46+
this.sharedService.gridOptions.excelCopyBufferOptions.onCopyCells(e, args);
47+
}
48+
});
49+
this._eventHandler.subscribe(this._extension.onCopyCancelled, (e: any, args: { ranges: SelectedRange[] }) => {
50+
if (this.sharedService.gridOptions.excelCopyBufferOptions && typeof this.sharedService.gridOptions.excelCopyBufferOptions.onCopyCancelled === 'function') {
51+
this.sharedService.gridOptions.excelCopyBufferOptions.onCopyCancelled(e, args);
52+
}
53+
});
54+
this._eventHandler.subscribe(this._extension.onPasteCells, (e: any, args: { ranges: SelectedRange[] }) => {
55+
if (this.sharedService.gridOptions.excelCopyBufferOptions && typeof this.sharedService.gridOptions.excelCopyBufferOptions.onPasteCells === 'function') {
56+
this.sharedService.gridOptions.excelCopyBufferOptions.onPasteCells(e, args);
57+
}
58+
});
59+
}
60+
7361
return this._extension;
7462
}
7563
return null;
@@ -104,6 +92,53 @@ export class CellExternalCopyManagerExtension implements Extension {
10492
};
10593
}
10694

95+
/**
96+
* @return default plugin (addon) options
97+
*/
98+
private getDefaultOptions(): ExcelCopyBufferOption {
99+
let newRowIds = 0;
100+
101+
return {
102+
clipboardCommandHandler: (editCommand: any) => {
103+
this._undoRedoBuffer.queueAndExecuteCommand.call(this._undoRedoBuffer, editCommand);
104+
},
105+
dataItemColumnValueExtractor: (item: any, columnDef: Column) => {
106+
// when grid or cell is not editable, we will possibly evaluate the Formatter if it was passed
107+
// to decide if we evaluate the Formatter, we will use the same flag from Export which is "exportWithFormatter"
108+
if (!this.sharedService.gridOptions.editable || !columnDef.editor) {
109+
const isEvaluatingFormatter = (columnDef.exportWithFormatter !== undefined) ? columnDef.exportWithFormatter : (this.sharedService.gridOptions.exportOptions && this.sharedService.gridOptions.exportOptions.exportWithFormatter);
110+
if (columnDef.formatter && isEvaluatingFormatter) {
111+
const formattedOutput = columnDef.formatter(0, 0, item[columnDef.field], columnDef, item, this.sharedService.grid);
112+
if (columnDef.sanitizeDataExport || (this.sharedService.gridOptions.exportOptions && this.sharedService.gridOptions.exportOptions.sanitizeDataExport)) {
113+
let outputString = formattedOutput as string;
114+
if (formattedOutput && typeof formattedOutput === 'object' && formattedOutput.hasOwnProperty('text')) {
115+
outputString = formattedOutput.text;
116+
}
117+
if (outputString === null) {
118+
outputString = '';
119+
}
120+
return sanitizeHtmlToText(outputString);
121+
}
122+
return formattedOutput;
123+
}
124+
}
125+
// else use the default "dataItemColumnValueExtractor" from the plugin itself
126+
// we can do that by setting back the getter with null
127+
return null;
128+
},
129+
readOnlyMode: false,
130+
includeHeaderWhenCopying: false,
131+
newRowCreator: (count: number) => {
132+
for (let i = 0; i < count; i++) {
133+
const item = {
134+
id: 'newRow_' + newRowIds++
135+
};
136+
this.sharedService.grid.getData().addItem(item);
137+
}
138+
}
139+
};
140+
}
141+
107142
/** Attach an undo shortcut key hook that will redo/undo the copy buffer */
108143
private hookUndoShortcutKey() {
109144
// undo shortcut
Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
import { Column } from './column.interface';
2+
import { SelectedRange } from './selectedRange.interface';
3+
4+
export interface ExcelCopyBufferOption {
5+
/** defaults to "copied", sets the css className used for copied cells. */
6+
copiedCellStyle?: string;
7+
8+
/** defaults to "copy-manager", sets the layer key for setting css values of copied cells. */
9+
copiedCellStyleLayerKey?: string;
10+
11+
/** option to specify a custom column value extractor function */
12+
dataItemColumnValueExtractor?: (item: any, columnDef: Column) => any;
13+
14+
/** option to specify a custom column value setter function */
15+
dataItemColumnValueSetter?: (item: any, columnDef: Column, value: any) => any;
16+
17+
/** option to specify a custom handler for paste actions */
18+
clipboardCommandHandler?: (editCommand: any) => void;
19+
20+
/** set to true and the plugin will take the name property from each column (which is usually what appears in your header) and put that as the first row of the text that's copied to the clipboard */
21+
includeHeaderWhenCopying?: boolean;
22+
23+
/** option to specify a custom DOM element which to will be added the hidden textbox. It's useful if the grid is inside a modal dialog. */
24+
bodyElement?: HTMLElement;
25+
26+
/** optional handler to run when copy action initializes */
27+
onCopyInit?: any;
28+
29+
/** optional handler to run when copy action is complete */
30+
onCopySuccess?: any;
31+
32+
/** function to add rows to table if paste overflows bottom of table, if this function is not provided new rows will be ignored. */
33+
newRowCreator?: (count: number) => void;
34+
35+
/** suppresses paste */
36+
readOnlyMode?: boolean;
37+
38+
/** option to specify a custom column header value extractor function */
39+
headerColumnValueExtractor?: (columnDef: Column) => any;
40+
41+
42+
// --
43+
// Events
44+
// ------------
45+
46+
/** Fired after extension (plugin) is registered by SlickGrid */
47+
onExtensionRegistered?: (plugin: any) => void;
48+
49+
/** Fired when a copy cell is triggered */
50+
onCopyCells?: (e: Event, args: { ranges: SelectedRange[] }) => void;
51+
52+
/** Fired when the command to copy the cells is cancelled */
53+
onCopyCancelled?: (e: Event, args: { ranges: SelectedRange[] }) => void;
54+
55+
/** Fired when the user paste cells to the grid */
56+
onPasteCells?: (e: Event, args: { ranges: SelectedRange[] }) => void;
57+
}

src/app/modules/angular-slickgrid/models/gridOption.interface.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import {
77
CheckboxSelector,
88
DraggableGrouping,
99
EditCommand,
10+
ExcelCopyBufferOption,
1011
ExportOption,
1112
FormatterOption,
1213
GridMenu,
@@ -217,6 +218,9 @@ export interface GridOption {
217218
/** Do we want to enable localization translation (i18n)? */
218219
enableTranslate?: boolean;
219220

221+
/** Options for the ExcelCopyBuffer Extension */
222+
excelCopyBufferOptions?: ExcelCopyBufferOption;
223+
220224
/** Do we want explicit grid initialization? */
221225
explicitInitialization?: boolean;
222226

src/app/modules/angular-slickgrid/models/index.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ export * from './editorValidator.interface';
3232
export * from './editorValidatorOutput.interface';
3333
export * from './elementPosition.interface';
3434
export * from './emitterType.enum';
35+
export * from './excelCopyBufferOption.interface';
3536
export * from './exportOption.interface';
3637
export * from './extension.interface';
3738
export * from './extensionModel.interface';
@@ -85,6 +86,7 @@ export * from './queryArgument.interface';
8586
export * from './rowDetailView.interface';
8687
export * from './rowMoveManager.interface';
8788
export * from './searchTerm.type';
89+
export * from './selectedRange.interface';
8890
export * from './selectOption.interface';
8991
export * from './slickEvent.interface';
9092
export * from './sortChangedArgs.interface';
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
export interface SelectedRange {
2+
/** Selection start from which cell? */
3+
fromCell: number;
4+
5+
/** Selection start from which row? */
6+
fromRow: number;
7+
8+
/** Selection goes to which cell? */
9+
toCell: number;
10+
11+
/** Selection goes to which row? */
12+
toRow: number;
13+
14+
/** Does the selection contain a row & cell number? */
15+
contains?: (row: number, cell: number) => boolean;
16+
17+
/** Is it a Single Cell Selection? */
18+
isSingleCell?: () => boolean;
19+
20+
/** Is it a Single Row Selection? */
21+
isSingleRow?: () => boolean;
22+
23+
/** Output print to string */
24+
toString?: () => string;
25+
}

0 commit comments

Comments
 (0)