Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Master smooth scroll v2 rar #5416

Open
wants to merge 3 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 4 additions & 1 deletion demo/main.js
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,10 @@ class Demo extends Component {
sequence: 12,
isReadonlyAllowed: true,
isVisible: () => this.model.config.mode !== "dashboard",
execute: () => this.model.updateMode("dashboard"),
execute: () => {
this.model.updateMode("dashboard");
this.model.dispatch("SET_SROLL_MODE", { mode: "smooth" });
},
icon: "o-spreadsheet-Icon.OPEN_DASHBOARD",
});

Expand Down
67 changes: 56 additions & 11 deletions src/helpers/internal_viewport.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import {
Pixel,
Position,
Rect,
ScrollMode,
UID,
Zone,
} from "../types";
Expand All @@ -28,15 +29,18 @@ export class InternalViewport {
viewportHeight: Pixel;
offsetCorrectionX: Pixel;
offsetCorrectionY: Pixel;
mode: ScrollMode;

constructor(
private getters: Getters,
private sheetId: UID,
private boundaries: Zone,
sizeInGrid: DOMDimension,
options: { canScrollVertically: boolean; canScrollHorizontally: boolean },
offsets: DOMCoordinates
offsets: DOMCoordinates,
mode: ScrollMode
) {
this.mode = mode;
this.viewportWidth = sizeInGrid.width;
this.viewportHeight = sizeInGrid.height;
this.offsetScrollbarX = offsets.x;
Expand Down Expand Up @@ -117,7 +121,11 @@ export class InternalViewport {
if (x < this.offsetCorrectionX || x > this.offsetCorrectionX + this.viewportWidth) {
return -1;
}
return this.searchHeaderIndex("COL", x - this.offsetCorrectionX, this.left);
return this.searchHeaderIndex(
"COL",
x - this.offsetCorrectionX + Math.abs(this.scrollCorrection.x),
this.left
);
}

/**
Expand All @@ -129,7 +137,11 @@ export class InternalViewport {
if (y < this.offsetCorrectionY || y > this.offsetCorrectionY + this.viewportHeight) {
return -1;
}
return this.searchHeaderIndex("ROW", y - this.offsetCorrectionY, this.top);
return this.searchHeaderIndex(
"ROW",
y - this.offsetCorrectionY + Math.abs(this.scrollCorrection.y),
this.top
);
}

/**
Expand Down Expand Up @@ -231,19 +243,27 @@ export class InternalViewport {
*/
getVisibleRect(zone: Zone): Rect | undefined {
const targetZone = intersection(zone, this);
const scrollDeltaX = Math.abs(this.scrollCorrection.x);
const scrollDeltaY = Math.abs(this.scrollCorrection.y);
if (targetZone) {
const x =
this.getters.getColRowOffset("COL", this.left, targetZone.left) + this.offsetCorrectionX;
this.getters.getColRowOffset("COL", this.left, targetZone.left) +
this.offsetCorrectionX -
(this.left !== targetZone.left ? scrollDeltaX : 0);

const y =
this.getters.getColRowOffset("ROW", this.top, targetZone.top) + this.offsetCorrectionY;
this.getters.getColRowOffset("ROW", this.top, targetZone.top) +
this.offsetCorrectionY -
(this.top !== targetZone.top ? scrollDeltaY : 0);

const width = Math.min(
this.getters.getColRowOffset("COL", targetZone.left, targetZone.right + 1),
this.getters.getColRowOffset("COL", targetZone.left, targetZone.right + 1) -
(this.left === targetZone.left ? scrollDeltaX : 0),
this.viewportWidth
);
const height = Math.min(
this.getters.getColRowOffset("ROW", targetZone.top, targetZone.bottom + 1),
this.getters.getColRowOffset("ROW", targetZone.top, targetZone.bottom + 1) -
(this.top === targetZone.top ? scrollDeltaY : 0),
this.viewportHeight
);
return { x, y, width, height };
Expand All @@ -258,13 +278,15 @@ export class InternalViewport {
*/
getFullRect(zone: Zone): Rect | undefined {
const targetZone = intersection(zone, this);
const scrollDeltaX = Math.abs(this.scrollCorrection.x);
const scrollDeltaY = Math.abs(this.scrollCorrection.y);
if (targetZone) {
const x = this.getters.getColRowOffset("COL", this.left, zone.left) + this.offsetCorrectionX;
const y = this.getters.getColRowOffset("ROW", this.top, zone.top) + this.offsetCorrectionY;
const width = this.getters.getColRowOffset("COL", zone.left, zone.right + 1);

const height = this.getters.getColRowOffset("ROW", zone.top, zone.bottom + 1);
return { x, y, width, height };
return { x: x - scrollDeltaX, y: y - scrollDeltaY, width, height };
}
return undefined;
}
Expand All @@ -289,7 +311,7 @@ export class InternalViewport {
let start = startIndex;
let end = headers;
while (start <= end && start !== headers && end !== -1) {
const mid = Math.floor((start + end) / 2);
const mid: HeaderIndex = Math.floor((start + end) / 2);
const offset = this.getters.getColRowOffset(dimension, startIndex, mid);
const size = this.getters.getHeaderSize(sheetId, dimension, mid);
if (position >= offset && position < offset + size) {
Expand Down Expand Up @@ -352,7 +374,11 @@ export class InternalViewport {
this.left = this.searchHeaderIndex("COL", this.offsetScrollbarX, this.boundaries.left);
this.right = Math.min(
this.boundaries.right,
this.searchHeaderIndex("COL", this.viewportWidth, this.left)
this.searchHeaderIndex(
"COL",
this.viewportWidth + Math.abs(this.scrollCorrection.x),
this.left
)
);
if (this.left === -1) {
this.left = this.boundaries.left;
Expand All @@ -372,7 +398,11 @@ export class InternalViewport {
this.top = this.searchHeaderIndex("ROW", this.offsetScrollbarY, this.boundaries.top);
this.bottom = Math.min(
this.boundaries.bottom,
this.searchHeaderIndex("ROW", this.viewportHeight, this.top)
this.searchHeaderIndex(
"ROW",
this.viewportHeight + Math.abs(this.scrollCorrection.y),
this.top
)
);
if (this.top === -1) {
this.top = this.boundaries.top;
Expand All @@ -384,4 +414,19 @@ export class InternalViewport {
this.getters.getRowDimensions(sheetId, this.top).start -
this.getters.getRowDimensions(sheetId, this.boundaries.top).start;
}

get scrollCorrection() {
if (this.mode === "snap") {
return { x: 0, y: 0 };
}

return {
x:
this.offsetScrollbarX -
this.getters.getColRowOffset("COL", this.boundaries.left, Math.max(0, this.left)),
y:
this.offsetScrollbarY -
this.getters.getColRowOffset("ROW", this.boundaries.top, Math.max(0, this.top)),
};
}
}
2 changes: 1 addition & 1 deletion src/plugins/core/sheet.ts
Original file line number Diff line number Diff line change
Expand Up @@ -425,7 +425,7 @@ export class SheetPlugin extends CorePlugin<SheetState> implements SheetState {
return this.getSheet(sheetId).rows.length;
}

getNumberHeaders(sheetId: UID, dimension: Dimension) {
getNumberHeaders(sheetId: UID, dimension: Dimension): HeaderIndex {
return dimension === "COL" ? this.getNumberCols(sheetId) : this.getNumberRows(sheetId);
}

Expand Down
60 changes: 39 additions & 21 deletions src/plugins/ui_stateful/sheetview.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { getDefaultSheetViewSize } from "../../constants";
import { clip, findCellInNewZone, isDefined, largeMin, range } from "../../helpers";
import { clip, findCellInNewZone, isDefined, range } from "../../helpers";
import { scrollDelay } from "../../helpers/index";
import { InternalViewport } from "../../helpers/internal_viewport";
import { SelectionEvent } from "../../types/event_stream";
Expand All @@ -19,6 +19,7 @@ import {
Rect,
ResizeViewportCommand,
ScrollDirection,
ScrollMode,
SetViewportOffsetCommand,
SheetDOMScrollInfo,
SheetScrollInfo,
Expand Down Expand Up @@ -106,6 +107,7 @@ export class SheetViewPlugin extends UIPlugin {
"getRowDimensionsInViewport",
"getAllActiveViewportsZones",
"getRect",
"getScrollMode",
] as const;

readonly viewports: Record<UID, SheetViewports | undefined> = {};
Expand All @@ -123,6 +125,7 @@ export class SheetViewPlugin extends UIPlugin {

private sheetsWithDirtyViewports: Set<UID> = new Set();
private shouldAdjustViewports: boolean = false;
private mode: ScrollMode = "snap";

// ---------------------------------------------------------------------------
// Command Handling
Expand Down Expand Up @@ -258,6 +261,10 @@ export class SheetViewPlugin extends UIPlugin {
case "SCROLL_TO_CELL":
this.refreshViewport(this.getters.getActiveSheetId(), { col: cmd.col, row: cmd.row });
break;
case "SET_SCROLL_MODE":
this.mode = cmd.mode;
this.recomputeViewports();
break;
}
}

Expand Down Expand Up @@ -333,6 +340,9 @@ export class SheetViewPlugin extends UIPlugin {
* the grid left/top side, snapped to the columns/rows.
*/
getActiveSheetScrollInfo(): SheetScrollInfo {
if (this.mode === "smooth") {
return this.getActiveSheetDOMScrollInfo();
}
const sheetId = this.getters.getActiveSheetId();
const viewport = this.getMainInternalViewport(sheetId);
return {
Expand Down Expand Up @@ -576,38 +586,42 @@ export class SheetViewPlugin extends UIPlugin {
* column of the current viewport
*/
getColDimensionsInViewport(sheetId: UID, col: HeaderIndex): HeaderDimensions {
const left = largeMin(this.getters.getSheetViewVisibleCols());
const start = this.getters.getColRowOffsetInViewport("COL", left, col);
const size = this.getters.getColSize(sheetId, col);
const isColHidden = this.getters.isColHidden(sheetId, col);
return {
start,
size: size,
end: start + (isColHidden ? 0 : size),
const zone = {
left: col,
right: col,
top: 0,
bottom: this.getters.getNumberRows(sheetId) - 1,
};
const { x, width } = this.getVisibleRect(zone);
const start = x - this.gridOffsetX;
return { start, size: width, end: start + width };
}

/**
* Returns the size, start and end coordinates of a row relative to the top row
* of the current viewport
*/
getRowDimensionsInViewport(sheetId: UID, row: HeaderIndex): HeaderDimensions {
const top = largeMin(this.getters.getSheetViewVisibleRows());
const start = this.getters.getColRowOffsetInViewport("ROW", top, row);
const size = this.getters.getRowSize(sheetId, row);
const isRowHidden = this.getters.isRowHidden(sheetId, row);
return {
start,
size: size,
end: start + (isRowHidden ? 0 : size),
const zone = {
left: 0,
right: this.getters.getNumberCols(sheetId) - 1,
top: row,
bottom: row,
};
const { y, height } = this.getVisibleRect(zone);
const start = y - this.gridOffsetY;
return { start, size: height, end: start + height };
}

getAllActiveViewportsZones(): Zone[] {
const sheetId = this.getters.getActiveSheetId();
return this.getSubViewports(sheetId);
}

getScrollMode(): InternalViewport["mode"] {
return this.mode;
}

// ---------------------------------------------------------------------------
// Private
// ---------------------------------------------------------------------------
Expand Down Expand Up @@ -755,7 +769,8 @@ export class SheetViewPlugin extends UIPlugin {
{ left: 0, right: xSplit - 1, top: 0, bottom: ySplit - 1 },
{ width: colOffset, height: rowOffset },
{ canScrollHorizontally: false, canScrollVertically: false },
{ x: 0, y: 0 }
{ x: 0, y: 0 },
this.mode
)) ||
undefined,
topRight:
Expand All @@ -766,7 +781,8 @@ export class SheetViewPlugin extends UIPlugin {
{ left: xSplit, right: nCols - 1, top: 0, bottom: ySplit - 1 },
{ width: this.sheetViewWidth - colOffset, height: rowOffset },
{ canScrollHorizontally, canScrollVertically: false },
{ x: canScrollHorizontally ? previousOffset.x : 0, y: 0 }
{ x: canScrollHorizontally ? previousOffset.x : 0, y: 0 },
this.mode
)) ||
undefined,
bottomLeft:
Expand All @@ -777,7 +793,8 @@ export class SheetViewPlugin extends UIPlugin {
{ left: 0, right: xSplit - 1, top: ySplit, bottom: nRows - 1 },
{ width: colOffset, height: this.sheetViewHeight - rowOffset },
{ canScrollHorizontally: false, canScrollVertically },
{ x: 0, y: canScrollVertically ? previousOffset.y : 0 }
{ x: 0, y: canScrollVertically ? previousOffset.y : 0 },
this.mode
)) ||
undefined,
bottomRight: new InternalViewport(
Expand All @@ -792,7 +809,8 @@ export class SheetViewPlugin extends UIPlugin {
{
x: canScrollHorizontally ? previousOffset.x : 0,
y: canScrollVertically ? previousOffset.y : 0,
}
},
this.mode
),
};
this.viewports[sheetId] = sheetViewports;
Expand Down
17 changes: 17 additions & 0 deletions src/registries/menus/topbar_menu_registry.ts
Original file line number Diff line number Diff line change
Expand Up @@ -207,6 +207,23 @@ topbarMenuRegistry
sequence: 40,
separator: true,
})
.addChild("scroll_mode", ["view"], {
name: _t("scroll mode"),
sequence: 15,
separator: true,
})
.addChild("snap", ["view", "scroll_mode"], {
execute: (env) => env.model.dispatch("SET_SCROLL_MODE", { mode: "snap" }),
sequence: 10,
isActive: (env) => env.model.getters.getScrollMode() === "snap",
name: _t("Snapped scrolling"),
})
.addChild("smooth_scroll", ["view", "scroll_mode"], {
execute: (env) => env.model.dispatch("SET_SCROLL_MODE", { mode: "smooth" }),
sequence: 20,
isActive: (env) => env.model.getters.getScrollMode() === "smooth",
name: _t("Smooth scrolling"),
})

// ---------------------------------------------------------------------
// INSERT MENU ITEMS
Expand Down
Loading