canvas-datagrid 0.4.2 → 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/dist/types.d.ts CHANGED
@@ -516,6 +516,18 @@ declare class canvasDatagrid {
516
516
  * @returns header with the selected name, or undefined.
517
517
  */
518
518
  static getHeaderByName(name: string): header;
519
+ /**
520
+ * Hide column/columns
521
+ */
522
+ static hideColumns: any;
523
+ /**
524
+ * Hide rows
525
+ */
526
+ static hideRows: any;
527
+ /**
528
+ * Unhide rows
529
+ */
530
+ static unhideRows: any;
519
531
  /**
520
532
  * Resizes a column to fit the longest value in the column. Call without a value to resize all columns.
521
533
  * Warning, can be slow on very large record sets (1m records ~3-5 seconds on an i7).
@@ -620,6 +632,12 @@ declare class canvasDatagrid {
620
632
  * @returns cell at the selected location.
621
633
  */
622
634
  static getVisibleCellByIndex(x: number, y: number): cell;
635
+ /**
636
+ * Get an unhide indicator at grid pixel coordinate x and y.
637
+ * @param x - Number of pixels from the left.
638
+ * @param y - Number of pixels from the top.
639
+ */
640
+ static getUnhideIndicator(x: number, y: number): void;
623
641
  /**
624
642
  * Get a column group at grid pixel coordinate x and y.
625
643
  * @param x - Number of pixels from the left.
@@ -2186,3 +2204,10 @@ declare namespace canvasDatagrid {
2186
2204
  }
2187
2205
  }
2188
2206
 
2207
+ /**
2208
+ * Merge a new hidden row range into existed ranges array
2209
+ * @param hiddenRowRanges - tuples: Array<[bgeinRowIndex, endRowIndex]>
2210
+ * @param newRange - tuple: [beginRowIndex, endRowIndex]
2211
+ */
2212
+ declare function mergeHiddenRowRanges(hiddenRowRanges: any[], newRange: number[]): boolean;
2213
+
@@ -538,8 +538,114 @@ export default function (self) {
538
538
  });
539
539
  }
540
540
  }
