canvas-datagrid 0.3.25 → 0.4.3
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 +38 -0
- package/dist/canvas-datagrid.debug.js +2913 -116
- 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 +141 -0
- package/lib/contextMenu.js +427 -10
- package/lib/defaults.js +44 -0
- package/lib/docs.js +18 -0
- package/lib/dom.js +18 -1
- package/lib/draw.js +866 -36
- package/lib/events/index.js +224 -36
- package/lib/events/util.js +1 -1
- package/lib/groups/util.js +42 -0
- package/lib/intf.js +610 -1
- package/lib/publicMethods.js +459 -4
- package/package.json +1 -1
package/lib/draw.js
CHANGED
|
@@ -108,6 +108,98 @@ export default function (self) {
|
|
|
108
108
|
'</svg>\n',
|
|
109
109
|
);
|
|
110
110
|
}
|
|
111
|
+
/**
|
|
112
|
+
* @param {number[]} coords [x0,y0, x1,y1, x2,y2, ...]
|
|
113
|
+
* @param {boolean} [fill] fill the area that construct by these lines but not stroke
|
|
114
|
+
*/
|
|
115
|
+
function drawLines(coords, fill) {
|
|
116
|
+
if (coords.length < 4) return;
|
|
117
|
+
self.ctx.beginPath();
|
|
118
|
+
self.ctx.moveTo(
|
|
119
|
+
coords[0] + self.canvasOffsetLeft,
|
|
120
|
+
coords[1] + self.canvasOffsetTop,
|
|
121
|
+
);
|
|
122
|
+
for (let i = 2; i < coords.length; i += 2) {
|
|
123
|
+
const x = coords[i] + self.canvasOffsetLeft;
|
|
124
|
+
const y = coords[i + 1] + self.canvasOffsetTop;
|
|
125
|
+
self.ctx.lineTo(x, y);
|
|
126
|
+
}
|
|
127
|
+
if (fill) self.ctx.fill();
|
|
128
|
+
else self.ctx.stroke();
|
|
129
|
+
}
|
|
130
|
+
/**
|
|
131
|
+
* @param {number} x based-X (left-top)
|
|
132
|
+
* @param {number} y based-Y (left-top)
|
|
133
|
+
* @param {number} width
|
|
134
|
+
* @param {boolean} collapsed true: '+'; false: '-'
|
|
135
|
+
*/
|
|
136
|
+
function drawGroupHandle(x, y, width, collapsed) {
|
|
137
|
+
fillRect(x, y, width, width);
|
|
138
|
+
strokeRect(x, y, width, width);
|
|
139
|
+
const cx = x + width * 0.5;
|
|
140
|
+
const cy = y + width * 0.5;
|
|
141
|
+
drawLines([x + width * 0.2, cy, x + width * 0.78, cy]);
|
|
142
|
+
if (collapsed) drawLines([cx, y + width * 0.22, cx, y + width * 0.8]);
|
|
143
|
+
}
|
|
144
|
+
/**
|
|
145
|
+
* @param {number} x
|
|
146
|
+
* @param {number} y
|
|
147
|
+
* @param {number} size
|
|
148
|
+
* @param {string} dir Direction of the triangle, one of the 't','b','l' and 'r'
|
|
149
|
+
* @param {boolean} [active]
|
|
150
|
+
*/
|
|
151
|
+
function drawUnhideIndicator(x, y, size, dir, active) {
|
|
152
|
+
const minPadding = size * 0.2;
|
|
153
|
+
const maxPadding = size * 0.3;
|
|
154
|
+
/** The long edge width of the triangle */
|
|
155
|
+
const longEdge = size - 2 * minPadding;
|
|
156
|
+
/** The median width of the triangle */
|
|
157
|
+
const median = size - 2 * maxPadding;
|
|
158
|
+
const halfLongEdge = longEdge * 0.5;
|
|
159
|
+
let x0, y0;
|
|
160
|
+
let coords, borderCoords;
|
|
161
|
+
switch (dir) {
|
|
162
|
+
case 'r':
|
|
163
|
+
x0 = x + maxPadding;
|
|
164
|
+
y0 = y + minPadding;
|
|
165
|
+
borderCoords = [x, y, x + size, y, x + size, y + size, x, y + size];
|
|
166
|
+
coords = [x0, y0, x0, y0 + longEdge, x0 + median, y0 + halfLongEdge];
|
|
167
|
+
break;
|
|
168
|
+
case 'l':
|
|
169
|
+
x0 = x + size - maxPadding;
|
|
170
|
+
y0 = y + minPadding;
|
|
171
|
+
borderCoords = [x + size, y, x, y, x, y + size, x + size, y + size];
|
|
172
|
+
coords = [x0, y0, x0, y0 + longEdge, x0 - median, y0 + halfLongEdge];
|
|
173
|
+
break;
|
|
174
|
+
case 't':
|
|
175
|
+
x0 = x + minPadding;
|
|
176
|
+
y0 = y + size - maxPadding;
|
|
177
|
+
borderCoords = [x, y + size, x, y, x + size, y, x + size, y + size];
|
|
178
|
+
coords = [x0, y0, x0 + longEdge, y0, x0 + halfLongEdge, y0 - median];
|
|
179
|
+
break;
|
|
180
|
+
case 'b':
|
|
181
|
+
x0 = x + minPadding;
|
|
182
|
+
y0 = y + maxPadding;
|
|
183
|
+
borderCoords = [x, y, x, y + size, x + size, y + size, x + size, y];
|
|
184
|
+
coords = [x0, y0, x0 + longEdge, y0, x0 + halfLongEdge, y0 + median];
|
|
185
|
+
break;
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
if (active) {
|
|
189
|
+
self.ctx.strokeStyle = self.style.unhideIndicatorBorderColor;
|
|
190
|
+
self.ctx.lineWidth = 2;
|
|
191
|
+
drawLines(borderCoords);
|
|
192
|
+
|
|
193
|
+
self.ctx.fillStyle = self.style.unhideIndicatorBackgroundColor;
|
|
194
|
+
let offset = dir === 'r' || dir === 'b' ? 1 : 0;
|
|
195
|
+
if (dir === 'l' || dir === 'r')
|
|
196
|
+
fillRect(x - offset, y, size + offset, size);
|
|
197
|
+
else fillRect(x, y - offset, size, size + offset);
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
self.ctx.fillStyle = self.style.unhideIndicatorColor;
|
|
201
|
+
drawLines(coords, true);
|
|
202
|
+
}
|
|
111
203
|
function drawOrderByArrow(x, y) {
|
|
112
204
|
var mt = self.style.columnHeaderOrderByArrowMarginTop * self.scale,
|
|
113
205
|
ml = self.style.columnHeaderOrderByArrowMarginLeft * self.scale,
|
|
@@ -164,6 +256,54 @@ export default function (self) {
|
|
|
164
256
|
self.ctx.fill();
|
|
165
257
|
return ml + aw + mr;
|
|
166
258
|
}
|
|
259
|
+
function drawCellTreeIcon(cell, tree, rowTree) {
|
|
260
|
+
const parentCount = rowTree ? tree.parentCount : 0;
|
|
261
|
+
const iconSize = self.style.cellTreeIconWidth * self.scale,
|
|
262
|
+
marginTop = self.style.cellTreeIconMarginTop * self.scale,
|
|
263
|
+
marginRight = self.style.cellTreeIconMarginRight * self.scale,
|
|
264
|
+
marginLeft =
|
|
265
|
+
self.style.cellTreeIconMarginLeft * self.scale +
|
|
266
|
+
parentCount * (iconSize + cell.paddingLeft);
|
|
267
|
+
|
|
268
|
+
let x = cell.x + cell.paddingLeft + self.canvasOffsetLeft + marginLeft,
|
|
269
|
+
y = cell.y + self.canvasOffsetTop + marginTop;
|
|
270
|
+
|
|
271
|
+
if (tree.icon) {
|
|
272
|
+
self.ctx.beginPath();
|
|
273
|
+
const oldFillStyle = self.ctx.fillStyle;
|
|
274
|
+
const oldStrokeStyle = self.ctx.strokeStyle;
|
|
275
|
+
|
|
276
|
+
if (cell.hovered && self.hovers.onCellTreeIcon) {
|
|
277
|
+
self.ctx.fillStyle = self.style.cellTreeIconHoverFillColor;
|
|
278
|
+
} else {
|
|
279
|
+
self.ctx.fillStyle = self.style.cellTreeIconFillColor;
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
self.ctx.fillRect(x, y, iconSize, iconSize);
|
|
283
|
+
self.ctx.strokeStyle = self.style.cellTreeIconBorderColor;
|
|
284
|
+
self.ctx.rect(x, y, iconSize, iconSize);
|
|
285
|
+
self.ctx.stroke();
|
|
286
|
+
|
|
287
|
+
self.ctx.beginPath();
|
|
288
|
+
if (tree.expand) {
|
|
289
|
+
self.ctx.moveTo(x + 3, y + iconSize * 0.5);
|
|
290
|
+
self.ctx.lineTo(x + iconSize - 3, y + iconSize * 0.5);
|
|
291
|
+
} else {
|
|
292
|
+
self.ctx.moveTo(x + 2, y + iconSize * 0.5);
|
|
293
|
+
self.ctx.lineTo(x + iconSize - 2, y + iconSize * 0.5);
|
|
294
|
+
self.ctx.moveTo(x + iconSize * 0.5, y + 2);
|
|
295
|
+
self.ctx.lineTo(x + iconSize * 0.5, y + iconSize - 2);
|
|
296
|
+
}
|
|
297
|
+
self.ctx.lineWidth = self.style.cellTreeIconLineWidth;
|
|
298
|
+
self.ctx.strokeStyle = self.style.cellTreeIconLineColor;
|
|
299
|
+
self.ctx.stroke();
|
|
300
|
+
|
|
301
|
+
self.ctx.strokeStyle = oldStrokeStyle;
|
|
302
|
+
self.ctx.fillStyle = oldFillStyle;
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
return marginLeft + iconSize + marginRight;
|
|
306
|
+
}
|
|
167
307
|
function drawFilterButtonArrow(x, y) {
|
|
168
308
|
var mt =
|
|
169
309
|
((self.style.filterButtonHeight - self.style.filterButtonArrowHeight) /
|
|
@@ -460,7 +600,7 @@ export default function (self) {
|
|
|
460
600
|
height: cell.calculatedLineHeight * lines.length,
|
|
461
601
|
};
|
|
462
602
|
}
|
|
463
|
-
function drawText(cell) {
|
|
603
|
+
function drawText(cell, treeCellPadding = 0) {
|
|
464
604
|
var ll = cell.text.lines.length,
|
|
465
605
|
h = cell.fontHeight * cell.lineHeight,
|
|
466
606
|
x,
|
|
@@ -476,12 +616,18 @@ export default function (self) {
|
|
|
476
616
|
0.5,
|
|
477
617
|
0,
|
|
478
618
|
) + h,
|
|
479
|
-
hPos =
|
|
619
|
+
hPos =
|
|
620
|
+
cell.paddingLeft +
|
|
621
|
+
treeCellPadding +
|
|
622
|
+
cell.treeArrowWidth +
|
|
623
|
+
cell.orderByArrowWidth;
|
|
480
624
|
if (cell.horizontalAlignment === 'right') {
|
|
481
|
-
hPos =
|
|
625
|
+
hPos =
|
|
626
|
+
cell.paddingLeft + treeCellPadding + cell.paddedWidth - line.width;
|
|
482
627
|
} else if (cell.horizontalAlignment === 'center') {
|
|
483
628
|
hPos =
|
|
484
629
|
cell.paddingLeft +
|
|
630
|
+
treeCellPadding +
|
|
485
631
|
(cell.paddedWidth + cell.paddingRight) / 2 -
|
|
486
632
|
line.width / 2;
|
|
487
633
|
}
|
|
@@ -538,6 +684,7 @@ export default function (self) {
|
|
|
538
684
|
s = self.getSchema(),
|
|
539
685
|
x = 0,
|
|
540
686
|
n = Math.min(self.frozenColumn, s.length),
|
|
687
|
+
collapsedGroups = self.getCollapsedColumnGroups(),
|
|
541
688
|
column;
|
|
542
689
|
hiddenFrozenColumnCount = 0;
|
|
543
690
|
while (x < n) {
|
|
@@ -545,7 +692,15 @@ export default function (self) {
|
|
|
545
692
|
if (column.hidden) {
|
|
546
693
|
hiddenFrozenColumnCount += 1;
|
|
547
694
|
} else {
|
|
548
|
-
|
|
695
|
+
const isCollapsed =
|
|
696
|
+
collapsedGroups.findIndex(
|
|
697
|
+
(group) => x >= group.from && x <= group.to,
|
|
698
|
+
) >= 0;
|
|
699
|
+
if (isCollapsed) {
|
|
700
|
+
hiddenFrozenColumnCount += 1;
|
|
701
|
+
} else {
|
|
702
|
+
w += self.getColumnWidth(x);
|
|
703
|
+
}
|
|
549
704
|
}
|
|
550
705
|
x += 1;
|
|
551
706
|
}
|
|
@@ -615,7 +770,25 @@ export default function (self) {
|
|
|
615
770
|
u = self.currentCell || {},
|
|
616
771
|
columnHeaderCellHeight = self.getColumnHeaderCellHeight(),
|
|
617
772
|
rowHeaderCellWidth = self.getRowHeaderCellWidth(),
|
|
618
|
-
|
|
773
|
+
rowGroupsAreaWidth = self.getRowGroupAreaWidth(),
|
|
774
|
+
columnGroupsAreaHeight = self.getColumnGroupAreaHeight(),
|
|
775
|
+
/** key: boundRowIndex, value: `{y,h}` */
|
|
776
|
+
rowGroupsRectInfo = {},
|
|
777
|
+
/** value: `{y,h}` */
|
|
778
|
+
rowGroupsFrozenInfo = {},
|
|
779
|
+
/** key: columnIndex, value: `{x,w}` */
|
|
780
|
+
columnGroupsRectInfo = {},
|
|
781
|
+
collapsedColumnGroups = self.getCollapsedColumnGroups(),
|
|
782
|
+
collapsedRowGroups = self.getCollapsedRowGroups(),
|
|
783
|
+
cellHeight = self.style.cellHeight,
|
|
784
|
+
currentRowIndexOffset = 0,
|
|
785
|
+
/** @type {Array<{from:number,plus:number}>} */
|
|
786
|
+
rowIndexOffsetByHiddenRows = self.hiddenRowRanges
|
|
787
|
+
.map((range) => ({
|
|
788
|
+
from: range[0],
|
|
789
|
+
plus: range[1] - range[0] + 1,
|
|
790
|
+
}))
|
|
791
|
+
.sort((a, b) => a.from - b.from);
|
|
619
792
|
drawCount += 1;
|
|
620
793
|
p = performance.now();
|
|
621
794
|
self.visibleRowHeights = [];
|
|
@@ -623,6 +796,31 @@ export default function (self) {
|
|
|
623
796
|
if (viewData.length > self.orders.rows.length) {
|
|
624
797
|
self.createRowOrders();
|
|
625
798
|
}
|
|
799
|
+
function saveRowGroupsRectInfo(cell) {
|
|
800
|
+
let index = cell.boundRowIndex;
|
|
801
|
+
if (index >= -1 === false)
|
|
802
|
+
if (cell.rowIndex === -1) index = -1;
|
|
803
|
+
else return;
|
|
804
|
+
if (rowGroupsRectInfo[index]) return;
|
|
805
|
+
rowGroupsRectInfo[index] = { y: cell.y, h: cell.height };
|
|
806
|
+
}
|
|
807
|
+
function saveColumnGroupsRectInfo(cell) {
|
|
808
|
+
const index = cell.columnIndex;
|
|
809
|
+
if (columnGroupsRectInfo[index]) return;
|
|
810
|
+
columnGroupsRectInfo[index] = { x: cell.x, w: cell.width };
|
|
811
|
+
}
|
|
812
|
+
/**
|
|
813
|
+
* @param {number} columnIndex
|
|
814
|
+
* @returns {boolean}
|
|
815
|
+
*/
|
|
816
|
+
function isColumnCollapsedByGroup(columnIndex) {
|
|
817
|
+
return (
|
|
818
|
+
collapsedColumnGroups.findIndex(
|
|
819
|
+
(group) => columnIndex >= group.from && columnIndex <= group.to,
|
|
820
|
+
) >= 0
|
|
821
|
+
);
|
|
822
|
+
}
|
|
823
|
+
|
|
626
824
|
function drawScrollBars() {
|
|
627
825
|
var drawCorner,
|
|
628
826
|
en = self.scrollBox.entities,
|
|
@@ -976,6 +1174,15 @@ export default function (self) {
|
|
|
976
1174
|
activeHeader: activeHeader,
|
|
977
1175
|
value:
|
|
978
1176
|
isHeader && !isRowHeader ? header.title || header.name : rawValue,
|
|
1177
|
+
isRowTree:
|
|
1178
|
+
rowOrderIndex >= 0 &&
|
|
1179
|
+
columnOrderIndex == self.cellTree.rowTreeColIndex &&
|
|
1180
|
+
self.cellTree.rows.length > 0 &&
|
|
1181
|
+
self.cellTree.rows[rowOrderIndex].icon,
|
|
1182
|
+
isColumnTree:
|
|
1183
|
+
columnOrderIndex >= 0 &&
|
|
1184
|
+
self.cellTree.columns[rowOrderIndex] &&
|
|
1185
|
+
self.cellTree.columns[rowOrderIndex][columnOrderIndex].icon,
|
|
979
1186
|
};
|
|
980
1187
|
cell.calculatedLineHeight =
|
|
981
1188
|
cell.fontHeight * cell.lineHeight + cell.lineSpacing;
|
|
@@ -987,6 +1194,8 @@ export default function (self) {
|
|
|
987
1194
|
? self.sizes.columns.cornerCell
|
|
988
1195
|
: self.sizes.columns[headerIndex];
|
|
989
1196
|
self.visibleCells.unshift(cell);
|
|
1197
|
+
saveRowGroupsRectInfo(cell);
|
|
1198
|
+
saveColumnGroupsRectInfo(cell);
|
|
990
1199
|
if (self.dispatchEvent('beforerendercell', ev)) {
|
|
991
1200
|
return;
|
|
992
1201
|
}
|
|
@@ -1129,7 +1338,53 @@ export default function (self) {
|
|
|
1129
1338
|
if (cell.innerHTML || header.type === 'html') {
|
|
1130
1339
|
drawHtml(cell);
|
|
1131
1340
|
} else {
|
|
1132
|
-
|
|
1341
|
+
let treeCellPadding = 0,
|
|
1342
|
+
isDrawText = true;
|
|
1343
|
+
if (
|
|
1344
|
+
cell.columnIndex == self.cellTree.rowTreeColIndex &&
|
|
1345
|
+
!cell.isColumnHeader &&
|
|
1346
|
+
self.cellTree.rows.length > 0 &&
|
|
1347
|
+
Object.keys(self.cellTree.rows[cell.rowIndex]).length > 1
|
|
1348
|
+
)
|
|
1349
|
+
treeCellPadding = drawCellTreeIcon(
|
|
1350
|
+
cell,
|
|
1351
|
+
self.cellTree.rows[cell.rowIndex],
|
|
1352
|
+
true,
|
|
1353
|
+
);
|
|
1354
|
+
|
|
1355
|
+
if (
|
|
1356
|
+
!cell.isRowHeader &&
|
|
1357
|
+
cell.rowIndex > 0 &&
|
|
1358
|
+
self.cellTree.columns[cell.rowIndex - 1] &&
|
|
1359
|
+
self.cellTree.columns[cell.rowIndex - 1][cell.columnIndex]
|
|
1360
|
+
.icon
|
|
1361
|
+
) {
|
|
1362
|
+
for (let r = cell.rowIndex - 1; r >= 0; r--) {
|
|
1363
|
+
if (!self.cellTree.columns[r]) break;
|
|
1364
|
+
if (!self.cellTree.columns[r][cell.columnIndex].icon) break;
|
|
1365
|
+
if (!self.cellTree.columns[r][cell.columnIndex].expand) {
|
|
1366
|
+
isDrawText = false;
|
|
1367
|
+
break;
|
|
1368
|
+
}
|
|
1369
|
+
}
|
|
1370
|
+
}
|
|
1371
|
+
if (
|
|
1372
|
+
isDrawText &&
|
|
1373
|
+
!cell.isRowHeader &&
|
|
1374
|
+
self.cellTree.columns[cell.rowIndex] &&
|
|
1375
|
+
self.cellTree.columns[cell.rowIndex][cell.columnIndex].icon
|
|
1376
|
+
) {
|
|
1377
|
+
if (
|
|
1378
|
+
self.viewData.length > 0 &&
|
|
1379
|
+
self.viewData[cell.rowIndex][cell.columnIndex].length > 0
|
|
1380
|
+
)
|
|
1381
|
+
treeCellPadding = drawCellTreeIcon(
|
|
1382
|
+
cell,
|
|
1383
|
+
self.cellTree.columns[cell.rowIndex][cell.columnIndex],
|
|
1384
|
+
false,
|
|
1385
|
+
);
|
|
1386
|
+
}
|
|
1387
|
+
if (isDrawText) drawText(cell, treeCellPadding);
|
|
1133
1388
|
}
|
|
1134
1389
|
|
|
1135
1390
|
if (wrap && cell.text && cell.text.height > rowHeight) {
|
|
@@ -1171,10 +1426,31 @@ export default function (self) {
|
|
|
1171
1426
|
const previousRowNumber = self.getBoundRowIndexFromViewRowIndex(
|
|
1172
1427
|
rowOrderIndex - 1,
|
|
1173
1428
|
);
|
|
1174
|
-
|
|
1429
|
+
let hasRowGap =
|
|
1175
1430
|
previousRowNumber !== undefined &&
|
|
1176
1431
|
cell.boundRowIndex > 0 &&
|
|
1177
1432
|
cell.boundRowIndex - previousRowNumber > 1;
|
|
1433
|
+
if (hasRowGap && collapsedRowGroups.length > 0) {
|
|
1434
|
+
hasRowGap =
|
|
1435
|
+
collapsedRowGroups.find(
|
|
1436
|
+
(group) =>
|
|
1437
|
+
group.from === previousRowNumber &&
|
|
1438
|
+
group.to === cell.boundRowIndex,
|
|
1439
|
+
) >= 0;
|
|
1440
|
+
}
|
|
1441
|
+
// We don't treat the row index difference from hidden rows as the row gap.
|
|
1442
|
+
if (hasRowGap && self.hiddenRowRanges.length > 0) {
|
|
1443
|
+
for (let i = 0; i < self.hiddenRowRanges.length; i++) {
|
|
1444
|
+
const [beginRowIndex, endRowIndex] = self.hiddenRowRanges[i];
|
|
1445
|
+
if (
|
|
1446
|
+
cell.boundRowIndex === endRowIndex + 1 &&
|
|
1447
|
+
previousRowNumber === beginRowIndex - 1
|
|
1448
|
+
) {
|
|
1449
|
+
hasRowGap = false;
|
|
1450
|
+
break;
|
|
1451
|
+
}
|
|
1452
|
+
}
|
|
1453
|
+
}
|
|
1178
1454
|
|
|
1179
1455
|
if (hasRowGap) {
|
|
1180
1456
|
const barHeight = self.style.rowHeaderCellRowNumberGapHeight;
|
|
@@ -1190,9 +1466,141 @@ export default function (self) {
|
|
|
1190
1466
|
}
|
|
1191
1467
|
}
|
|
1192
1468
|
|
|
1469
|
+
//#region draw unhide indicator for column headers
|
|
1470
|
+
if (isColumnHeader && self.attributes.showUnhideColumnsIndicator) {
|
|
1471
|
+
const hovered = self.hovers.unhideIndicator;
|
|
1472
|
+
const size = self.style.unhideIndicatorSize;
|
|
1473
|
+
const cellX = x;
|
|
1474
|
+
const topY = cell.y + Math.max(0.5 * (cell.height - size), 0);
|
|
1475
|
+
|
|
1476
|
+
const isActive = (orderIndex) =>
|
|
1477
|
+
hovered &&
|
|
1478
|
+
(hovered.dir === 'l' || hovered.dir === 'r') &&
|
|
1479
|
+
orderIndex >= hovered.orderIndex0 &&
|
|
1480
|
+
orderIndex <= hovered.orderIndex1;
|
|
1481
|
+
const isHiddenColumn = (columnIndex) =>
|
|
1482
|
+
columnIndex >= 0 &&
|
|
1483
|
+
schema[columnIndex] &&
|
|
1484
|
+
schema[columnIndex].hidden;
|
|
1485
|
+
|
|
1486
|
+
let orderIndex0, orderIndex1;
|
|
1487
|
+
const drawIndicator = (leftX, dir, active) => {
|
|
1488
|
+
self.visibleUnhideIndicators.push({
|
|
1489
|
+
x: leftX - 1,
|
|
1490
|
+
y: topY - 1,
|
|
1491
|
+
x2: leftX + size + 2,
|
|
1492
|
+
y2: topY + size + 2,
|
|
1493
|
+
orderIndex0,
|
|
1494
|
+
orderIndex1,
|
|
1495
|
+
dir,
|
|
1496
|
+
});
|
|
1497
|
+
if (!active) {
|
|
1498
|
+
const line = cell.text && cell.text.lines && cell.text.lines[0];
|
|
1499
|
+
if (line) {
|
|
1500
|
+
const iconsWidth = orderByArrowSize + treeArrowSize;
|
|
1501
|
+
const lineX0 = iconsWidth > 0 ? iconsWidth : line.x;
|
|
1502
|
+
const lineX1 = line.x + line.width;
|
|
1503
|
+
if (leftX + size >= lineX0 && leftX <= lineX1) return;
|
|
1504
|
+
}
|
|
1505
|
+
}
|
|
1506
|
+
drawUnhideIndicator(leftX, topY, size, dir, active);
|
|
1507
|
+
}; // end of drawIndicator
|
|
1508
|
+
|
|
1509
|
+
let orderIndexPtr = columnOrderIndex - 1;
|
|
1510
|
+
const prevColumnIndex = self.orders.columns[orderIndexPtr];
|
|
1511
|
+
if (isHiddenColumn(prevColumnIndex)) {
|
|
1512
|
+
const active = isActive(prevColumnIndex);
|
|
1513
|
+
orderIndex0 = orderIndexPtr;
|
|
1514
|
+
orderIndex1 = orderIndexPtr;
|
|
1515
|
+
while (--orderIndexPtr >= 0) {
|
|
1516
|
+
if (isHiddenColumn(self.orders.columns[orderIndexPtr]))
|
|
1517
|
+
orderIndex0 = orderIndexPtr;
|
|
1518
|
+
else break;
|
|
1519
|
+
}
|
|
1520
|
+
drawIndicator(cellX, 'r', active);
|
|
1521
|
+
}
|
|
1522
|
+
|
|
1523
|
+
orderIndexPtr = columnOrderIndex + 1;
|
|
1524
|
+
const nextColumnIndex = self.orders.columns[orderIndexPtr];
|
|
1525
|
+
if (isHiddenColumn(nextColumnIndex)) {
|
|
1526
|
+
const active = isActive(nextColumnIndex);
|
|
1527
|
+
orderIndex0 = orderIndexPtr;
|
|
1528
|
+
orderIndex1 = orderIndexPtr;
|
|
1529
|
+
while (++orderIndexPtr < self.orders.columns.length) {
|
|
1530
|
+
if (isHiddenColumn(self.orders.columns[orderIndexPtr]))
|
|
1531
|
+
orderIndex1 = orderIndexPtr;
|
|
1532
|
+
else break;
|
|
1533
|
+
}
|
|
1534
|
+
const indicatorX = x + cell.width - size;
|
|
1535
|
+
drawIndicator(indicatorX, 'l', active);
|
|
1536
|
+
}
|
|
1537
|
+
}
|
|
1538
|
+
//#endregion draw unhide indicator for column headers
|
|
1539
|
+
|
|
1540
|
+
//#region draw unhide indicator for row headers
|
|
1541
|
+
if (
|
|
1542
|
+
isRowHeader &&
|
|
1543
|
+
self.attributes.showUnhideRowsIndicator &&
|
|
1544
|
+
self.hiddenRowRanges.length > 0
|
|
1545
|
+
) {
|
|
1546
|
+
// Leo's comment:
|
|
1547
|
+
// from the first row to the last row, `rowIndex` is from 0 to the count of rows
|
|
1548
|
+
// but `boundRowIndex` can be disordered if there are any ordered columns or filtered columns
|
|
1549
|
+
// Like this statement:
|
|
1550
|
+
// console.log(rowIndex, cell.boundRowIndex, cell.formattedValue);
|
|
1551
|
+
// can output the result like this:
|
|
1552
|
+
// 0 1 '2'
|
|
1553
|
+
// 1 3 '4'
|
|
1554
|
+
const hovered = self.hovers.unhideIndicator;
|
|
1555
|
+
const size = self.style.unhideIndicatorSize;
|
|
1556
|
+
const leftX = cell.x + cell.width - size - 2;
|
|
1557
|
+
const cellY = y;
|
|
1558
|
+
const topIndicators = {};
|
|
1559
|
+
const bottomIndicators = {};
|
|
1560
|
+
self.hiddenRowRanges.forEach((it) => {
|
|
1561
|
+
topIndicators[it[0] - 1] = it;
|
|
1562
|
+
bottomIndicators[it[1] + 1] = it;
|
|
1563
|
+
});
|
|
1564
|
+
|
|
1565
|
+
const rowIndex = cell.rowIndex + currentRowIndexOffset;
|
|
1566
|
+
const isActive = () =>
|
|
1567
|
+
hovered &&
|
|
1568
|
+
(hovered.dir === 't' || hovered.dir === 'b') &&
|
|
1569
|
+
rowIndex >= hovered.orderIndex0 - 1 &&
|
|
1570
|
+
rowIndex <= hovered.orderIndex1 + 1;
|
|
1571
|
+
|
|
1572
|
+
let orderIndex0, orderIndex1;
|
|
1573
|
+
const drawIndicator = (topY, dir, active) => {
|
|
1574
|
+
self.visibleUnhideIndicators.push({
|
|
1575
|
+
x: leftX - 1,
|
|
1576
|
+
y: topY - 1,
|
|
1577
|
+
x2: leftX + size + 2,
|
|
1578
|
+
y2: topY + size + 2,
|
|
1579
|
+
orderIndex0,
|
|
1580
|
+
orderIndex1,
|
|
1581
|
+
dir,
|
|
1582
|
+
});
|
|
1583
|
+
drawUnhideIndicator(leftX, topY, size, dir, active);
|
|
1584
|
+
}; // end of drawIndicator
|
|
1585
|
+
|
|
1586
|
+
let matched = topIndicators[rowIndex];
|
|
1587
|
+
if (matched) {
|
|
1588
|
+
const indicatorY = cellY + cell.height - size;
|
|
1589
|
+
[orderIndex0, orderIndex1] = matched;
|
|
1590
|
+
drawIndicator(indicatorY, 't', isActive());
|
|
1591
|
+
}
|
|
1592
|
+
matched = bottomIndicators[rowIndex];
|
|
1593
|
+
if (matched) {
|
|
1594
|
+
const indicatorY = cellY;
|
|
1595
|
+
[orderIndex0, orderIndex1] = matched;
|
|
1596
|
+
drawIndicator(indicatorY, 'b', isActive());
|
|
1597
|
+
}
|
|
1598
|
+
}
|
|
1599
|
+
//#endregion draw unhide indicator for row headers
|
|
1600
|
+
|
|
1193
1601
|
x += cell.width + (bc ? 0 : self.style.cellBorderWidth);
|
|
1194
1602
|
return cell.width;
|
|
1195
|
-
};
|
|
1603
|
+
}; // end of drawEach
|
|
1196
1604
|
}
|
|
1197
1605
|
function drawFilterButton(cell, ev) {
|
|
1198
1606
|
var posX = cell.x + cell.width - self.style.filterButtonWidth - 1;
|
|
@@ -1230,16 +1638,20 @@ export default function (self) {
|
|
|
1230
1638
|
// as it is in the unfiltered data, instead of simply the viewed
|
|
1231
1639
|
// row index + 1. If rowIndex > viewData.length, it's a new row
|
|
1232
1640
|
// added to the end, and we want to render that new row's number
|
|
1233
|
-
|
|
1234
|
-
|
|
1235
|
-
|
|
1236
|
-
|
|
1641
|
+
let filteredRowNumber;
|
|
1642
|
+
if (self.viewData && rowIndex < self.viewData.length)
|
|
1643
|
+
filteredRowNumber =
|
|
1644
|
+
self.getBoundRowIndexFromViewRowIndex(rowIndex) + 1;
|
|
1645
|
+
else
|
|
1646
|
+
filteredRowNumber = self.originalData
|
|
1237
1647
|
? self.originalData.length + 1
|
|
1238
1648
|
: rowOrderIndex + 1;
|
|
1239
1649
|
|
|
1240
|
-
|
|
1241
|
-
|
|
1242
|
-
|
|
1650
|
+
let rowHeaderValue =
|
|
1651
|
+
self.hasActiveFilters() || self.hasCollapsedRowGroup()
|
|
1652
|
+
? filteredRowNumber
|
|
1653
|
+
: rowIndex + 1;
|
|
1654
|
+
rowHeaderValue += currentRowIndexOffset;
|
|
1243
1655
|
|
|
1244
1656
|
const rowHeaderCell = { rowHeaderCell: rowHeaderValue };
|
|
1245
1657
|
const headerDescription = {
|
|
@@ -1256,6 +1668,14 @@ export default function (self) {
|
|
|
1256
1668
|
-1,
|
|
1257
1669
|
-1,
|
|
1258
1670
|
);
|
|
1671
|
+
|
|
1672
|
+
if (
|
|
1673
|
+
rowIndexOffsetByHiddenRows[0] &&
|
|
1674
|
+
rowHeaderValue >= rowIndexOffsetByHiddenRows[0].from
|
|
1675
|
+
) {
|
|
1676
|
+
const { plus } = rowIndexOffsetByHiddenRows.shift();
|
|
1677
|
+
currentRowIndexOffset += plus;
|
|
1678
|
+
}
|
|
1259
1679
|
}
|
|
1260
1680
|
}
|
|
1261
1681
|
function drawHeaders() {
|
|
@@ -1271,7 +1691,7 @@ export default function (self) {
|
|
|
1271
1691
|
for (o = start; o < end; o += 1) {
|
|
1272
1692
|
i = self.orders.columns[o];
|
|
1273
1693
|
header = schema[i];
|
|
1274
|
-
if (!header.hidden) {
|
|
1694
|
+
if (!header.hidden && !isColumnCollapsedByGroup(o)) {
|
|
1275
1695
|
d = {
|
|
1276
1696
|
title: header.title,
|
|
1277
1697
|
name: header.name,
|
|
@@ -1383,12 +1803,14 @@ export default function (self) {
|
|
|
1383
1803
|
columnOrderIndex < g;
|
|
1384
1804
|
columnOrderIndex += 1
|
|
1385
1805
|
) {
|
|
1386
|
-
|
|
1387
|
-
|
|
1388
|
-
|
|
1389
|
-
|
|
1390
|
-
|
|
1391
|
-
|
|
1806
|
+
if (!isColumnCollapsedByGroup(columnOrderIndex)) {
|
|
1807
|
+
headerIndex = self.orders.columns[columnOrderIndex];
|
|
1808
|
+
x += drawCell(rowData, rowOrderIndex, rowIndex)(
|
|
1809
|
+
schema[headerIndex],
|
|
1810
|
+
headerIndex,
|
|
1811
|
+
columnOrderIndex,
|
|
1812
|
+
);
|
|
1813
|
+
}
|
|
1392
1814
|
if (x > self.width) {
|
|
1393
1815
|
self.scrollIndexRight = columnOrderIndex;
|
|
1394
1816
|
self.scrollPixelRight = x;
|
|
@@ -1405,12 +1827,14 @@ export default function (self) {
|
|
|
1405
1827
|
columnOrderIndex < self.frozenColumn;
|
|
1406
1828
|
columnOrderIndex += 1
|
|
1407
1829
|
) {
|
|
1408
|
-
|
|
1409
|
-
|
|
1410
|
-
|
|
1411
|
-
|
|
1412
|
-
|
|
1413
|
-
|
|
1830
|
+
if (!isColumnCollapsedByGroup(columnOrderIndex)) {
|
|
1831
|
+
headerIndex = self.orders.columns[columnOrderIndex];
|
|
1832
|
+
x += drawCell(rowData, rowOrderIndex, rowIndex)(
|
|
1833
|
+
schema[headerIndex],
|
|
1834
|
+
headerIndex,
|
|
1835
|
+
columnOrderIndex,
|
|
1836
|
+
);
|
|
1837
|
+
}
|
|
1414
1838
|
if (x > self.width) {
|
|
1415
1839
|
break;
|
|
1416
1840
|
}
|
|
@@ -1439,7 +1863,18 @@ export default function (self) {
|
|
|
1439
1863
|
scrollTop: self.scrollBox.scrollTop,
|
|
1440
1864
|
scrollLeft: self.scrollBox.scrollLeft,
|
|
1441
1865
|
rowIndex: rowOrderIndex,
|
|
1866
|
+
columnGroupsAreaHeight: columnGroupsAreaHeight,
|
|
1867
|
+
rowGroupsAreaWidth: rowGroupsAreaWidth,
|
|
1442
1868
|
};
|
|
1869
|
+
if (self.intf.parentNode) {
|
|
1870
|
+
const {
|
|
1871
|
+
columnGroupsAreaHeight,
|
|
1872
|
+
rowGroupsAreaWidth,
|
|
1873
|
+
} = self.intf.parentNode;
|
|
1874
|
+
treeGrid.parentNode.columnGroupsAreaHeight +=
|
|
1875
|
+
columnGroupsAreaHeight || 0;
|
|
1876
|
+
treeGrid.parentNode.rowGroupsAreaWidth += rowGroupsAreaWidth || 0;
|
|
1877
|
+
}
|
|
1443
1878
|
self.visibleCells.unshift({
|
|
1444
1879
|
rowIndex: rowOrderIndex,
|
|
1445
1880
|
columnIndex: 0,
|
|
@@ -1464,12 +1899,21 @@ export default function (self) {
|
|
|
1464
1899
|
self.visibleRows = [];
|
|
1465
1900
|
schema = self.getSchema();
|
|
1466
1901
|
self.visibleCells = [];
|
|
1902
|
+
self.visibleGroups = [];
|
|
1903
|
+
self.visibleUnhideIndicators = [];
|
|
1467
1904
|
self.canvasOffsetTop = self.isChildGrid ? self.parentNode.offsetTop : 0.5;
|
|
1468
1905
|
self.canvasOffsetLeft = self.isChildGrid
|
|
1469
1906
|
? self.parentNode.offsetLeft
|
|
1470
1907
|
: -0.5;
|
|
1471
1908
|
h = self.height;
|
|
1472
1909
|
w = self.width;
|
|
1910
|
+
|
|
1911
|
+
// patch for first row being hidden
|
|
1912
|
+
const firstRowIndexOffset = rowIndexOffsetByHiddenRows[0];
|
|
1913
|
+
if (firstRowIndexOffset && firstRowIndexOffset.from === 0) {
|
|
1914
|
+
currentRowIndexOffset = firstRowIndexOffset.plus;
|
|
1915
|
+
rowIndexOffsetByHiddenRows.shift();
|
|
1916
|
+
}
|
|
1473
1917
|
}
|
|
1474
1918
|
function drawBackground() {
|
|
1475
1919
|
radiusRect(0, 0, w, h, 0);
|
|
@@ -1477,6 +1921,379 @@ export default function (self) {
|
|
|
1477
1921
|
self.ctx.fillStyle = self.style.gridBackgroundColor;
|
|
1478
1922
|
fillRect(0, 0, w, h);
|
|
1479
1923
|
}
|
|
1924
|
+
function initGroupArea() {
|
|
1925
|
+
self.ctx.translate(rowGroupsAreaWidth, columnGroupsAreaHeight);
|
|
1926
|
+
}
|
|
1927
|
+
function drawGroupArea() {
|
|
1928
|
+
const mx = rowGroupsAreaWidth;
|
|
1929
|
+
const my = columnGroupsAreaHeight;
|
|
1930
|
+
const frozenColumnsWidth = getFrozenColumnsWidth();
|
|
1931
|
+
const frozenRowsHeight =
|
|
1932
|
+
rowGroupsFrozenInfo.y + rowGroupsFrozenInfo.h - columnHeaderCellHeight;
|
|
1933
|
+
const onTheLeft = self.attributes.columnGroupIndicatorPosition === 'left';
|
|
1934
|
+
const onTheTop = self.attributes.rowGroupIndicatorPosition === 'top';
|
|
1935
|
+
|
|
1936
|
+
/** @type {CanvasRenderingContext2D} */
|
|
1937
|
+
const ctx = self.ctx;
|
|
1938
|
+
ctx.save();
|
|
1939
|
+
ctx.fillStyle = self.style.groupingAreaBackgroundColor;
|
|
1940
|
+
fillRect(0, -my, w, my);
|
|
1941
|
+
fillRect(-mx, -my, mx, h);
|
|
1942
|
+
ctx.restore();
|
|
1943
|
+
|
|
1944
|
+
//#region Columns Grouping
|
|
1945
|
+
/** it extends `self.groupedRows` */
|
|
1946
|
+
const groupedColumns = [];
|
|
1947
|
+
for (let row = 0; row < self.groupedColumns.length; row++) {
|
|
1948
|
+
const groups = self.groupedColumns[row];
|
|
1949
|
+
for (let j = 0; j < groups.length; j++) {
|
|
1950
|
+
groupedColumns.push(Object.assign({ row }, groups[j]));
|
|
1951
|
+
}
|
|
1952
|
+
}
|
|
1953
|
+
if (groupedColumns.length > 0) {
|
|
1954
|
+
const rowHeight = self.style.columnGroupRowHeight;
|
|
1955
|
+
const toggleHandleSize = rowHeight * 0.5;
|
|
1956
|
+
const toggleHandlePadding = (rowHeight - toggleHandleSize) * 0.5;
|
|
1957
|
+
|
|
1958
|
+
for (let i = 0; i < groupedColumns.length; i++) {
|
|
1959
|
+
const group = groupedColumns[i];
|
|
1960
|
+
const { row, collapsed } = group;
|
|
1961
|
+
|
|
1962
|
+
const topY = -my + row * rowHeight;
|
|
1963
|
+
const centerY = topY + rowHeight * 0.5;
|
|
1964
|
+
const bottomY = topY + rowHeight - toggleHandlePadding;
|
|
1965
|
+
const leftmostX =
|
|
1966
|
+
rowHeaderCellWidth - toggleHandleSize - toggleHandlePadding;
|
|
1967
|
+
|
|
1968
|
+
const drawGroupHandleAtX = (x) =>
|
|
1969
|
+
drawGroupHandle(
|
|
1970
|
+
x,
|
|
1971
|
+
topY + toggleHandlePadding,
|
|
1972
|
+
toggleHandleSize,
|
|
1973
|
+
group.collapsed,
|
|
1974
|
+
);
|
|
1975
|
+
const pushToVisibleGroups = (leftX, rightX) =>
|
|
1976
|
+
self.visibleGroups.push({
|
|
1977
|
+
type: 'c',
|
|
1978
|
+
collapsed,
|
|
1979
|
+
from: group.from,
|
|
1980
|
+
to: group.to,
|
|
1981
|
+
row,
|
|
1982
|
+
x: leftX + mx,
|
|
1983
|
+
y: topY + my,
|
|
1984
|
+
x2: rightX + mx,
|
|
1985
|
+
y2: bottomY + my,
|
|
1986
|
+
});
|
|
1987
|
+
|
|
1988
|
+
//#region check the relationship between this group and frozen columns
|
|
1989
|
+
const crossTheFrozen =
|
|
1990
|
+
group.from < self.frozenColumn && group.to >= self.frozenColumn;
|
|
1991
|
+
const notInFrozen = group.from >= self.frozenColumn;
|
|
1992
|
+
//#endregion
|
|
1993
|
+
|
|
1994
|
+
if (collapsed) {
|
|
1995
|
+
let leftX = leftmostX + toggleHandleSize;
|
|
1996
|
+
// This group is not sticking on the first column
|
|
1997
|
+
if (group.from > 0) {
|
|
1998
|
+
let colIndex = group.to + 1;
|
|
1999
|
+
let col = columnGroupsRectInfo[colIndex];
|
|
2000
|
+
if (!col) {
|
|
2001
|
+
colIndex = group.from - 1;
|
|
2002
|
+
col = columnGroupsRectInfo[colIndex];
|
|
2003
|
+
if (!col) continue; // don't draw this group indicator because it is invisible
|
|
2004
|
+
leftX = col.x + col.w - toggleHandleSize * 0.5;
|
|
2005
|
+
} else {
|
|
2006
|
+
leftX = col.x + toggleHandlePadding;
|
|
2007
|
+
}
|
|
2008
|
+
if (colIndex >= self.frozenColumn) {
|
|
2009
|
+
const compare =
|
|
2010
|
+
frozenColumnsWidth + rowHeaderCellWidth - toggleHandlePadding;
|
|
2011
|
+
// don't draw this group indicator because it is hidden by frozen columns
|
|
2012
|
+
if (leftX < compare) continue;
|
|
2013
|
+
}
|
|
2014
|
+
}
|
|
2015
|
+
const rightX = leftX + toggleHandleSize;
|
|
2016
|
+
ctx.save();
|
|
2017
|
+
ctx.strokeStyle = self.style.groupIndicatorColor;
|
|
2018
|
+
ctx.fillStyle = self.style.groupIndicatorBackgroundColor;
|
|
2019
|
+
drawGroupHandleAtX(leftX);
|
|
2020
|
+
ctx.restore();
|
|
2021
|
+
pushToVisibleGroups(leftX, rightX);
|
|
2022
|
+
} // end of collapsed group
|
|
2023
|
+
|
|
2024
|
+
/** @type {number} pointer for loop */
|
|
2025
|
+
let ptr;
|
|
2026
|
+
let left = columnGroupsRectInfo[group.from];
|
|
2027
|
+
let right = columnGroupsRectInfo[group.to];
|
|
2028
|
+
|
|
2029
|
+
let containsBegining = true;
|
|
2030
|
+
let containsEnd = true;
|
|
2031
|
+
ptr = group.from;
|
|
2032
|
+
while (!left && ptr < group.to) {
|
|
2033
|
+
left = columnGroupsRectInfo[++ptr];
|
|
2034
|
+
containsBegining = false;
|
|
2035
|
+
}
|
|
2036
|
+
ptr = group.to;
|
|
2037
|
+
while (!right && ptr > group.from) {
|
|
2038
|
+
right = columnGroupsRectInfo[--ptr];
|
|
2039
|
+
containsEnd = false;
|
|
2040
|
+
}
|
|
2041
|
+
if (!left || !right) continue;
|
|
2042
|
+
let rightX = right.x + right.w;
|
|
2043
|
+
let leftX = left.x;
|
|
2044
|
+
if (crossTheFrozen) {
|
|
2045
|
+
const rightCompare = columnGroupsRectInfo[self.frozenColumn - 1];
|
|
2046
|
+
if (rightCompare) {
|
|
2047
|
+
let compareX = rightCompare.x + rightCompare.w;
|
|
2048
|
+
if (!onTheLeft) compareX += toggleHandleSize;
|
|
2049
|
+
if (compareX >= rightX) {
|
|
2050
|
+
right = rightCompare;
|
|
2051
|
+
rightX = right.x + right.w;
|
|
2052
|
+
containsEnd = false;
|
|
2053
|
+
}
|
|
2054
|
+
}
|
|
2055
|
+
}
|
|
2056
|
+
|
|
2057
|
+
let minLeftX =
|
|
2058
|
+
rowHeaderCellWidth + (notInFrozen ? frozenColumnsWidth : 0);
|
|
2059
|
+
if (rightX + (onTheLeft ? 0 : toggleHandleSize) < minLeftX) continue;
|
|
2060
|
+
|
|
2061
|
+
rightX -= toggleHandlePadding;
|
|
2062
|
+
leftX += toggleHandlePadding;
|
|
2063
|
+
|
|
2064
|
+
ctx.save();
|
|
2065
|
+
ctx.strokeStyle = self.style.groupIndicatorColor;
|
|
2066
|
+
ctx.fillStyle = self.style.groupIndicatorBackgroundColor;
|
|
2067
|
+
const lineCoords = [];
|
|
2068
|
+
|
|
2069
|
+
if (onTheLeft) {
|
|
2070
|
+
// avoid lines from two groups be overlapping
|
|
2071
|
+
minLeftX += toggleHandlePadding * 2;
|
|
2072
|
+
if (leftX < minLeftX) leftX = minLeftX;
|
|
2073
|
+
if (group.from === 0) leftX -= toggleHandlePadding * 2;
|
|
2074
|
+
if (rightX >= leftX) {
|
|
2075
|
+
lineCoords.push(leftX, centerY, rightX, centerY);
|
|
2076
|
+
if (containsEnd) lineCoords.push(rightX, bottomY);
|
|
2077
|
+
} else {
|
|
2078
|
+
rightX = leftX;
|
|
2079
|
+
}
|
|
2080
|
+
|
|
2081
|
+
leftX -= toggleHandleSize;
|
|
2082
|
+
drawGroupHandleAtX(leftX);
|
|
2083
|
+
// add more clickable area into `visibleGroups`
|
|
2084
|
+
rightX += toggleHandlePadding - 1;
|
|
2085
|
+
} else {
|
|
2086
|
+
// handle on the right
|
|
2087
|
+
if (leftX < minLeftX) leftX = minLeftX;
|
|
2088
|
+
if (group.from === 0) leftX -= toggleHandlePadding * 2;
|
|
2089
|
+
if (containsEnd) {
|
|
2090
|
+
if (group.to === self.frozenColumn - 1) {
|
|
2091
|
+
rightX -= toggleHandleSize;
|
|
2092
|
+
} else {
|
|
2093
|
+
rightX += toggleHandlePadding * 2;
|
|
2094
|
+
}
|
|
2095
|
+
drawGroupHandleAtX(rightX);
|
|
2096
|
+
}
|
|
2097
|
+
if (leftX > rightX) {
|
|
2098
|
+
leftX = rightX;
|
|
2099
|
+
} else {
|
|
2100
|
+
if (group.from === 0) {
|
|
2101
|
+
containsBegining = true;
|
|
2102
|
+
leftX = leftmostX + toggleHandleSize;
|
|
2103
|
+
}
|
|
2104
|
+
if (containsBegining) lineCoords.push(leftX, bottomY);
|
|
2105
|
+
lineCoords.push(leftX, centerY, rightX, centerY);
|
|
2106
|
+
}
|
|
2107
|
+
|
|
2108
|
+
// add more clickable area into `visibleGroups`
|
|
2109
|
+
leftX -= toggleHandlePadding + 1;
|
|
2110
|
+
if (containsEnd) rightX += toggleHandleSize;
|
|
2111
|
+
}
|
|
2112
|
+
drawLines(lineCoords);
|
|
2113
|
+
ctx.restore();
|
|
2114
|
+
pushToVisibleGroups(leftX, rightX);
|
|
2115
|
+
}
|
|
2116
|
+
}
|
|
2117
|
+
//#endregion Columns Grouping
|
|
2118
|
+
|
|
2119
|
+
//#region Rows Grouping
|
|
2120
|
+
/** it extends `self.groupedRows` */
|
|
2121
|
+
const groupedRows = [];
|
|
2122
|
+
for (let col = 0; col < self.groupedRows.length; col++) {
|
|
2123
|
+
const groups = self.groupedRows[col];
|
|
2124
|
+
for (let j = 0; j < groups.length; j++) {
|
|
2125
|
+
groupedRows.push(Object.assign({ col }, groups[j]));
|
|
2126
|
+
}
|
|
2127
|
+
}
|
|
2128
|
+
if (groupedRows.length > 0) {
|
|
2129
|
+
const colWidth = self.style.rowGroupColumnWidth;
|
|
2130
|
+
const toggleHandleSize = colWidth * 0.5;
|
|
2131
|
+
const toggleHandlePadding = (colWidth - toggleHandleSize) * 0.5;
|
|
2132
|
+
|
|
2133
|
+
for (let i = 0; i < groupedRows.length; i++) {
|
|
2134
|
+
const group = groupedRows[i];
|
|
2135
|
+
const { col, collapsed } = group;
|
|
2136
|
+
|
|
2137
|
+
const leftX = -mx + col * colWidth;
|
|
2138
|
+
const centerX = leftX + colWidth * 0.5;
|
|
2139
|
+
const rightX = leftX + colWidth - toggleHandlePadding;
|
|
2140
|
+
const topmostY =
|
|
2141
|
+
columnHeaderCellHeight - toggleHandleSize - toggleHandlePadding;
|
|
2142
|
+
|
|
2143
|
+
const drawGroupHandleAtY = (y) =>
|
|
2144
|
+
drawGroupHandle(
|
|
2145
|
+
leftX + toggleHandlePadding,
|
|
2146
|
+
y,
|
|
2147
|
+
toggleHandleSize,
|
|
2148
|
+
group.collapsed,
|
|
2149
|
+
);
|
|
2150
|
+
const pushToVisibleGroups = (topY, bottomY) =>
|
|
2151
|
+
self.visibleGroups.push({
|
|
2152
|
+
type: 'r',
|
|
2153
|
+
collapsed,
|
|
2154
|
+
from: group.from,
|
|
2155
|
+
to: group.to,
|
|
2156
|
+
col,
|
|
2157
|
+
x: leftX + mx,
|
|
2158
|
+
y: topY + my,
|
|
2159
|
+
x2: rightX + mx,
|
|
2160
|
+
y2: bottomY + my,
|
|
2161
|
+
});
|
|
2162
|
+
|
|
2163
|
+
//#region check the relationship between this group and frozen columns
|
|
2164
|
+
const crossTheFrozen =
|
|
2165
|
+
group.from < self.frozenRow && group.to >= self.frozenRow;
|
|
2166
|
+
const notInFrozen = group.from >= self.frozenRow;
|
|
2167
|
+
//#endregion
|
|
2168
|
+
|
|
2169
|
+
if (collapsed) {
|
|
2170
|
+
let topY = topmostY + toggleHandleSize;
|
|
2171
|
+
// This group is not sticking on the first column
|
|
2172
|
+
if (group.from > 0) {
|
|
2173
|
+
let rowIndex = group.to + 1;
|
|
2174
|
+
let row = rowGroupsRectInfo[rowIndex];
|
|
2175
|
+
if (!row) {
|
|
2176
|
+
rowIndex = group.from - 1;
|
|
2177
|
+
row = rowGroupsRectInfo[rowIndex];
|
|
2178
|
+
if (!row) continue; // don't draw this group indicator because it is invisible
|
|
2179
|
+
topY = row.y + row.h - toggleHandleSize * 0.5;
|
|
2180
|
+
} else {
|
|
2181
|
+
topY = row.y;
|
|
2182
|
+
}
|
|
2183
|
+
if (rowIndex >= self.frozenRow) {
|
|
2184
|
+
const compare =
|
|
2185
|
+
frozenRowsHeight +
|
|
2186
|
+
columnHeaderCellHeight -
|
|
2187
|
+
toggleHandlePadding;
|
|
2188
|
+
// don't draw this group indicator because it is hidden by frozen columns
|
|
2189
|
+
if (topY < compare) continue;
|
|
2190
|
+
}
|
|
2191
|
+
}
|
|
2192
|
+
const bottomY = topY + toggleHandleSize;
|
|
2193
|
+
ctx.save();
|
|
2194
|
+
ctx.strokeStyle = self.style.groupIndicatorColor;
|
|
2195
|
+
ctx.fillStyle = self.style.groupIndicatorBackgroundColor;
|
|
2196
|
+
drawGroupHandleAtY(topY);
|
|
2197
|
+
ctx.restore();
|
|
2198
|
+
pushToVisibleGroups(topY, bottomY);
|
|
2199
|
+
} // end of collapsed group
|
|
2200
|
+
|
|
2201
|
+
/** @type {number} pointer for loop */
|
|
2202
|
+
let ptr;
|
|
2203
|
+
let top = rowGroupsRectInfo[group.from];
|
|
2204
|
+
let bottom = rowGroupsRectInfo[group.to];
|
|
2205
|
+
|
|
2206
|
+
let containsBegining = true;
|
|
2207
|
+
let containsEnd = true;
|
|
2208
|
+
ptr = group.from;
|
|
2209
|
+
while (!top && ptr < group.to) {
|
|
2210
|
+
top = rowGroupsRectInfo[++ptr];
|
|
2211
|
+
containsBegining = false;
|
|
2212
|
+
}
|
|
2213
|
+
ptr = group.to;
|
|
2214
|
+
while (!bottom && ptr > group.from) {
|
|
2215
|
+
bottom = rowGroupsRectInfo[--ptr];
|
|
2216
|
+
containsEnd = false;
|
|
2217
|
+
}
|
|
2218
|
+
if (!top || !bottom) continue;
|
|
2219
|
+
let bottomY = bottom.y + bottom.h;
|
|
2220
|
+
let topY = top.y;
|
|
2221
|
+
if (crossTheFrozen) {
|
|
2222
|
+
const bottomCompare = rowGroupsRectInfo[self.frozenRow - 1];
|
|
2223
|
+
if (bottomCompare) {
|
|
2224
|
+
let compareY = bottomCompare.y + bottomCompare.h;
|
|
2225
|
+
if (!onTheTop) compareY += toggleHandleSize;
|
|
2226
|
+
if (compareY >= bottomY) {
|
|
2227
|
+
bottom = bottomCompare;
|
|
2228
|
+
bottomY = bottom.y + bottom.h;
|
|
2229
|
+
containsEnd = false;
|
|
2230
|
+
}
|
|
2231
|
+
}
|
|
2232
|
+
}
|
|
2233
|
+
|
|
2234
|
+
let minTopY =
|
|
2235
|
+
columnHeaderCellHeight + (notInFrozen ? frozenRowsHeight : 0);
|
|
2236
|
+
if (bottomY + (onTheTop ? 0 : toggleHandleSize) < minTopY) continue;
|
|
2237
|
+
|
|
2238
|
+
bottomY -= toggleHandlePadding;
|
|
2239
|
+
topY += toggleHandlePadding;
|
|
2240
|
+
|
|
2241
|
+
ctx.save();
|
|
2242
|
+
ctx.strokeStyle = self.style.groupIndicatorColor;
|
|
2243
|
+
ctx.fillStyle = self.style.groupIndicatorBackgroundColor;
|
|
2244
|
+
const lineCoords = [];
|
|
2245
|
+
|
|
2246
|
+
if (onTheTop) {
|
|
2247
|
+
// avoid lines from two groups be overlapping
|
|
2248
|
+
minTopY += toggleHandlePadding * 2;
|
|
2249
|
+
if (topY < minTopY) topY = minTopY;
|
|
2250
|
+
if (group.from === 0) topY -= toggleHandlePadding * 2;
|
|
2251
|
+
if (bottomY >= topY) {
|
|
2252
|
+
lineCoords.push(centerX, topY, centerX, bottomY);
|
|
2253
|
+
if (containsEnd) lineCoords.push(rightX, bottomY);
|
|
2254
|
+
} else {
|
|
2255
|
+
bottomY = topY;
|
|
2256
|
+
}
|
|
2257
|
+
|
|
2258
|
+
topY -= toggleHandleSize;
|
|
2259
|
+
drawGroupHandleAtY(topY);
|
|
2260
|
+
|
|
2261
|
+
// add more clickable area into `visibleGroups`
|
|
2262
|
+
bottomY += toggleHandlePadding - 1;
|
|
2263
|
+
} else {
|
|
2264
|
+
// handle on the bottom
|
|
2265
|
+
if (topY < minTopY) topY = minTopY;
|
|
2266
|
+
if (group.from === 0) topY -= toggleHandlePadding * 2;
|
|
2267
|
+
if (containsEnd) {
|
|
2268
|
+
if (group.to === self.frozenRow - 1) {
|
|
2269
|
+
bottomY -= toggleHandleSize;
|
|
2270
|
+
} else {
|
|
2271
|
+
// bottomY += toggleHandlePadding * 2;
|
|
2272
|
+
}
|
|
2273
|
+
drawGroupHandleAtY(bottomY);
|
|
2274
|
+
}
|
|
2275
|
+
if (topY > bottomY) {
|
|
2276
|
+
topY = bottomY;
|
|
2277
|
+
} else {
|
|
2278
|
+
if (group.from === 0) {
|
|
2279
|
+
containsBegining = true;
|
|
2280
|
+
topY = topmostY + toggleHandleSize;
|
|
2281
|
+
}
|
|
2282
|
+
if (containsBegining) lineCoords.push(rightX, topY);
|
|
2283
|
+
lineCoords.push(centerX, topY, centerX, bottomY);
|
|
2284
|
+
// add more clickable area into `visibleGroups`
|
|
2285
|
+
topY -= toggleHandlePadding + 1;
|
|
2286
|
+
}
|
|
2287
|
+
// add more clickable area into `visibleGroups`
|
|
2288
|
+
if (containsEnd) bottomY += toggleHandleSize;
|
|
2289
|
+
}
|
|
2290
|
+
drawLines(lineCoords);
|
|
2291
|
+
ctx.restore();
|
|
2292
|
+
pushToVisibleGroups(topY, bottomY);
|
|
2293
|
+
}
|
|
2294
|
+
}
|
|
2295
|
+
//#endregion Rows Grouping
|
|
2296
|
+
}
|
|
1480
2297
|
function drawFrozenRows() {
|
|
1481
2298
|
var rowOrderIndex,
|
|
1482
2299
|
ln = Math.min(viewData.length, self.frozenRow);
|
|
@@ -1535,6 +2352,12 @@ export default function (self) {
|
|
|
1535
2352
|
rowOrderIndex = self.orders.rows[rowIndex];
|
|
1536
2353
|
self.scrollIndexBottom = rowIndex;
|
|
1537
2354
|
self.scrollPixelBottom = y;
|
|
2355
|
+
if (
|
|
2356
|
+
self.cellTree.rows.length > 0 &&
|
|
2357
|
+
Object.keys(self.cellTree.rows[rowOrderIndex]).length > 0 &&
|
|
2358
|
+
self.cellTree.rows[rowOrderIndex].hide
|
|
2359
|
+
)
|
|
2360
|
+
continue;
|
|
1538
2361
|
if (!drawRow(rowOrderIndex, rowIndex)) {
|
|
1539
2362
|
break;
|
|
1540
2363
|
}
|
|
@@ -1550,12 +2373,14 @@ export default function (self) {
|
|
|
1550
2373
|
columnOrderIndex < g;
|
|
1551
2374
|
columnOrderIndex += 1
|
|
1552
2375
|
) {
|
|
1553
|
-
|
|
1554
|
-
|
|
1555
|
-
|
|
1556
|
-
|
|
1557
|
-
|
|
1558
|
-
|
|
2376
|
+
if (!isColumnCollapsedByGroup(columnOrderIndex)) {
|
|
2377
|
+
headerIndex = self.orders.columns[columnOrderIndex];
|
|
2378
|
+
x += drawCell(self.newRow, viewData.length, viewData.length)(
|
|
2379
|
+
schema[headerIndex],
|
|
2380
|
+
headerIndex,
|
|
2381
|
+
columnOrderIndex,
|
|
2382
|
+
);
|
|
2383
|
+
}
|
|
1559
2384
|
if (x > self.width + self.scrollBox.scrollLeft) {
|
|
1560
2385
|
break;
|
|
1561
2386
|
}
|
|
@@ -1767,6 +2592,7 @@ export default function (self) {
|
|
|
1767
2592
|
self.ctx.restore();
|
|
1768
2593
|
}
|
|
1769
2594
|
function drawFrozenMarkers() {
|
|
2595
|
+
if (self.dragStartObject !== undefined) return;
|
|
1770
2596
|
var my = self.lastFrozenRowPixel - self.style.frozenMarkerWidth,
|
|
1771
2597
|
mx = self.lastFrozenColumnPixel - self.style.frozenMarkerBorderWidth,
|
|
1772
2598
|
xHover =
|
|
@@ -1801,14 +2627,16 @@ export default function (self) {
|
|
|
1801
2627
|
: self.style.frozenMarkerBorderColor;
|
|
1802
2628
|
fillRect(0, my, self.width, self.style.frozenMarkerWidth);
|
|
1803
2629
|
strokeRect(0, my, self.width, self.style.frozenMarkerWidth);
|
|
2630
|
+
const height =
|
|
2631
|
+
self.style.frozenMarkerWidth + self.style.frozenMarkerBorderWidth;
|
|
1804
2632
|
self.visibleCells.unshift({
|
|
1805
2633
|
x: 0,
|
|
1806
2634
|
y: my,
|
|
1807
|
-
height
|
|
1808
|
-
self.style.frozenMarkerWidth + self.style.frozenMarkerBorderWidth,
|
|
2635
|
+
height,
|
|
1809
2636
|
width: self.width,
|
|
1810
2637
|
style: 'frozen-row-marker',
|
|
1811
2638
|
});
|
|
2639
|
+
rowGroupsFrozenInfo = { y: my, h: height };
|
|
1812
2640
|
}
|
|
1813
2641
|
if (self.freezeMarkerPosition) {
|
|
1814
2642
|
self.ctx.fillStyle = self.style.frozenMarkerActiveColor;
|
|
@@ -2047,6 +2875,7 @@ export default function (self) {
|
|
|
2047
2875
|
self.ctx.save();
|
|
2048
2876
|
initDraw();
|
|
2049
2877
|
drawBackground();
|
|
2878
|
+
initGroupArea();
|
|
2050
2879
|
drawFrozenRows();
|
|
2051
2880
|
drawRows();
|
|
2052
2881
|
drawActiveCell();
|
|
@@ -2061,6 +2890,7 @@ export default function (self) {
|
|
|
2061
2890
|
if (checkScrollHeight) {
|
|
2062
2891
|
self.resize(true);
|
|
2063
2892
|
}
|
|
2893
|
+
drawGroupArea();
|
|
2064
2894
|
drawDebug();
|
|
2065
2895
|
drawPerfLines();
|
|
2066
2896
|
if (self.dispatchEvent('afterdraw', {})) {
|