canvas-datagrid 0.3.21 → 0.3.25
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 +27 -0
- package/dist/canvas-datagrid.debug.js +226 -151
- 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/dom.js +9 -4
- package/lib/events/index.js +55 -92
- package/lib/events/util.js +40 -1
- package/lib/intf.js +75 -1
- package/lib/publicMethods.js +19 -12
- package/package.json +1 -1
package/lib/dom.js
CHANGED
|
@@ -143,11 +143,16 @@ export default function (self) {
|
|
|
143
143
|
s = e,
|
|
144
144
|
h,
|
|
145
145
|
w;
|
|
146
|
+
let calculatedTree = false;
|
|
146
147
|
while (e.offsetParent && e.nodeName !== 'CANVAS-DATAGRID') {
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
148
|
+
const isTree = e.nodeType === 'canvas-datagrid-tree';
|
|
149
|
+
if (!isTree || !calculatedTree) {
|
|
150
|
+
x += e.offsetLeft;
|
|
151
|
+
y += e.offsetTop;
|
|
152
|
+
h = e.offsetHeight;
|
|
153
|
+
w = e.offsetWidth;
|
|
154
|
+
}
|
|
155
|
+
if (isTree) calculatedTree = true;
|
|
151
156
|
e = e.offsetParent;
|
|
152
157
|
}
|
|
153
158
|
if (ignoreScrollOffset) {
|
package/lib/events/index.js
CHANGED
|
@@ -797,7 +797,26 @@ export default function (self) {
|
|
|
797
797
|
}
|
|
798
798
|
self.ellipsisCache = {};
|
|
799
799
|
};
|
|
800
|
-
self.stopDragResize = function () {
|
|
800
|
+
self.stopDragResize = function (event) {
|
|
801
|
+
const pos = self.getLayerPos(event);
|
|
802
|
+
|
|
803
|
+
if (self.dragMode === 'ew-resize') {
|
|
804
|
+
const hasMoved = !!(pos.x - self.dragStart.x);
|
|
805
|
+
// Check that dragItem is selected or part of selection.
|
|
806
|
+
const dragItemIsSelected = self.isColumnSelected(
|
|
807
|
+
self.dragItem.columnIndex,
|
|
808
|
+
);
|
|
809
|
+
|
|
810
|
+
if (hasMoved && dragItemIsSelected) {
|
|
811
|
+
const width = Math.max(
|
|
812
|
+
self.resizingStartingWidth + pos.x - self.dragStart.x,
|
|
813
|
+
self.style.minColumnWidth,
|
|
814
|
+
);
|
|
815
|
+
// If the column is selected, resize it to width plus any other selected columns.
|
|
816
|
+
self.fitSelectedColumns(width);
|
|
817
|
+
}
|
|
818
|
+
}
|
|
819
|
+
|
|
801
820
|
self.resize();
|
|
802
821
|
document.body.removeEventListener(
|
|
803
822
|
'mousemove',
|
|
@@ -956,6 +975,7 @@ export default function (self) {
|
|
|
956
975
|
-1,
|
|
957
976
|
self.activeCell.rowIndex,
|
|
958
977
|
);
|
|
978
|
+
if (!self.reorderObject) return;
|
|
959
979
|
self.reorderTarget = self.currentCell;
|
|
960
980
|
self.reorderObject.dragOffset = {
|
|
961
981
|
x: x,
|
|
@@ -1609,7 +1629,17 @@ export default function (self) {
|
|
|
1609
1629
|
self.currentCell.context === 'ew-resize' &&
|
|
1610
1630
|
self.currentCell.style === 'columnHeaderCell'
|
|
1611
1631
|
) {
|
|
1612
|
-
|
|
1632
|
+
// Check that double-clicked cell is selected or part of selection.
|
|
1633
|
+
const currentCellIsSelected = self.isColumnSelected(
|
|
1634
|
+
self.currentCell.columnIndex,
|
|
1635
|
+
);
|
|
1636
|
+
|
|
1637
|
+
if (currentCellIsSelected) {
|
|
1638
|
+
// There might be more
|
|
1639
|
+
self.fitSelectedColumns();
|
|
1640
|
+
} else {
|
|
1641
|
+
self.fitColumnToValues(self.currentCell.header.name);
|
|
1642
|
+
}
|
|
1613
1643
|
} else if (
|
|
1614
1644
|
self.currentCell.context === 'ew-resize' &&
|
|
1615
1645
|
self.currentCell.style === 'cornerCell'
|
|
@@ -1841,107 +1871,40 @@ export default function (self) {
|
|
|
1841
1871
|
self.draw();
|
|
1842
1872
|
});
|
|
1843
1873
|
};
|
|
1844
|
-
self.cut = function (
|
|
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
|
-
}
|
|
1874
|
+
self.cut = function (event) {
|
|
1875
|
+
if (self.dispatchEvent('cut', { NativeEvent: event })) {
|
|
1876
|
+
return;
|
|
1869
1877
|
}
|
|
1870
1878
|
|
|
1871
|
-
|
|
1879
|
+
// Expecting instance of `ClipboardEvent` with `clipboardData` attribute
|
|
1880
|
+
if (!self.hasFocus || !event.clipboardData) {
|
|
1872
1881
|
return;
|
|
1873
1882
|
}
|
|
1874
1883
|
|
|
1884
|
+
self.copySelectedCellsToClipboard(event.clipboardData);
|
|
1885
|
+
|
|
1886
|
+
const affectedCells = self.clearSelectedCells();
|
|
1887
|
+
|
|
1888
|
+
self.dispatchEvent('aftercut', {
|
|
1889
|
+
cells: affectedCells,
|
|
1890
|
+
});
|
|
1891
|
+
|
|
1875
1892
|
requestAnimationFrame(() => self.draw());
|
|
1893
|
+
event.preventDefault();
|
|
1876
1894
|
};
|
|
1877
|
-
self.copy = function (
|
|
1878
|
-
if (self.dispatchEvent('copy', { NativeEvent:
|
|
1895
|
+
self.copy = function (event) {
|
|
1896
|
+
if (self.dispatchEvent('copy', { NativeEvent: event })) {
|
|
1879
1897
|
return;
|
|
1880
1898
|
}
|
|
1881
|
-
|
|
1899
|
+
|
|
1900
|
+
// Expecting instance of `ClipboardEvent` with `clipboardData` attribute
|
|
1901
|
+
if (!self.hasFocus || !event.clipboardData) {
|
|
1882
1902
|
return;
|
|
1883
1903
|
}
|
|
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
|
-
}
|
|
1904
|
+
|
|
1905
|
+
self.copySelectedCellsToClipboard(event.clipboardData);
|
|
1906
|
+
|
|
1907
|
+
event.preventDefault();
|
|
1945
1908
|
};
|
|
1946
1909
|
return;
|
|
1947
1910
|
}
|
package/lib/events/util.js
CHANGED
|
@@ -86,11 +86,50 @@ const parseData = function (data, mimeType) {
|
|
|
86
86
|
return parseText(data);
|
|
87
87
|
};
|
|
88
88
|
|
|
89
|
+
const htmlSafe = function (value) {
|
|
90
|
+
if (typeof value !== 'string') return value;
|
|
91
|
+
|
|
92
|
+
return value.replace(/</g, '<').replace(/>/g, '>');
|
|
93
|
+
};
|
|
94
|
+
|
|
95
|
+
const createTextString = function (selectedData, isNeat) {
|
|
96
|
+
// Selected like [[0, 1], [0, 1]] of [[0, 3]] is neat; Selected like [[0, 1], [1, 2]] is untidy.
|
|
97
|
+
// If not isNeat we just return a simple string of concatenated values.
|
|
98
|
+
if (!isNeat)
|
|
99
|
+
return selectedData.map((row) => Object.values(row).join('')).join('');
|
|
100
|
+
|
|
101
|
+
// If isNeat, we can create tab separated mutti-line text.
|
|
102
|
+
return selectedData.map((row) => Object.values(row).join('\t')).join('\n');
|
|
103
|
+
};
|
|
104
|
+
|
|
105
|
+
const createHTMLString = function (selectedData, isNeat) {
|
|
106
|
+
if (!isNeat) return createTextString(selectedData, isNeat);
|
|
107
|
+
|
|
108
|
+
// If isNeat, we can create a HTML table with the selected data.
|
|
109
|
+
let htmlString = '<table>';
|
|
110
|
+
htmlString += selectedData
|
|
111
|
+
.map(
|
|
112
|
+
(row) =>
|
|
113
|
+
'<tr>' +
|
|
114
|
+
Object.values(row)
|
|
115
|
+
.map((value) => '<td>' + htmlSafe(value) + '</td>')
|
|
116
|
+
.join('') +
|
|
117
|
+
'</tr>',
|
|
118
|
+
)
|
|
119
|
+
.join('');
|
|
120
|
+
htmlString += '</table>';
|
|
121
|
+
|
|
122
|
+
return htmlString;
|
|
123
|
+
};
|
|
124
|
+
|
|
89
125
|
export {
|
|
126
|
+
createTextString,
|
|
127
|
+
createHTMLString,
|
|
90
128
|
isSupportedHtml,
|
|
91
|
-
|
|
129
|
+
htmlSafe,
|
|
92
130
|
parseData,
|
|
93
131
|
parseHtmlTable,
|
|
94
132
|
parseHtmlText,
|
|
95
133
|
parseText,
|
|
134
|
+
sanitizeElementData,
|
|
96
135
|
};
|
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,27 @@ 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 (width) {
|
|
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
|
+
if (isNaN(width)) {
|
|
407
|
+
const column = schema[self.orders.columns[selectedColumn]];
|
|
408
|
+
self.fitColumnToValues(column.name);
|
|
409
|
+
} else {
|
|
410
|
+
self.sizes.columns[selectedColumn] = width;
|
|
411
|
+
self.dispatchEvent('resizecolumn', {
|
|
412
|
+
x: width,
|
|
413
|
+
y: self.resizingStartingHeight,
|
|
414
|
+
draggingItem: self.currentCell,
|
|
415
|
+
});
|
|
416
|
+
}
|
|
417
|
+
}
|
|
418
|
+
}
|
|
419
|
+
};
|
|
346
420
|
self.getVisibleSchema = function () {
|
|
347
421
|
return self.getSchema().filter(function (col) {
|
|
348
422
|
return !col.hidden;
|
package/lib/publicMethods.js
CHANGED
|
@@ -342,18 +342,25 @@ export default function (self) {
|
|
|
342
342
|
* @param {number} [offsetY=0] Percentage offset the cell should be from the top edge (not including headers). The default is 0, meaning the cell will appear at the top edge. Valid values are 0 through 1. 1 = Bottom, 0 = Top, 0.5 = Center.
|
|
343
343
|
*/
|
|
344
344
|
self.scrollIntoView = function (x, y, offsetX, offsetY) {
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
345
|
+
const matched = self.visibleCells.filter(function (cell) {
|
|
346
|
+
return (
|
|
347
|
+
(cell.rowIndex === y || y === undefined) &&
|
|
348
|
+
(cell.columnIndex === x || x === undefined) &&
|
|
349
|
+
cell.x > 0 &&
|
|
350
|
+
cell.y > 0 &&
|
|
351
|
+
cell.x + cell.width < self.width &&
|
|
352
|
+
cell.y + cell.height < self.height
|
|
353
|
+
);
|
|
354
|
+
});
|
|
355
|
+
if (matched.length === 1 && x !== undefined && y !== undefined) {
|
|
356
|
+
// goto specific cell and its part be hidden by header
|
|
357
|
+
if (
|
|
358
|
+
matched[0].x < self.getRowHeaderCellWidth() ||
|
|
359
|
+
matched[0].y < self.getColumnHeaderCellHeight()
|
|
360
|
+
)
|
|
361
|
+
matched.length = 0;
|
|
362
|
+
}
|
|
363
|
+
if (matched.length === 0) {
|
|
357
364
|
self.gotoCell(x, y, offsetX, offsetY);
|
|
358
365
|
}
|
|
359
366
|
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "canvas-datagrid",
|
|
3
|
-
"version": "0.3.
|
|
3
|
+
"version": "0.3.25",
|
|
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",
|