541
+
542
+ /**
543
+ * Return a tuple if the user selected contiguous columns, otherwise `null`.
544
+ * Info: Because the user may reorder the columns,
545
+ * the schemaIndex of the first item may be greater than the schemaIndex of the second item,
546
+ * but the columnIndex of the firs item must less than the columnIndex of the second item.
547
+ * @param {any[]} schema from `self.getSchema()`
548
+ * @returns {any[]} column schemas tuple (each schema has an additional field `schemaIndex`)
549
+ */
550
+ function getSelectedContiguousColumns(ev, schema) {
551
+ const memoKey = '__contiguousColumns';
552
+ if (Array.isArray(ev[memoKey]) || ev[memoKey] === null) return ev[memoKey];
553
+ ev[memoKey] = null;
554
+
555
+ if (!Array.isArray(self.selections) || self.selections.length === 0) return;
556
+ const selection = self.selections[0];
557
+ if (!selection || selection.length === 0) return;
558
+ for (let rowIndex = 0; rowIndex < self.viewData.length; rowIndex++) {
559
+ const row = self.viewData[rowIndex];
560
+ if (!row) continue;
561
+ const compare = self.selections[rowIndex];
562
+ if (!compare) return;
563
+ if (compare.length !== selection.length) return;
564
+ for (let i = 0; i < selection.length; i++)
565
+ if (selection[i] !== compare[i]) return;
566
+ }
567
+ selection.sort((a, b) => a - b);
568
+
569
+ /** @type {number[][]} */
570
+ const ranges = [];
571
+ let begin = selection[0];
572
+ let end = selection[0];
573
+ for (let i = 1; i < selection.length; i++) {
574
+ const orderIndex = selection[i];
575
+ if (orderIndex === end + 1) {
576
+ end = orderIndex;
577
+ continue;
578
+ }
579
+ ranges.push([begin, end]);
580
+ begin = orderIndex;
581
+ end = orderIndex;
582
+ }
583
+ ranges.push([begin, end]);
584
+
585
+ const currentOrderIndex = ev.cell.columnIndex;
586
+ const matchedRange = ranges.find(
587
+ (range) =>
588
+ currentOrderIndex >= range[0] &&
589
+ currentOrderIndex <= range[1] &&
590
+ range[0] !== range[1],
591
+ );
592
+ if (!matchedRange) return;
593
+
594
+ /** @type {number[]} orders[index] => columnIndex */
595
+ const orders = self.orders.columns;
596
+ if (!Array.isArray(orders)) return;
597
+
598
+ const matchedSchema = matchedRange.map((orderIndex) => {
599
+ const schemaIndex = orders[orderIndex];
600
+ const thisSchema = schema[schemaIndex];
601
+ return Object.assign({}, thisSchema, { orderIndex });
602
+ });
603
+ if (matchedSchema.findIndex((it) => !it) >= 0) return;
604
+ return (ev[memoKey] = matchedSchema);
605
+ }
606
+ /**
607
+ * @param {boolean} [allowOnlyOneRow]
608
+ * @returns {number[]} a rowIndex tuple. It can contains one row index or two row indexes.
609
+ */
610
+ function getSelectedContiguousRows(allowOnlyOneRow) {
611
+ let range = [];
612
+ let prev = -2;
613
+ let ok = true;
614
+ self.selections.forEach(function (row, index) {
615
+ if (!ok) return;
616
+ if (prev < -1) {
617
+ prev = index;
618
+ range[0] = index;
619
+ return;
620
+ }
621
+ if (index !== prev + 1 || !row || row.length === 0) {
622
+ ok = false;
623
+ return;
624
+ }
625
+ prev = index;
626
+ range[1] = index;
627
+ });
628
+ if (ok) {
629
+ if (range.length === 1) return allowOnlyOneRow ? range : null;
630
+ return range;
631
+ }
632
+ }
541
633
  function addDefaultContextMenuItem(e) {
542
- var isNormalCell =
634
+ const schema = self.getSchema();
635
+ /**
636
+ * A map between columnIndex and column data
637
+ * @type {Map<string,any>}
638
+ */
639
+ let columns;
640
+ const getColumnsMap = () => {
641
+ if (!columns)
642
+ columns = new Map(schema.map((_col) => [_col.columnIndex, _col]));
643
+ return columns;
644
+ };
645
+ const isSorting =
646
+ self.orderings.columns && self.orderings.columns.length > 0;
647
+
648
+ const isNormalCell =
543
649
  !(
544
650
  e.cell.isBackground ||
545
651
  e.cell.isColumnHeaderCellCap ||
@@ -614,20 +720,30 @@ export default function (self) {
614
720
  const columnOrderIndex = e.cell.columnIndex;
615
721
  const columnIndex = self.orders.columns[columnOrderIndex];
616
722
 
723
+ const contiguousColumns = getSelectedContiguousColumns(e, schema);
724
+ let title = '';
725
+ if (contiguousColumns) {
726
+ title = contiguousColumns
727
+ .map((col) => col.title || col.name)
728
+ .join('-');
729
+ } else {
730
+ const column = schema[columnIndex];
731
+ if (column) title = column.title || column.name;
732
+ }
617
733
  e.items.push({
618
- title: self.attributes.hideColumnText.replace(
619
- /%s/gi,
620
- e.cell.header.title || e.cell.header.name,
621
- ),
734
+ title: self.attributes.hideColumnText.replace(/%s/gi, title),
622
735
  click: function (ev) {
623
- self.getSchema()[columnIndex].hidden = true;
624
736
  ev.preventDefault();
625
737
  self.stopPropagation(ev);
626
738
  self.disposeContextMenu();
627
- self.setStorageData();
628
- setTimeout(function () {
629
- self.resize(true);
630
- }, 10);
739
+ if (contiguousColumns) {
740
+ self.hideColumns(
741
+ contiguousColumns[0].orderIndex,
742
+ contiguousColumns[1].orderIndex,
743
+ );
744
+ } else {
745
+ self.hideColumns(columnOrderIndex);
746
+ }
631
747
  },
632
748
  });
633
749
  }
@@ -689,6 +805,51 @@ export default function (self) {
689
805
  },
690
806
  });
