Skip to content

Commit

Permalink
wip smooth
Browse files Browse the repository at this point in the history
  • Loading branch information
rrahir committed Jan 16, 2025
1 parent fefaa8d commit c2e8112
Show file tree
Hide file tree
Showing 9 changed files with 172 additions and 68 deletions.
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
66 changes: 55 additions & 11 deletions src/helpers/internal_viewport.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,15 +28,18 @@ export class InternalViewport {
viewportHeight: Pixel;
offsetCorrectionX: Pixel;
offsetCorrectionY: Pixel;
mode: "snapped" | "smooth";

constructor(
private getters: Getters,
private sheetId: UID,
private boundaries: Zone,
sizeInGrid: DOMDimension,
options: { canScrollVertically: boolean; canScrollHorizontally: boolean },
offsets: DOMCoordinates
offsets: DOMCoordinates,
mode: "snapped" | "smooth"
) {
this.mode = mode;
this.viewportWidth = sizeInGrid.width;
this.viewportHeight = sizeInGrid.height;
this.offsetScrollbarX = offsets.x;
Expand Down Expand Up @@ -117,7 +120,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 +136,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 +242,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 @@ -253,6 +272,8 @@ 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;
Expand All @@ -262,7 +283,7 @@ export class InternalViewport {
const height =
this.getters.getColRowOffset("ROW", this.boundaries.top, zone.bottom + 1) -
this.getters.getColRowOffset("ROW", this.boundaries.top, zone.top);
return { x, y, width, height };
return { x: x - scrollDeltaX, y: y - scrollDeltaY, width, height };
} else {
return undefined;
}
Expand All @@ -288,7 +309,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 @@ -351,7 +372,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 @@ -371,7 +396,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 @@ -383,4 +412,19 @@ export class InternalViewport {
this.getters.getRowDimensions(sheetId, this.top).start -
this.getters.getRowDimensions(sheetId, this.boundaries.top).start;
}

get scrollCorrection() {
if (this.mode === "snapped") {
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
59 changes: 38 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 Down Expand Up @@ -106,6 +106,7 @@ export class SheetViewPlugin extends UIPlugin {
"getRowDimensionsInViewport",
"getAllActiveViewportsZones",
"getRenderingRect",
"getScrollMode",
] as const;

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

private sheetsWithDirtyViewports: Set<UID> = new Set();
private shouldAdjustViewports: boolean = false;
private mode: InternalViewport["mode"] = "smooth";

// ---------------------------------------------------------------------------
// Command Handling
Expand Down Expand Up @@ -258,6 +260,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 +339,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 @@ -584,38 +593,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 @@ -763,7 +776,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 @@ -774,7 +788,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 @@ -785,7 +800,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 @@ -800,7 +816,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("snapped", ["view", "scroll_mode"], {
execute: (env) => env.model.dispatch("SET_SCROLL_MODE", { mode: "snapped" }),
sequence: 10,
isActive: (env) => env.model.getters.getScrollMode() === "snapped",
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

0 comments on commit c2e8112

Please sign in to comment.