canvas-datagrid 0.3.11 → 0.3.15
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 +29 -0
- package/dist/canvas-datagrid.debug.js +352 -99
- 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/dist/types.d.ts +6 -0
- package/lib/defaults.js +3 -0
- package/lib/docs.js +5 -0
- package/lib/draw.js +8 -6
- package/lib/{events.js → events/index.js} +58 -60
- package/lib/events/util.js +96 -0
- package/lib/intf.js +69 -5
- package/lib/publicMethods.js +4 -0
- package/package.json +1 -1
package/dist/types.d.ts
CHANGED
|
@@ -66,6 +66,8 @@
|
|
|
66
66
|
* @param [args.autoScrollMargin = 5] - Number of pixels of mouse movement to trigger auto scroll.
|
|
67
67
|
* @param [args.allowFreezingRows = false] - When true, the UI provides a drag-able cutter to freeze rows.
|
|
68
68
|
* @param [args.allowFreezingColumns = false] - When true, the UI provides a drag-able cutter to freeze columns.
|
|
69
|
+
* @param [args.filterFrozenRows = true] - When false, rows on and above {@link canvasDatagrid#property:frozenRow}` will be ignored by filters when {@link canvasDatagrid#param:allowFreezingRows} is true.
|
|
70
|
+
* @param [args.sortFrozenRows = true] - When false, rows on and above {@link canvasDatagrid#property:frozenRow}` will be ignored by sorters when {@link canvasDatagrid#param:allowSorting} is true.
|
|
69
71
|
* @param [args.allowColumnReordering = true] - When true columns can be reordered.
|
|
70
72
|
* @param [args.allowMovingSelection = false] - When true selected data can be moved by clicking and dragging on the border of the selection.
|
|
71
73
|
* @param [args.allowRowReordering = false] - When true rows can be reordered.
|
|
@@ -76,6 +78,7 @@
|
|
|
76
78
|
* @param [args.pageUpDownOverlap = 1] - Amount of rows to overlap when pageup/pagedown is used.
|
|
77
79
|
* @param [args.persistantSelectionMode = false] - When true, selections will behave as if the command/control key is held down at all times. Conflicts with singleSelectionMode.
|
|
78
80
|
* @param [args.singleSelectionMode = false] - When true, selections will ignore the command/control key. Conflicts with persistantSelectionMode.
|
|
81
|
+
* @param [args.keepFocusOnMouseOut = false] - When true, grid will continue to handle keydown events when mouse is outside of the grid.
|
|
79
82
|
* @param [args.selectionMode = 'cell'] - Can be 'cell', or 'row'. This setting dictates what will be selected when the user clicks a cell. The cell, or the entire row respectively.
|
|
80
83
|
* @param [args.hoverMode = 'cell'] - Can be 'cell', or 'row'. This setting dictates whether to highlight either the individual cell, or the entire row when hovering over a cell.
|
|
81
84
|
* @param [args.autoResizeColumns = false] - When true, all columns will be automatically resized to fit the data in them. Warning! Expensive for large (>100k ~2 seconds) datasets.
|
|
@@ -152,6 +155,8 @@ declare class canvasDatagrid {
|
|
|
152
155
|
autoScrollMargin?: number;
|
|
153
156
|
allowFreezingRows?: boolean;
|
|
154
157
|
allowFreezingColumns?: boolean;
|
|
158
|
+
filterFrozenRows?: boolean;
|
|
159
|
+
sortFrozenRows?: boolean;
|
|
155
160
|
allowColumnReordering?: boolean;
|
|
156
161
|
allowMovingSelection?: boolean;
|
|
157
162
|
allowRowReordering?: boolean;
|
|
@@ -162,6 +167,7 @@ declare class canvasDatagrid {
|
|
|
162
167
|
pageUpDownOverlap?: number;
|
|
163
168
|
persistantSelectionMode?: boolean;
|
|
164
169
|
singleSelectionMode?: boolean;
|
|
170
|
+
keepFocusOnMouseOut?: boolean;
|
|
165
171
|
selectionMode?: string;
|
|
166
172
|
hoverMode?: string;
|
|
167
173
|
autoResizeColumns?: boolean;
|
package/lib/defaults.js
CHANGED
|
@@ -38,9 +38,11 @@ export default function (self) {
|
|
|
38
38
|
['ellipsisText', '...'],
|
|
39
39
|
['filterOptionText', 'Filter %s'],
|
|
40
40
|
['filterTextPrefix', '(filtered) '],
|
|
41
|
+
['filterFrozenRows', true],
|
|
41
42
|
['globalRowResize', false],
|
|
42
43
|
['hideColumnText', 'Hide %s'],
|
|
43
44
|
['hoverMode', 'cell'],
|
|
45
|
+
['keepFocusOnMouseOut', false],
|
|
44
46
|
['maxAutoCompleteItems', 200],
|
|
45
47
|
['multiLine', false],
|
|
46
48
|
['name', ''],
|
|
@@ -76,6 +78,7 @@ export default function (self) {
|
|
|
76
78
|
['showRowNumberGaps', true],
|
|
77
79
|
['singleSelectionMode', false],
|
|
78
80
|
['snapToRow', false],
|
|
81
|
+
['sortFrozenRows', true],
|
|
79
82
|
['touchContextMenuTimeMs', 800],
|
|
80
83
|
['touchDeadZone', 3],
|
|
81
84
|
['touchEasingMethod', 'easeOutQuad'],
|
package/lib/docs.js
CHANGED
|
@@ -29,6 +29,8 @@
|
|
|
29
29
|
* @param {number} [args.autoScrollMargin=5] - Number of pixels of mouse movement to trigger auto scroll.
|
|
30
30
|
* @param {boolean} [args.allowFreezingRows=false] - When true, the UI provides a drag-able cutter to freeze rows.
|
|
31
31
|
* @param {boolean} [args.allowFreezingColumns=false] - When true, the UI provides a drag-able cutter to freeze columns.
|
|
32
|
+
* @param {boolean} [args.filterFrozenRows=true] - When false, rows on and above {@link canvasDatagrid#property:frozenRow}` will be ignored by filters when {@link canvasDatagrid#param:allowFreezingRows} is true.
|
|
33
|
+
* @param {boolean} [args.sortFrozenRows=true] - When false, rows on and above {@link canvasDatagrid#property:frozenRow}` will be ignored by sorters when {@link canvasDatagrid#param:allowSorting} is true.
|
|
32
34
|
* @param {boolean} [args.allowColumnReordering=true] - When true columns can be reordered.
|
|
33
35
|
* @param {boolean} [args.allowMovingSelection=false] - When true selected data can be moved by clicking and dragging on the border of the selection.
|
|
34
36
|
* @param {boolean} [args.allowRowReordering=false] - When true rows can be reordered.
|
|
@@ -39,6 +41,7 @@
|
|
|
39
41
|
* @param {number} [args.pageUpDownOverlap=1] - Amount of rows to overlap when pageup/pagedown is used.
|
|
40
42
|
* @param {boolean} [args.persistantSelectionMode=false] - When true, selections will behave as if the command/control key is held down at all times. Conflicts with singleSelectionMode.
|
|
41
43
|
* @param {boolean} [args.singleSelectionMode=false] - When true, selections will ignore the command/control key. Conflicts with persistantSelectionMode.
|
|
44
|
+
* @param {boolean} [args.keepFocusOnMouseOut=false] - When true, grid will continue to handle keydown events when mouse is outside of the grid.
|
|
42
45
|
* @param {string} [args.selectionMode='cell'] - Can be 'cell', or 'row'. This setting dictates what will be selected when the user clicks a cell. The cell, or the entire row respectively.
|
|
43
46
|
* @param {string} [args.hoverMode='cell'] - Can be 'cell', or 'row'. This setting dictates whether to highlight either the individual cell, or the entire row when hovering over a cell.
|
|
44
47
|
* @param {boolean} [args.autoResizeColumns=false] - When true, all columns will be automatically resized to fit the data in them. Warning! Expensive for large (>100k ~2 seconds) datasets.
|
|
@@ -454,6 +457,8 @@
|
|
|
454
457
|
* @param {object} e.selectedData Selected data.
|
|
455
458
|
* @param {array} e.selections Selections object. 2D matrix of selections.
|
|
456
459
|
* @param {rectangle} e.selectionBounds rectangle object describing the selection bounds.
|
|
460
|
+
* @param {object} e.boundRowIndexMap Mapping of view data row to bound data row index.
|
|
461
|
+
* @param {object} e.boundColumnIndexMap Mapping of view column order to bound column order.
|
|
457
462
|
*/
|
|
458
463
|
/**
|
|
459
464
|
* Fires when the user clicks on the "drill in" arrow. When the arrow is clicked a new
|
package/lib/draw.js
CHANGED
|
@@ -122,15 +122,15 @@ export default function (self) {
|
|
|
122
122
|
x = x + ml;
|
|
123
123
|
y = y + mt;
|
|
124
124
|
if (self.orderDirection === 'asc') {
|
|
125
|
-
self.ctx.moveTo(x, y);
|
|
126
|
-
self.ctx.lineTo(x + aw, y);
|
|
127
|
-
self.ctx.lineTo(x + aw * 0.5, y + ah);
|
|
128
|
-
self.ctx.moveTo(x, y);
|
|
129
|
-
} else {
|
|
130
125
|
self.ctx.lineTo(x, y + ah);
|
|
131
126
|
self.ctx.lineTo(x + aw, y + ah);
|
|
132
127
|
self.ctx.lineTo(x + aw * 0.5, y);
|
|
133
128
|
self.ctx.lineTo(x, y + ah);
|
|
129
|
+
} else {
|
|
130
|
+
self.ctx.moveTo(x, y);
|
|
131
|
+
self.ctx.lineTo(x + aw, y);
|
|
132
|
+
self.ctx.lineTo(x + aw * 0.5, y + ah);
|
|
133
|
+
self.ctx.moveTo(x, y);
|
|
134
134
|
}
|
|
135
135
|
self.ctx.stroke();
|
|
136
136
|
self.ctx.fill();
|
|
@@ -911,7 +911,9 @@ export default function (self) {
|
|
|
911
911
|
viewColumnIndex: columnOrderIndex,
|
|
912
912
|
|
|
913
913
|
boundRowIndex: self.getBoundRowIndexFromViewRowIndex(rowOrderIndex),
|
|
914
|
-
boundColumnIndex:
|
|
914
|
+
boundColumnIndex: self.getBoundColumnIndexFromViewColumnIndex(
|
|
915
|
+
columnOrderIndex,
|
|
916
|
+
),
|
|
915
917
|
|
|
916
918
|
sortColumnIndex: headerIndex,
|
|
917
919
|
sortRowIndex: rowIndex,
|
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
'use strict';
|
|
4
4
|
|
|
5
5
|
import isPrintableKeyEvent from 'is-printable-key-event';
|
|
6
|
+
import { isSupportedHtml, parseData } from './util';
|
|
6
7
|
|
|
7
8
|
export default function (self) {
|
|
8
9
|
var wheeling;
|
|
@@ -518,13 +519,12 @@ export default function (self) {
|
|
|
518
519
|
self.selections[cell.rowIndex].indexOf(cell.columnIndex) === -1
|
|
519
520
|
) {
|
|
520
521
|
self.selections[cell.rowIndex].push(cell.columnIndex);
|
|
521
|
-
|
|
522
522
|
const event = {
|
|
523
523
|
selections: self.selections,
|
|
524
524
|
selectedData: self.getSelectedData(),
|
|
525
|
+
selectedCells: self.getSelectedCells(),
|
|
525
526
|
selectionBounds: self.getSelectionBounds(),
|
|
526
527
|
};
|
|
527
|
-
|
|
528
528
|
self.dispatchEvent('selectionchanged', event);
|
|
529
529
|
}
|
|
530
530
|
}
|
|
@@ -583,12 +583,14 @@ export default function (self) {
|
|
|
583
583
|
function checkSelectionChange() {
|
|
584
584
|
var ev,
|
|
585
585
|
sb = self.getSelectionBounds();
|
|
586
|
+
|
|
586
587
|
if (startingBounds === JSON.stringify(sb)) {
|
|
587
588
|
return;
|
|
588
589
|
}
|
|
589
590
|
ev = {
|
|
590
591
|
selections: self.selections,
|
|
591
592
|
selectionBounds: self.getSelectionBounds(),
|
|
593
|
+
selectedCells: self.getSelectedCells(),
|
|
592
594
|
};
|
|
593
595
|
Object.defineProperty(ev, 'selectedData', {
|
|
594
596
|
get: function () {
|
|
@@ -897,6 +899,7 @@ export default function (self) {
|
|
|
897
899
|
self.stopDragReorder = function (e) {
|
|
898
900
|
var oIndex,
|
|
899
901
|
tIndex,
|
|
902
|
+
odata,
|
|
900
903
|
cr = {
|
|
901
904
|
'row-reorder': self.orders.rows,
|
|
902
905
|
'column-reorder': self.orders.columns,
|
|
@@ -927,12 +930,15 @@ export default function (self) {
|
|
|
927
930
|
self.ignoreNextClick = true;
|
|
928
931
|
oIndex = cr[self.dragMode].indexOf(self.reorderObject[i]);
|
|
929
932
|
tIndex = cr[self.dragMode].indexOf(self.reorderTarget[i]);
|
|
930
|
-
cr[self.dragMode].splice(oIndex, 1);
|
|
931
|
-
cr[self.dragMode].splice(tIndex, 0, self.reorderObject[i]);
|
|
932
933
|
if (self.dragMode === 'column-reorder') {
|
|
934
|
+
cr[self.dragMode].splice(oIndex, 1);
|
|
935
|
+
cr[self.dragMode].splice(tIndex, 0, self.reorderObject[i]);
|
|
933
936
|
self.orders.columns = cr[self.dragMode];
|
|
934
937
|
} else {
|
|
935
|
-
self.orders.rows = cr[self.dragMode];
|
|
938
|
+
// self.orders.rows = cr[self.dragMode];
|
|
939
|
+
odata = self.viewData[oIndex];
|
|
940
|
+
self.viewData[oIndex] = self.viewData[tIndex];
|
|
941
|
+
self.viewData[tIndex] = odata;
|
|
936
942
|
}
|
|
937
943
|
self.resize();
|
|
938
944
|
self.setStorageData();
|
|
@@ -1167,6 +1173,13 @@ export default function (self) {
|
|
|
1167
1173
|
}
|
|
1168
1174
|
if (['row-reorder', 'column-reorder'].indexOf(self.dragMode) !== -1) {
|
|
1169
1175
|
self.draggingItem = self.dragStartObject;
|
|
1176
|
+
if (
|
|
1177
|
+
self.dragMode === 'column-reorder' &&
|
|
1178
|
+
self.attributes.columnHeaderClickBehavior === 'select'
|
|
1179
|
+
) {
|
|
1180
|
+
self.selectColumn(self.currentCell.header.index, ctrl, e.shiftKey);
|
|
1181
|
+
self.draw();
|
|
1182
|
+
}
|
|
1170
1183
|
document.body.addEventListener('mousemove', self.dragReorder, false);
|
|
1171
1184
|
document.body.addEventListener('mouseup', self.stopDragReorder, false);
|
|
1172
1185
|
return;
|
|
@@ -1174,9 +1187,14 @@ export default function (self) {
|
|
|
1174
1187
|
};
|
|
1175
1188
|
self.mouseup = function (e) {
|
|
1176
1189
|
clearTimeout(self.scrollTimer);
|
|
1190
|
+
var ctrl = e.ctrlKey || e.metaKey;
|
|
1177
1191
|
self.cellBoundaryCrossed = true;
|
|
1178
1192
|
self.rowBoundaryCrossed = true;
|
|
1179
1193
|
self.columnBoundaryCrossed = true;
|
|
1194
|
+
if (self.draggingItem) {
|
|
1195
|
+
self.selectColumn(self.currentCell.header.index, ctrl, e.shiftKey);
|
|
1196
|
+
self.draw();
|
|
1197
|
+
}
|
|
1180
1198
|
self.selecting = undefined;
|
|
1181
1199
|
self.draggingItem = undefined;
|
|
1182
1200
|
self.dragStartObject = undefined;
|
|
@@ -1246,7 +1264,7 @@ export default function (self) {
|
|
|
1246
1264
|
return;
|
|
1247
1265
|
}
|
|
1248
1266
|
|
|
1249
|
-
if (!self.hasFocus) {
|
|
1267
|
+
if (!self.attributes.keepFocusOnMouseOut && !self.hasFocus) {
|
|
1250
1268
|
return;
|
|
1251
1269
|
}
|
|
1252
1270
|
|
|
@@ -1401,6 +1419,7 @@ export default function (self) {
|
|
|
1401
1419
|
selectedData: self.getSelectedData(),
|
|
1402
1420
|
selections: self.selections,
|
|
1403
1421
|
selectionBounds: self.getSelectionBounds(),
|
|
1422
|
+
selectedCells: self.getSelectedCells(),
|
|
1404
1423
|
};
|
|
1405
1424
|
Object.defineProperty(ev, 'selectedData', {
|
|
1406
1425
|
get: function () {
|
|
@@ -1497,24 +1516,13 @@ export default function (self) {
|
|
|
1497
1516
|
}, 1);
|
|
1498
1517
|
}
|
|
1499
1518
|
};
|
|
1500
|
-
|
|
1501
1519
|
self.pasteData = function (
|
|
1502
1520
|
pasteValue,
|
|
1503
1521
|
mimeType,
|
|
1504
1522
|
startRowIndex,
|
|
1505
1523
|
startColIndex,
|
|
1506
1524
|
) {
|
|
1507
|
-
|
|
1508
|
-
const isSupportedHtmlTable = (htmlString) =>
|
|
1509
|
-
/(?:^(<meta[^>]+>)?<table.*?>)|(?:<!--StartFragment-->\s*<table.*?>)/.test(
|
|
1510
|
-
htmlString,
|
|
1511
|
-
);
|
|
1512
|
-
|
|
1513
|
-
// TODO: support pasting tables from Excel
|
|
1514
|
-
if (
|
|
1515
|
-
mimeType === 'text/html' &&
|
|
1516
|
-
isSupportedHtmlTable(pasteValue) === false
|
|
1517
|
-
) {
|
|
1525
|
+
if (mimeType === 'text/html' && !isSupportedHtml(pasteValue)) {
|
|
1518
1526
|
console.warn(
|
|
1519
1527
|
'Unrecognized HTML format. HTML must be a simple table, e.g.: <table><tr><td>data</td></tr></table>.',
|
|
1520
1528
|
);
|
|
@@ -1524,48 +1532,12 @@ export default function (self) {
|
|
|
1524
1532
|
|
|
1525
1533
|
return;
|
|
1526
1534
|
}
|
|
1527
|
-
|
|
1528
|
-
function parseData(data, mimeType) {
|
|
1529
|
-
// TODO: use DOMParser
|
|
1530
|
-
if (mimeType === 'text/html') {
|
|
1531
|
-
const doc = new DOMParser().parseFromString(data, 'text/html');
|
|
1532
|
-
const trs = doc.querySelectorAll('table tr');
|
|
1533
|
-
const rows = [];
|
|
1534
|
-
trs.forEach((tr) => {
|
|
1535
|
-
const row = [];
|
|
1536
|
-
tr.childNodes.forEach((td) => {
|
|
1537
|
-
if (td.nodeName === '#text') return;
|
|
1538
|
-
const col = {
|
|
1539
|
-
// style: {}, // keep style if need
|
|
1540
|
-
value: [],
|
|
1541
|
-
};
|
|
1542
|
-
td.childNodes.forEach((ele) => {
|
|
1543
|
-
col.value.push({
|
|
1544
|
-
// style: {}, // keep style if need
|
|
1545
|
-
value: ele.nodeType === 1 ? ele.innerText : ele.data,
|
|
1546
|
-
});
|
|
1547
|
-
});
|
|
1548
|
-
row.push(col);
|
|
1549
|
-
});
|
|
1550
|
-
rows.push(row);
|
|
1551
|
-
});
|
|
1552
|
-
return rows;
|
|
1553
|
-
}
|
|
1554
|
-
|
|
1555
|
-
// Default data format is string, so split on new line,
|
|
1556
|
-
// and then enclose in an array (a row with one cell):
|
|
1557
|
-
return data
|
|
1558
|
-
.split('\n')
|
|
1559
|
-
.map((item) =>
|
|
1560
|
-
item.split('\t').map((value) => ({ value: [{ value }] })),
|
|
1561
|
-
);
|
|
1562
|
-
}
|
|
1563
|
-
|
|
1535
|
+
var schema = self.getSchema();
|
|
1564
1536
|
var rows = parseData(pasteValue, mimeType);
|
|
1565
1537
|
|
|
1566
1538
|
// selected cell. This mimics Excel's paste functionality, and works
|
|
1567
1539
|
// as a poor-man's fill-down.
|
|
1568
|
-
if (rows.length === 1) {
|
|
1540
|
+
if (rows.length === 1 && rows[0].length === 1) {
|
|
1569
1541
|
var cellData = rows[0][0].value.map((item) => item.value).join();
|
|
1570
1542
|
|
|
1571
1543
|
self.forEachSelectedCell(function (data, rowIndex, colName) {
|
|
@@ -1627,7 +1599,8 @@ export default function (self) {
|
|
|
1627
1599
|
affectedCells.push([
|
|
1628
1600
|
rowIndex,
|
|
1629
1601
|
columnIndex,
|
|
1630
|
-
self.
|
|
1602
|
+
self.getBoundRowIndexFromViewRowIndex(rowIndex),
|
|
1603
|
+
self.getBoundColumnIndexFromViewColumnIndex(columnIndex),
|
|
1631
1604
|
]);
|
|
1632
1605
|
});
|
|
1633
1606
|
});
|
|
@@ -1722,9 +1695,34 @@ export default function (self) {
|
|
|
1722
1695
|
};
|
|
1723
1696
|
self.cut = function (e) {
|
|
1724
1697
|
self.copy(e);
|
|
1725
|
-
self.
|
|
1726
|
-
|
|
1727
|
-
|
|
1698
|
+
const schema = self.getSchema();
|
|
1699
|
+
|
|
1700
|
+
var affectedCells = [];
|
|
1701
|
+
for (const [rowIndex, row] of self.selections.entries()) {
|
|
1702
|
+
if (!row) continue;
|
|
1703
|
+
|
|
1704
|
+
const boundRowIndex = self.getBoundRowIndexFromViewRowIndex(rowIndex);
|
|
1705
|
+
|
|
1706
|
+
for (const columnIndex of row) {
|
|
1707
|
+
const boundColumnIndex = self.getBoundColumnIndexFromViewColumnIndex(
|
|
1708
|
+
columnIndex,
|
|
1709
|
+
);
|
|
1710
|
+
const colName = schema[boundColumnIndex].name;
|
|
1711
|
+
|
|
1712
|
+
self.viewData[rowIndex][colName] = '';
|
|
1713
|
+
|
|
1714
|
+
affectedCells.push([
|
|
1715
|
+
rowIndex,
|
|
1716
|
+
columnIndex,
|
|
1717
|
+
boundRowIndex,
|
|
1718
|
+
boundColumnIndex,
|
|
1719
|
+
]);
|
|
1720
|
+
}
|
|
1721
|
+
}
|
|
1722
|
+
|
|
1723
|
+
if (self.dispatchEvent('cut', { NativeEvent: e, cells: affectedCells })) {
|
|
1724
|
+
return;
|
|
1725
|
+
}
|
|
1728
1726
|
};
|
|
1729
1727
|
self.copy = function (e) {
|
|
1730
1728
|
if (self.dispatchEvent('copy', { NativeEvent: e })) {
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const isSupportedHtml = function (pasteValue) {
|
|
4
|
+
// We need to match new lines in the HTML, .* won't match new line characters.
|
|
5
|
+
// `s` regex modifier can't be used with `ecmaVersion === 2017`.
|
|
6
|
+
// As a workaround using [\s\S]*. Fix when we upgrade `ecmaVersion`.
|
|
7
|
+
const genericDiv = /(?:^(<meta[^>]*>)?[\s\S]*<div[^>]*>)/;
|
|
8
|
+
const genericSpan = /(?:^(<meta[^>]*>)?[\s\S]*<span[^>]*>)/;
|
|
9
|
+
const genericTable = /(?:^(<meta[^>]*>)?[\s\S]*<table[^>]*>)/; // Matches Google Sheets format clipboard data format too.
|
|
10
|
+
const excelTable = /(?:<!--StartFragment-->[\s\S]*<tr[^>]*>)/;
|
|
11
|
+
const excelTableRow = /(?:<!--StartFragment-->[\s\S]*<td[^>]*>)/;
|
|
12
|
+
|
|
13
|
+
return [
|
|
14
|
+
genericDiv,
|
|
15
|
+
genericTable,
|
|
16
|
+
genericSpan,
|
|
17
|
+
excelTable,
|
|
18
|
+
excelTableRow,
|
|
19
|
+
].some((expression) => expression.test(pasteValue));
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
// Explanation of nodeType here: https://developer.mozilla.org/en-US/docs/Web/API/Node/nodeType
|
|
23
|
+
const IGNORE_NODETYPES = [8, 3]; // '#text' and '#comment'
|
|
24
|
+
|
|
25
|
+
const isHtmlTable = function (pasteValue) {
|
|
26
|
+
return /(?:<table[^>]*>)|(?:<tr[^]*>)/.test(pasteValue);
|
|
27
|
+
};
|
|
28
|
+
|
|
29
|
+
const sanitizeElementData = function (element) {
|
|
30
|
+
// It is not entirely clear if this check on nodeType is required.
|
|
31
|
+
let elementData = element.nodeType === 1 ? element.innerText : element.data;
|
|
32
|
+
|
|
33
|
+
return String(elementData).replace(/\s+/g, ' ').trim();
|
|
34
|
+
};
|
|
35
|
+
|
|
36
|
+
const parseHtmlText = function (data) {
|
|
37
|
+
const doc = new DOMParser().parseFromString(data, 'text/html');
|
|
38
|
+
const element = doc.querySelector('div') || doc.querySelector('span');
|
|
39
|
+
const elementData = sanitizeElementData(element);
|
|
40
|
+
|
|
41
|
+
return elementData
|
|
42
|
+
.split('\n')
|
|
43
|
+
.map((item) => item.split('\t').map((value) => ({ value: [{ value }] })));
|
|
44
|
+
};
|
|
45
|
+
|
|
46
|
+
const parseHtmlTable = function (data) {
|
|
47
|
+
const doc = new DOMParser().parseFromString(data, 'text/html');
|
|
48
|
+
const trs = doc.querySelectorAll('table tr');
|
|
49
|
+
const rows = [];
|
|
50
|
+
|
|
51
|
+
for (const tr of trs) {
|
|
52
|
+
const row = [];
|
|
53
|
+
|
|
54
|
+
for (const childNode of tr.childNodes) {
|
|
55
|
+
if (IGNORE_NODETYPES.includes(childNode.nodeType)) continue;
|
|
56
|
+
|
|
57
|
+
const col = { value: [] };
|
|
58
|
+
const value = sanitizeElementData(childNode);
|
|
59
|
+
|
|
60
|
+
if (value) col.value.push({ value });
|
|
61
|
+
|
|
62
|
+
row.push(col);
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
rows.push(row);
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
return rows;
|
|
69
|
+
};
|
|
70
|
+
|
|
71
|
+
const parseText = function (data) {
|
|
72
|
+
return data
|
|
73
|
+
.split('\n')
|
|
74
|
+
.map((item) => item.split('\t').map((value) => ({ value: [{ value }] })));
|
|
75
|
+
};
|
|
76
|
+
|
|
77
|
+
const parseData = function (data, mimeType) {
|
|
78
|
+
if (mimeType === 'text/html' && isHtmlTable(data)) {
|
|
79
|
+
return parseHtmlTable(data);
|
|
80
|
+
} else if (mimeType === 'text/html') {
|
|
81
|
+
return parseHtmlText(data);
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
// Default data format is string, so split on new line,
|
|
85
|
+
// and then enclose in an array (a row with one cell):
|
|
86
|
+
return parseText(data);
|
|
87
|
+
};
|
|
88
|
+
|
|
89
|
+
export {
|
|
90
|
+
isSupportedHtml,
|
|
91
|
+
sanitizeElementData,
|
|
92
|
+
parseData,
|
|
93
|
+
parseHtmlTable,
|
|
94
|
+
parseHtmlText,
|
|
95
|
+
parseText,
|
|
96
|
+
};
|
package/lib/intf.js
CHANGED
|
@@ -184,6 +184,54 @@ export default function (self, ctor) {
|
|
|
184
184
|
|
|
185
185
|
return selectedData;
|
|
186
186
|
};
|
|
187
|
+
self.getSelectedCells = function (expandToRow) {
|
|
188
|
+
const selectedCells = [];
|
|
189
|
+
const schema = self.getSchema();
|
|
190
|
+
const viewDataLength = self.viewData.length;
|
|
191
|
+
|
|
192
|
+
if (viewDataLength === 0) {
|
|
193
|
+
return [];
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
for (const [viewRowIndex, row] of self.selections.entries()) {
|
|
197
|
+
if (!row || row.length === 0) continue;
|
|
198
|
+
|
|
199
|
+
for (const [, viewColumnIndex] of row.entries()) {
|
|
200
|
+
if (viewColumnIndex === -1 || !schema[viewColumnIndex]) {
|
|
201
|
+
continue;
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
const boundColumnIndex = self.getBoundColumnIndexFromViewColumnIndex(
|
|
205
|
+
viewColumnIndex,
|
|
206
|
+
);
|
|
207
|
+
|
|
208
|
+
if (!expandToRow && schema[boundColumnIndex].hidden) {
|
|
209
|
+
continue;
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
if (self.viewData[viewRowIndex]) {
|
|
213
|
+
const header = schema[boundColumnIndex];
|
|
214
|
+
const value = self.viewData[viewRowIndex][header.name];
|
|
215
|
+
const boundRowIndex = self.getBoundRowIndexFromViewRowIndex(
|
|
216
|
+
viewRowIndex,
|
|
217
|
+
);
|
|
218
|
+
|
|
219
|
+
const cell = {
|
|
220
|
+
value,
|
|
221
|
+
header,
|
|
222
|
+
boundRowIndex,
|
|
223
|
+
boundColumnIndex,
|
|
224
|
+
viewRowIndex,
|
|
225
|
+
viewColumnIndex,
|
|
226
|
+
};
|
|
227
|
+
|
|
228
|
+
selectedCells.push(cell);
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
return selectedCells;
|
|
234
|
+
};
|
|
187
235
|
self.getBoundRowIndexFromViewRowIndex = function (viewRowIndex) {
|
|
188
236
|
if (self.boundRowIndexMap && self.boundRowIndexMap.has(viewRowIndex)) {
|
|
189
237
|
return self.boundRowIndexMap.get(viewRowIndex);
|
|
@@ -191,6 +239,9 @@ export default function (self, ctor) {
|
|
|
191
239
|
|
|
192
240
|
return undefined;
|
|
193
241
|
};
|
|
242
|
+
self.getBoundColumnIndexFromViewColumnIndex = function (viewColumnIndex) {
|
|
243
|
+
return self.orders.columns[viewColumnIndex];
|
|
244
|
+
};
|
|
194
245
|
self.getColumnHeaderCellHeight = function () {
|
|
195
246
|
if (!self.attributes.showColumnHeaders) {
|
|
196
247
|
return 0;
|
|
@@ -320,11 +371,15 @@ export default function (self, ctor) {
|
|
|
320
371
|
const currentFilterFunction =
|
|
321
372
|
header.filter || self.filter(header.type || 'string');
|
|
322
373
|
|
|
323
|
-
newViewData = newViewData.filter(function ([row]) {
|
|
324
|
-
|
|
325
|
-
|
|
374
|
+
newViewData = newViewData.filter(function ([row, originalRowIndex]) {
|
|
375
|
+
if (
|
|
376
|
+
self.attributes.allowFreezingRows &&
|
|
377
|
+
!self.attributes.filterFrozenRows &&
|
|
378
|
+
originalRowIndex < self.frozenRow
|
|
379
|
+
)
|
|
380
|
+
return true;
|
|
326
381
|
|
|
327
|
-
return
|
|
382
|
+
return currentFilterFunction(row[headerName], filterText);
|
|
328
383
|
});
|
|
329
384
|
}
|
|
330
385
|
|
|
@@ -332,7 +387,15 @@ export default function (self, ctor) {
|
|
|
332
387
|
for (const column of self.orderings.columns) {
|
|
333
388
|
const sortFn = column.sortFunction(column.orderBy, column.orderDirection);
|
|
334
389
|
|
|
335
|
-
newViewData.sort(([rowA], [rowB]) =>
|
|
390
|
+
newViewData.sort(([rowA], [rowB, rowIndexB]) => {
|
|
391
|
+
if (
|
|
392
|
+
self.attributes.allowFreezingRows &&
|
|
393
|
+
!self.attributes.sortFrozenRows &&
|
|
394
|
+
rowIndexB < self.frozenRow
|
|
395
|
+
)
|
|
396
|
+
return 0;
|
|
397
|
+
return sortFn(rowA, rowB);
|
|
398
|
+
});
|
|
336
399
|
}
|
|
337
400
|
|
|
338
401
|
return {
|
|
@@ -738,6 +801,7 @@ export default function (self, ctor) {
|
|
|
738
801
|
self.intf.clearPxColorAssertions = self.clearPxColorAssertions;
|
|
739
802
|
self.intf.integerToAlpha = self.integerToAlpha;
|
|
740
803
|
self.intf.copy = self.copy;
|
|
804
|
+
self.intf.cut = self.cut;
|
|
741
805
|
self.intf.paste = self.paste;
|
|
742
806
|
self.intf.setStyleProperty = self.setStyleProperty;
|
|
743
807
|
Object.defineProperty(self.intf, 'defaults', {
|
package/lib/publicMethods.js
CHANGED
|
@@ -353,6 +353,7 @@ export default function (self) {
|
|
|
353
353
|
selectedData: self.getSelectedData(),
|
|
354
354
|
selections: self.selections,
|
|
355
355
|
selectionBounds: self.selectionBounds,
|
|
356
|
+
selectedCells: self.getSelectedCells(),
|
|
356
357
|
});
|
|
357
358
|
if (dontDraw) {
|
|
358
359
|
return;
|
|
@@ -485,6 +486,7 @@ export default function (self) {
|
|
|
485
486
|
selectedData: self.getSelectedData(),
|
|
486
487
|
selections: self.selections,
|
|
487
488
|
selectionBounds: self.getSelectionBounds(),
|
|
489
|
+
selectedCells: self.getSelectedCells(),
|
|
488
490
|
});
|
|
489
491
|
};
|
|
490
492
|
/**
|
|
@@ -510,6 +512,7 @@ export default function (self) {
|
|
|
510
512
|
selectedData: self.getSelectedData(),
|
|
511
513
|
selections: self.selections,
|
|
512
514
|
selectionBounds: self.selectionBounds,
|
|
515
|
+
selectedCells: self.getSelectedCells(),
|
|
513
516
|
});
|
|
514
517
|
}
|
|
515
518
|
function addRow(ri) {
|
|
@@ -1252,6 +1255,7 @@ export default function (self) {
|
|
|
1252
1255
|
ev = {
|
|
1253
1256
|
selections: self.selections,
|
|
1254
1257
|
selectionBounds: self.selectionBounds,
|
|
1258
|
+
selectedCells: self.getSelectedCells(),
|
|
1255
1259
|
};
|
|
1256
1260
|
Object.defineProperty(ev, 'selectedData', {
|
|
1257
1261
|
get: function () {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "canvas-datagrid",
|
|
3
|
-
"version": "0.3.
|
|
3
|
+
"version": "0.3.15",
|
|
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",
|