691
807
  }
808
+
809
+ //#region hide rows
810
+ const canHideRows = !isSorting && e.cell.isRowHeader && e.cell.header;
811
+ if (canHideRows) {
812
+ const range = getSelectedContiguousRows(true);
813
+ if (range) {
814
+ const boundRowIndexes = range.map((viewRowIndex) =>
815
+ self.getBoundRowIndexFromViewRowIndex(viewRowIndex),
816
+ );
817
+ let title;
818
+ if (boundRowIndexes.length === 1) {
819
+ if (typeof boundRowIndexes[0] === 'number')
820
+ title = boundRowIndexes[0] + 1;
821
+ else title = range[0] + 1;
822
+
823
+ title = self.attributes.showHideRow.replace('%s', title);
824
+ // hide one row
825
+ e.items.push({
826
+ title,
827
+ click: function (ev) {
828
+ ev.preventDefault();
829
+ self.hideRows(boundRowIndexes[0], boundRowIndexes[0]);
830
+ },
831
+ });
832
+ } else if (boundRowIndexes[0] <= boundRowIndexes[1]) {
833
+ title = boundRowIndexes
834
+ .map((it, index) => {
835
+ if (typeof it === 'number') return it + 1;
836
+ return range[index] + 1;
837
+ })
838
+ .join('-');
839
+ title = self.attributes.showHideRows.replace('%s', title);
840
+ // hide rows
841
+ e.items.push({
842
+ title,
843
+ click: function (ev) {
844
+ ev.preventDefault();
845
+ self.hideRows(boundRowIndexes[0], boundRowIndexes[1]);
846
+ },
847
+ });
848
+ }
849
+ }
850
+ }
851
+ //#endregion hide rows
852
+
692
853
  //#region group/ungroup columns
693
854
  const groupAreaHeight = self.getColumnGroupAreaHeight();
694
855
  const groupAreaWidth = self.getRowGroupAreaWidth();
