canvas-datagrid 0.3.21 → 0.3.22

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.
@@ -797,7 +797,32 @@ export default function (self) {
797
797
  }
798
798
  self.ellipsisCache = {};
799
799
  };
800
- self.stopDragResize = function () {
800
+ self.stopDragResize = function (e) {
801
+ const pos = self.getLayerPos(e);
802
+ const hasMoved = !!(pos.x - self.dragStart.x);
803
+ const width = self.resizingStartingWidth + pos.x - self.dragStart.x;
804
+
805
+ if (self.dragMode === 'ew-resize') {
806
+ const selectedColumns = self.selections[0];
807
+ const dragItemIsSelected = self.isColumnSelected(
808
+ self.dragItem.columnIndex,
809
+ );
810
+
811
+ if (dragItemIsSelected)
812
+ for (const selectedColumnIndex of selectedColumns) {
813
+ const boundColumnIndex = self.getBoundColumnIndexFromViewColumnIndex(
814
+ selectedColumnIndex,
815
+ );
816
+
817
+ if (
818
+ hasMoved &&
819
+ dragItemIsSelected &&
820
+ self.isColumnSelected(selectedColumnIndex)
821
+ )
822
+ self.sizes.columns[boundColumnIndex] = width;
823
+ }
824
+ }
825
+
801
826
  self.resize();
802
827
  document.body.removeEventListener(
803
828
  'mousemove',
@@ -1609,7 +1634,17 @@ export default function (self) {
1609
1634
  self.currentCell.context === 'ew-resize' &&
1610
1635
  self.currentCell.style === 'columnHeaderCell'
1611
1636
  ) {
1612
- self.fitColumnToValues(self.currentCell.header.name);
1637
+ // Check that double-clicked cell is selected or part of selection.
1638
+ const currentCellIsSelected = self.isColumnSelected(
1639
+ self.currentCell.columnIndex,
1640
+ );
1641
+
1642
+ if (currentCellIsSelected) {
1643
+ // There might be more
1644
+ self.fitSelectedColumns();
1645
+ } else {
1646
+ self.fitColumnToValues(self.currentCell.header.name);
1647
+ }
1613
1648
  } else if (
1614
1649
  self.currentCell.context === 'ew-resize' &&
1615
1650
  self.currentCell.style === 'cornerCell'
@@ -1842,106 +1877,38 @@ export default function (self) {
1842
1877
  });
1843
1878
  };
1844
1879
  self.cut = function (e) {
1845
- self.copy(e);
1846
- const schema = self.getSchema();
1847
-
1848
- var affectedCells = [];
1849
- for (const [rowIndex, row] of self.selections.entries()) {
1850
- if (!row) continue;
1851
-
1852
- const boundRowIndex = self.getBoundRowIndexFromViewRowIndex(rowIndex);
1853
-
1854
- for (const columnIndex of row) {
1855
- const boundColumnIndex = self.getBoundColumnIndexFromViewColumnIndex(
1856
- columnIndex,
1857
- );
1858
- const colName = schema[boundColumnIndex].name;
1859
-
1860
- self.viewData[rowIndex][colName] = '';
1861
-
1862
- affectedCells.push([
1863
- rowIndex,
1864
- columnIndex,
1865
- boundRowIndex,
1866
- boundColumnIndex,
1867
- ]);
1868
- }
1880
+ if (self.dispatchEvent('cut', { NativeEvent: e })) {
1881
+ return;
1869
1882
  }
1870
1883
 
1871
- if (self.dispatchEvent('cut', { NativeEvent: e, cells: affectedCells })) {
1884
+ // Expecting instance of `ClipboardEvent` with `clipboardData` attribute
1885
+ if (!self.hasFocus || !e.clipboardData) {
1872
1886
  return;
1873
1887
  }
1874
1888
 
1889
+ self.copySelectedCellsToClipboard(e.clipboardData);
1890
+
1891
+ const affectedCells = self.clearSelectedCells();
1892
+
1893
+ self.dispatchEvent('aftercut', {
1894
+ cells: affectedCells,
1895
+ });
1896
+
1875
1897
  requestAnimationFrame(() => self.draw());
1876
1898
  };
1877
1899
  self.copy = function (e) {
1878
1900
  if (self.dispatchEvent('copy', { NativeEvent: e })) {
1879
1901
  return;
1880
1902
  }
1903
+
1904
+ // Expecting instance of `ClipboardEvent` with `clipboardData` attribute
1881
1905
  if (!self.hasFocus || !e.clipboardData) {
1882
1906
  return;
1883
1907
  }
1884
- var t = '',
1885
- d = '',
1886
- textRows = [],
1887
- sData = self.getSelectedData(),
1888
- s = self.getSchema(),
1889
- sSorted = [],
1890
- firstRowKeys,
1891
- isNeat = true; // Selected like [[0, 1], [0, 1]] of [[0, 3]] is neat; Selected like [[0, 1], [1, 2]] is untidy
1892
- function htmlSafe(v) {
1893
- if (typeof v === 'number') return v;
1894
- return v.replace(/</g, '&lt;').replace(/>/g, '&gt;');
1895
- }
1896
- s.forEach(function (column, columnIndex) {
1897
- sSorted.push(s[self.orders.columns[columnIndex]]);
1898
- });
1899
- if (sData.length > 0) {
1900
- sData.forEach((row) => {
1901
- if (!row) return;
1902
- var rowKeys = Object.keys(row);
1903
- var textRow = [];
1904
- if (!firstRowKeys) firstRowKeys = Object.keys(row);
1905
- if (isNeat && rowKeys.length !== firstRowKeys.length) isNeat = false;
1906
- sSorted.forEach(function (column, columnIndex) {
1907
- if (rowKeys.indexOf(String(column.name)) < 0) {
1908
- if (firstRowKeys.indexOf(String(column.name)) < 0) {
1909
- return;
1910
- } else if (isNeat) {
1911
- isNeat = false;
1912
- }
1913
- }
1914
- textRow.push(row[column.name] || '');
1915
- });
1916
- textRows.push(textRow);
1917
- });
1918
- if (isNeat) {
1919
- t = textRows.map((row) => row.join('\t')).join('\n');
1920
- /**
1921
- * The html content copied by Excel has not header
1922
- */
1923
- d += '<table>';
1924
- d += textRows
1925
- .map(
1926
- (row) =>
1927
- '<tr>' +
1928
- row.map((value) => '<td>' + htmlSafe(value) + '</td>').join('') +
1929
- '</tr>',
1930
- )
1931
- .join('');
1932
- d += '</table>';
1933
- } else {
1934
- t = textRows.map((row) => row.join('')).join('');
1935
- d = t;
1936
- }
1937
- if (t) {
1938
- e.clipboardData.setData('text/html', d);
1939
- e.clipboardData.setData('text/plain', t);
1940
- e.clipboardData.setData('text/csv', t);
1941
- e.clipboardData.setData('application/json', JSON.stringify(sData));
1942
- }
1943
- e.preventDefault();
1944
- }
1908
+
1909
+ self.copySelectedCellsToClipboard(e.clipboardData);
1910
+
1911
+ e.preventDefault();
1945
1912
  };
1946
1913
  return;
1947
1914
  }
@@ -86,11 +86,49 @@ const parseData = function (data, mimeType) {
86
86
  return parseText(data);
87
87
  };
88
88
 
89
+ const htmlSafe = (value) => {
90
+ if (typeof value === 'number') return value;
91
+ return value.replace(/</g, '&lt;').replace(/>/g, '&gt;');
92
+ };
93
+
94
+ const createTextString = (selectedData, isNeat) => {
95
+ // Selected like [[0, 1], [0, 1]] of [[0, 3]] is neat; Selected like [[0, 1], [1, 2]] is untidy.
96
+ // If not isNeat we just return a simple string of concatenated values.
97
+ if (!isNeat)
98
+ return selectedData.map((row) => Object.values(row).join('')).join('');
99
+
100
+ // If isNeat, we can create tab separated mutti-line text.
101
+ return selectedData.map((row) => Object.values(row).join('\t')).join('\n');
102
+ };
103
+
104
+ const createHTMLString = (selectedData, isNeat) => {
105
+ if (!isNeat) return createTextString(selectedData, isNeat);
106
+
107
+ // If isNeat, we can create a HTML table with the selected data.
108
+ let htmlString = '<table>';
109
+ htmlString += selectedData
110
+ .map(
111
+ (row) =>
112
+ '<tr>' +
113
+ Object.values(row)
114
+ .map((value) => '<td>' + htmlSafe(value) + '</td>')
115
+ .join('') +
116
+ '</tr>',
117
+ )
118
+ .join('');
119
+ htmlString += '</table>';
120
+
121
+ return htmlString;
122
+ };
123
+
89
124
  export {
125
+ createTextString,
126
+ createHTMLString,
90
127
  isSupportedHtml,
91
- sanitizeElementData,
128
+ htmlSafe,
92
129
  parseData,
93
130
  parseHtmlTable,
94
131
  parseHtmlText,
95
132
  parseText,
133
+ sanitizeElementData,
96
134
  };
package/lib/intf.js CHANGED
@@ -2,6 +2,8 @@
2
2
  /*globals HTMLElement: false, Reflect: false, define: true, MutationObserver: false, requestAnimationFrame: false, performance: false, btoa: false*/
3
3
  'use strict';
4
4
 
5
+ import { createHTMLString, createTextString } from './events/util';
6
+
5
7
  export default function (self, ctor) {
6
8
  self.scale = 1;
7
9
  self.orders = {
@@ -242,6 +244,57 @@ export default function (self, ctor) {
242
244
 
243
245
  return selectedCells;
244
246
  };
247
+ self.copySelectedCellsToClipboard = function (clipboardData) {
248
+ const selectedData = [];
249
+ const schema = self.getSchema();
250
+ let firstRowAsString;
251
+ let isNeat = true; // Selected like [[0, 1], [0, 1]] of [[0, 3]] is neat; Selected like [[0, 1], [1, 2]] is untidy
252
+
253
+ for (const [rowIndex, row] of self.selections.entries()) {
254
+ // If no cells are selected for a particular rowIndex the selections array will contain an empty element for that rowIndex.
255
+ if (!row) continue;
256
+
257
+ // Convert to string for easy comparison to the first row.
258
+ const rowAsString = row.join(',').toString();
259
+ const rowDict = {};
260
+
261
+ if (!firstRowAsString) firstRowAsString = row.join(',').toString();
262
+ if (isNeat && rowAsString !== firstRowAsString) isNeat = false;
263
+
264
+ const boundRowIndex = self.getBoundRowIndexFromViewRowIndex(rowIndex);
265
+
266
+ for (let columnIndex of row) {
267
+ // If the whole row is selected the columnIndex for the rowHeader is -1.
268
+ if (columnIndex < 0) continue;
269
+
270
+ const boundColumnIndex = self.getBoundColumnIndexFromViewColumnIndex(
271
+ columnIndex,
272
+ );
273
+ const columnName = schema[boundColumnIndex].name;
274
+
275
+ const value = self.originalData[boundRowIndex][columnName];
276
+
277
+ rowDict[columnName] = value;
278
+ }
279
+ selectedData.push(rowDict);
280
+ }
281
+
282
+ if (selectedData.length > 0) {
283
+ const textString = createTextString(selectedData, isNeat);
284
+ const htmlString = createHTMLString(selectedData, isNeat);
285
+
286
+ const copiedData = {
287
+ 'text/plain': textString,
288
+ 'text/html': htmlString,
289
+ 'text/csv': textString,
290
+ 'application/json': JSON.stringify(selectedData),
291
+ };
292
+
293
+ for (const [mimeType, data] of Object.entries(copiedData)) {
294
+ clipboardData.setData(mimeType, data);
295
+ }
296
+ }
297
+ };
245
298
  self.clearSelectedCells = function () {
246
299
  const schema = self.getSchema();
247
300
  const affectedCells = [];
@@ -261,7 +314,7 @@ export default function (self, ctor) {
261
314
  );
262
315
  const columnName = schema[boundColumnIndex].name;
263
316
 
264
- self.viewData[boundRowIndex][columnName] = null;
317
+ self.viewData[boundRowIndex][columnName] = '';
265
318
 
266
319
  affectedCells.push([
267
320
  rowIndex,
@@ -343,6 +396,18 @@ export default function (self, ctor) {
343
396
  self.createRowOrders = function () {
344
397
  self.orders.rows = fillArray(0, self.originalData.length - 1);
345
398
  };
399
+ self.fitSelectedColumns = function () {
400
+ const selectedColumns = self.selections[0];
401
+ const schema = self.getSchema();
402
+
403
+ for (const selectedColumn of selectedColumns) {
404
+ // Make sure the column is not the row header and that the whole column has in fact been selected.
405
+ if (selectedColumn >= 0 && self.isColumnSelected(selectedColumn)) {
406
+ const column = schema[self.orders.columns[selectedColumn]];
407
+ self.fitColumnToValues(column.name);
408
+ }
409
+ }
410
+ };
346
411
  self.getVisibleSchema = function () {
347
412
  return self.getSchema().filter(function (col) {
348
413
  return !col.hidden;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "canvas-datagrid",
3
- "version": "0.3.21",
3
+ "version": "0.3.22",
4
4
  "description": "Canvas based data grid web component. Capable of displaying millions of contiguous hierarchical rows and columns without paging or loading, on a single canvas element.",
5
5
  "main": "./dist/canvas-datagrid.js",
6
6
  "module": "./dist/canvas-datagrid.module.js",