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.
- package/CHANGELOG.md +7 -0
- package/dist/canvas-datagrid.debug.js +209 -147
- package/dist/canvas-datagrid.debug.map +1 -1
- package/dist/canvas-datagrid.js +1 -1
- package/dist/canvas-datagrid.module.js +1 -1
- package/dist/canvas-datagrid.module.js.map +1 -1
- package/lib/events/index.js +55 -88
- package/lib/events/util.js +39 -1
- package/lib/intf.js +66 -1
- package/package.json +1 -1
package/lib/events/index.js
CHANGED
|
@@ -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
|
-
|
|
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.
|
|
1846
|
-
|
|
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
|
-
|
|
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
|
-
|
|
1885
|
-
|
|
1886
|
-
|
|
1887
|
-
|
|
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, '<').replace(/>/g, '>');
|
|
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
|
}
|
package/lib/events/util.js
CHANGED
|
@@ -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, '<').replace(/>/g, '>');
|
|
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
|
-
|
|
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] =
|
|
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.
|
|
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",
|