@@ -760,28 +921,13 @@ export default function (self) {
760
921
  const canUngroupColumns =
761
922
  self.attributes.allowGroupingColumns && e.cell.isColumnHeader;
762
923
  const canGroupByRows =
763
- self.attributes.allowGroupingRows && e.cell.isRowHeader && e.cell.header;
924
+ !isSorting &&
925
+ self.attributes.allowGroupingRows &&
926
+ e.cell.isRowHeader &&
927
+ e.cell.header;
764
928
  const canUngroupRows =
765
929
  self.attributes.allowGroupingRows && e.cell.isRowHeader;
766
930
 
767
- /**
768
- * The value for storing the return value from `self.getSchema()`
769
- * @type {any[]}
770
- */
771
- let schema;
772
- /**
773
- * A map between columnIndex and column data
774
- * @type {Map<string,any>}
775
- */
776
- let columns;
777
- const getColumnsMap = () => {
778
- if (!columns) {
779
- if (!schema) schema = self.getSchema();
780
- columns = new Map(schema.map((_col) => [_col.columnIndex, _col]));
781
- }
782
- return columns;
783
- };
784
-
785
931
  if (canGroupByColumns) {
786
932
  /** @type {number[]} */
787
933
  const groupIndexes = [];
@@ -863,23 +1009,7 @@ export default function (self) {
863
1009
  }
864
1010
  }
865
1011
  if (canGroupByRows) {
866
- let range = [];
867
- let prev = -2;
868
- let ok = true;
869
- self.selections.forEach(function (row, index) {
870
- if (!ok) return;
871
- if (prev < -1) {
872
- prev = index;
873
- range[0] = index;
874
- return;
875
- }
876
- if (index !== prev + 1 || !row || row.length === 0) {
877
- ok = false;
878
- return;
879
- }
880
- prev = index;
881
- range[1] = index;
882
- });
1012
+ const range = getSelectedContiguousRows(false) || [];
883
1013
  const rangeTitle = range
884
1014
  .map((rowIndex) => {
885
1015
  const index = self.getBoundRowIndexFromViewRowIndex(rowIndex);
@@ -888,7 +1018,6 @@ export default function (self) {
888
1018
  })
889
1019
  .join('-');
890
1020
  if (
891
- ok &&
892
1021
  range.length === 2 &&
893
1022
  self.isNewGroupRangeValid(self.groupedRows, range[0], range[1])
894
1023
  ) {
package/lib/defaults.js CHANGED
@@ -45,6 +45,10 @@ export default function (self) {
45
45
  ['filterFrozenRows', true],
46
46
  ['globalRowResize', false],
47
47
  ['hideColumnText', 'Hide %s'],
48
+ ['showUnhideColumnsIndicator', false],
49
+ ['showUnhideRowsIndicator', false],
50
+ ['showHideRow', 'Hide row %s'],
51
+ ['showHideRows', 'Hide rows %s'],
48
52
  ['hoverMode', 'cell'],
49
53
  ['keepFocusOnMouseOut', false],
50
54
  ['maxAutoCompleteItems', 200],
@@ -377,6 +381,10 @@ export default function (self) {
377
381
  ['treeArrowMarginTop', 6],
378
382
  ['treeArrowWidth', 13],
379
383
  ['treeGridHeight', 250],
384
+ ['unhideIndicatorColor', 'rgba(0, 0, 0, 1)'],
385
+ ['unhideIndicatorBackgroundColor', 'rgba(255, 255, 255, 1)'],
386
+ ['unhideIndicatorBorderColor', 'rgba(174, 193, 232, 1)'],
387
+ ['unhideIndicatorSize', 16],
380
388
  ['width', 'auto'],
381
389
  ],
382
390
  };
package/lib/draw.js CHANGED
@@ -110,8 +110,9 @@ export default function (self) {
110
110
  }
111
111
  /**
112
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
113
114
  */
114
- function strokeLines(coords) {
115
+ function drawLines(coords, fill) {
115
116
  if (coords.length < 4) return;
116
117
  self.ctx.beginPath();
117
118
  self.ctx.moveTo(
@@ -123,7 +124,8 @@ export default function (self) {
123
124
  const y = coords[i + 1] + self.canvasOffsetTop;
124
125
  self.ctx.lineTo(x, y);
125
126
  }
126
- self.ctx.stroke();
127
+ if (fill) self.ctx.fill();
128
+ else self.ctx.stroke();
127
129
  }
128
130
  /**
129
131
  * @param {number} x based-X (left-top)
@@ -136,8 +138,67 @@ export default function (self) {
136
138
  strokeRect(x, y, width, width);
137
139
  const cx = x + width * 0.5;
138
140
  const cy = y + width * 0.5;
139
- strokeLines([x + width * 0.2, cy, x + width * 0.78, cy]);
140
- if (collapsed) strokeLines([cx, y + width * 0.22, cx, y + width * 0.8]);
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);
141
202
  }
142
203
  function drawOrderByArrow(x, y) {
143
204
  var mt = self.style.columnHeaderOrderByArrowMarginTop * self.scale,
@@ -719,7 +780,15 @@ export default function (self) {
719
780
  columnGroupsRectInfo = {},
720
781
  collapsedColumnGroups = self.getCollapsedColumnGroups(),
721
782
  collapsedRowGroups = self.getCollapsedRowGroups(),
722
- cellHeight = self.style.cellHeight;
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);
723
792
  drawCount += 1;
724
793
  p = performance.now();
725
794
  self.visibleRowHeights = [];
@@ -1369,6 +1438,19 @@ export default function (self) {
1369
1438
  group.to === cell.boundRowIndex,
1370
1439
  ) >= 0;
1371
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
+ }
1372
1454
 
1373
1455
  if (hasRowGap) {
1374
1456
  const barHeight = self.style.rowHeaderCellRowNumberGapHeight;
@@ -1384,9 +1466,141 @@ export default function (self) {
1384
1466
  }
1385
1467
  }
1386
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
+
1387
1601
  x += cell.width + (bc ? 0 : self.style.cellBorderWidth);
1388
1602
  return cell.width;
1389
- };
1603
+ }; // end of drawEach
1390
1604
  }
1391
1605
  function drawFilterButton(cell, ev) {
1392
1606
  var posX = cell.x + cell.width - self.style.filterButtonWidth - 1;
@@ -1424,17 +1638,20 @@ export default function (self) {
1424
1638
  // as it is in the unfiltered data, instead of simply the viewed
1425
1639
  // row index + 1. If rowIndex > viewData.length, it's a new row
1426
1640
  // added to the end, and we want to render that new row's number
1427
- const filteredRowNumber =
1428
- self.viewData && rowIndex < self.viewData.length
1429
- ? self.getBoundRowIndexFromViewRowIndex(rowIndex) + 1
1430
- : self.originalData
1641
+ let filteredRowNumber;
1642
+ if (self.viewData && rowIndex < self.viewData.length)
1643
+ filteredRowNumber =
1644
+ self.getBoundRowIndexFromViewRowIndex(rowIndex) + 1;
1645
+ else
1646
+ filteredRowNumber = self.originalData
1431
1647
  ? self.originalData.length + 1
1432
1648
  : rowOrderIndex + 1;
1433
1649
 
1434
- const rowHeaderValue =
1650
+ let rowHeaderValue =
1435
1651
  self.hasActiveFilters() || self.hasCollapsedRowGroup()
1436
1652
  ? filteredRowNumber
1437
1653
  : rowIndex + 1;
1654
+ rowHeaderValue += currentRowIndexOffset;
1438
1655
 
1439
1656
  const rowHeaderCell = { rowHeaderCell: rowHeaderValue };
1440
1657
  const headerDescription = {
@@ -1451,6 +1668,14 @@ export default function (self) {
1451
1668
  -1,
1452
1669
  -1,
1453
1670
  );
1671
+
1672
+ if (
1673
+ rowIndexOffsetByHiddenRows[0] &&
1674
+ rowHeaderValue >= rowIndexOffsetByHiddenRows[0].from
1675
+ ) {
1676
+ const { plus } = rowIndexOffsetByHiddenRows.shift();
1677
+ currentRowIndexOffset += plus;
1678
+ }
1454
1679
  }
1455
1680
  }
1456
1681
  function drawHeaders() {
@@ -1675,12 +1900,20 @@ export default function (self) {
1675
1900
  schema = self.getSchema();
1676
1901
  self.visibleCells = [];
1677
1902
  self.visibleGroups = [];
1903
+ self.visibleUnhideIndicators = [];
1678
1904
  self.canvasOffsetTop = self.isChildGrid ? self.parentNode.offsetTop : 0.5;
1679
1905
  self.canvasOffsetLeft = self.isChildGrid
1680
1906
  ? self.parentNode.offsetLeft
1681
1907
  : -0.5;
1682
1908
  h = self.height;
1683
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
+ }
1684
1917
  }
1685
1918
  function drawBackground() {
1686
1919
  radiusRect(0, 0, w, h, 0);
@@ -1876,7 +2109,7 @@ export default function (self) {
1876
2109
  leftX -= toggleHandlePadding + 1;
1877
2110
  if (containsEnd) rightX += toggleHandleSize;
1878
2111
  }
1879
- strokeLines(lineCoords);
2112
+ drawLines(lineCoords);
1880
2113
  ctx.restore();
1881
2114
  pushToVisibleGroups(leftX, rightX);
1882
2115
  }
@@ -2054,7 +2287,7 @@ export default function (self) {
2054
2287
  // add more clickable area into `visibleGroups`
2055
2288
  if (containsEnd) bottomY += toggleHandleSize;
2056
2289
  }
2057
- strokeLines(lineCoords);
2290
+ drawLines(lineCoords);
2058
2291
  ctx.restore();
2059
2292
  pushToVisibleGroups(topY, bottomY);
2060
2293
  }