diff --git a/src/helpers/internal_viewport.ts b/src/helpers/internal_viewport.ts index 3541f9b08..86467ad05 100644 --- a/src/helpers/internal_viewport.ts +++ b/src/helpers/internal_viewport.ts @@ -26,8 +26,8 @@ export class InternalViewport { canScrollHorizontally: boolean; viewportWidth: Pixel; viewportHeight: Pixel; - private offsetCorrectionX: Pixel; - private offsetCorrectionY: Pixel; + offsetCorrectionX: Pixel; + offsetCorrectionY: Pixel; constructor( private getters: Getters, diff --git a/src/plugins/ui_stateful/sheetview.ts b/src/plugins/ui_stateful/sheetview.ts index 0751d1db5..29ca6bb79 100644 --- a/src/plugins/ui_stateful/sheetview.ts +++ b/src/plugins/ui_stateful/sheetview.ts @@ -138,13 +138,14 @@ export class SheetViewPlugin extends UIPlugin { } private handleEvent(event: SelectionEvent) { + const sheetId = this.getters.getActiveSheetId(); if (event.options.scrollIntoView) { let { col, row } = findCellInNewZone(event.previousAnchor.zone, event.anchor.zone); if (event.mode === "updateAnchor") { const oldZone = event.previousAnchor.zone; const newZone = event.anchor.zone; // altering a zone should not move the viewport in a dimension that wasn't changed - const { top, bottom, left, right } = this.getters.getActiveMainViewport(); + const { top, bottom, left, right } = this.getMainInternalViewport(sheetId); if (oldZone.left === newZone.left && oldZone.right === newZone.right) { col = left > col || col > right ? left : col; } @@ -152,7 +153,6 @@ export class SheetViewPlugin extends UIPlugin { row = top > row || row > bottom ? top : row; } } - const sheetId = this.getters.getActiveSheetId(); col = Math.min(col, this.getters.getNumberCols(sheetId) - 1); row = Math.min(row, this.getters.getNumberRows(sheetId) - 1); if (!this.sheetsWithDirtyViewports.has(sheetId)) { @@ -191,20 +191,16 @@ export class SheetViewPlugin extends UIPlugin { this.setSheetViewOffset(cmd.offsetX, cmd.offsetY); break; case "SHIFT_VIEWPORT_DOWN": - const { top } = this.getActiveMainViewport(); const sheetId = this.getters.getActiveSheetId(); - const shiftedOffsetY = this.clipOffsetY( - this.getters.getRowDimensions(sheetId, top).start + this.sheetViewHeight - ); - this.shiftVertically(shiftedOffsetY); + const { top, viewportHeight, offsetCorrectionY } = this.getMainInternalViewport(sheetId); + const topRowDims = this.getters.getRowDimensions(sheetId, top); + this.shiftVertically(topRowDims.start + viewportHeight - offsetCorrectionY); break; case "SHIFT_VIEWPORT_UP": { - const { top } = this.getActiveMainViewport(); const sheetId = this.getters.getActiveSheetId(); - const shiftedOffsetY = this.clipOffsetY( - this.getters.getRowDimensions(sheetId, top).end - this.sheetViewHeight - ); - this.shiftVertically(shiftedOffsetY); + const { top, viewportHeight, offsetCorrectionY } = this.getMainInternalViewport(sheetId); + const topRowDims = this.getters.getRowDimensions(sheetId, top); + this.shiftVertically(topRowDims.end - offsetCorrectionY - viewportHeight); break; } case "REMOVE_FILTER_TABLE": @@ -658,18 +654,6 @@ export class SheetViewPlugin extends UIPlugin { ); } - /** - * Clip the vertical offset within the allowed range. - * Not above the sheet, nor below the sheet. - */ - private clipOffsetY(offsetY: Pixel): Pixel { - const { height } = this.getMainViewportRect(); - const maxOffset = height - this.sheetViewHeight; - offsetY = Math.min(offsetY, maxOffset); - offsetY = Math.max(offsetY, 0); - return offsetY; - } - private getViewportOffset(sheetId: UID) { return { x: this.viewports[sheetId]?.bottomRight.offsetScrollbarX || 0, @@ -760,12 +744,15 @@ export class SheetViewPlugin extends UIPlugin { * viewport top. */ private shiftVertically(offset: Pixel) { - const { top } = this.getActiveMainViewport(); + const sheetId = this.getters.getActiveSheetId(); + const { top } = this.getMainInternalViewport(sheetId); const { scrollX } = this.getActiveSheetScrollInfo(); this.setSheetViewOffset(scrollX, offset); const { anchor } = this.getters.getSelection(); - const deltaRow = this.getActiveMainViewport().top - top; - this.selection.selectCell(anchor.cell.col, anchor.cell.row + deltaRow); + if (anchor.cell.row >= this.getters.getPaneDivisions(sheetId).ySplit) { + const deltaRow = this.getMainInternalViewport(sheetId).top - top; + this.selection.selectCell(anchor.cell.col, anchor.cell.row + deltaRow); + } } getVisibleFigures(): Figure[] { diff --git a/tests/sheet/sheetview_plugin.test.ts b/tests/sheet/sheetview_plugin.test.ts index fa28c7917..1df2ab29b 100644 --- a/tests/sheet/sheetview_plugin.test.ts +++ b/tests/sheet/sheetview_plugin.test.ts @@ -1389,6 +1389,47 @@ describe("shift viewport up/down", () => { }); }); + describe("shift down/up with frozen panes", () => { + test("shift down/up with frozen rows and with selection in frozen rows", () => { + freezeRows(model, 5); + const { bottom, top } = model.getters.getActiveMainViewport(); + model.dispatch("SHIFT_VIEWPORT_DOWN"); + expect(model.getters.getActiveMainViewport().top).toBe(bottom); + expect(zoneToXc(model.getters.getSelectedZone())).toEqual("A1"); + model.dispatch("SHIFT_VIEWPORT_UP"); + expect(model.getters.getActiveMainViewport().top).toBe(top); + expect(zoneToXc(model.getters.getSelectedZone())).toEqual("A1"); + }); + + test("shift down/up with frozen rows and with selection not in frozen rows", () => { + freezeRows(model, 5); + selectCell(model, "A6"); + const { bottom, top } = model.getters.getActiveMainViewport(); + model.dispatch("SHIFT_VIEWPORT_DOWN"); + expect(model.getters.getActiveMainViewport().top).toBe(bottom); + expect(zoneToXc(model.getters.getSelectedZone())).toEqual(toXC(0, bottom)); + model.dispatch("SHIFT_VIEWPORT_UP"); + expect(model.getters.getActiveMainViewport().top).toBe(top); + expect(zoneToXc(model.getters.getSelectedZone())).toEqual("A6"); + }); + + test("shift down scrolls until the last row if there are frozen rows", () => { + const sheetId = model.getters.getActiveSheetId(); + const numberOfRows = model.getters.getNumberRows(sheetId); + freezeRows(model, 10); + let { bottom } = model.getters.getActiveMainViewport(); + while (true) { + model.dispatch("SHIFT_VIEWPORT_DOWN"); + const newBottom = model.getters.getActiveMainViewport().bottom; + if (newBottom === bottom) { + break; + } + bottom = newBottom; + } + expect(model.getters.getActiveMainViewport().bottom).toBe(numberOfRows - 1); + }); + }); + test.each(["A1", "A2"])( "viewport and selection %s do not move when its already the end of the sheet", (selectedCell) => {