finos / finos/regular-table

Sub-cell transform offset can go permanently stale for some grid shapes (unbounded, narrow grids)

Open
#291 0 comments 0 reactions 0 assignees View on GitHub
Dominant language
JavaScript
Stars
403
Forks
43
Avg merge
3m
Merged PRs (30d)
1

Description

## Summary

`_update_sub_cell_offset` (the function that writes `--regular-table--transform-y`/`--regular-table--clip-y` etc. for sub-cell/fractional-row scroll positioning) is only ever invoked as a side effect buried inside the generator-driven render pass in `table.ts`'s `_render_pass` - gated behind a `first_iteration` flag inside a `style_callback` that only runs on whichever `yield "style"` point the pass happens to reach for a given data shape/`virtual_mode` combination.

## Reproduction

Confirmed live on an unbounded, narrow (few-column) grid: the transform-y/clip-y CSS variables never changed at all across dozens of scroll events spanning the full scrollable range, even though the row content itself updated correctly on every one. Some path through `_render_pass` for that shape never yields `"style"` at all, so `style_callback` (and therefore `_update_sub_cell_offset`) never runs, leaving the transform frozen at whatever value predated the test - which visually reads as the whole grid's content being offset by a fixed amount that never corrects itself.

## Status: workaround, not root cause

I have **not** identified exactly which generator path/condition in `_render_pass` is responsible for skipping the `"style"` yield for this shape - it may not be only one. Rather than chase that down, our fork works around it by having `_on_scroll` also recompute and apply the sub-cell offset directly and unconditionally after every scroll-driven commit, from the live scroll position:

```ts
const last_response = this.table_model._lastDataResponse;
if (last_response) {
const fresh_viewport = this._calculate_viewport(
last_response.num_rows,
last_response.num_columns,
this.scrollTop,
this.scrollLeft,
);
this._update_sub_cell_offset(fresh_viewport);
}
```

(This required making `_lastDataResponse` non-private on `table.ts`'s view model.) It's cheap, pure, synchronous math with no data fetch involved, so doing it defensively on every scroll costs nothing noticeable, and it no longer depends on the nested render pass reaching a particular yield point to keep the visible position correct.

Filing this as a real bug report (rather than just a PR with the workaround) because I think the actual root cause - why some data shape/`virtual_mode` combinations skip the `"style"` yield in `_render_pass` entirely - is worth someone more familiar with that generator's control flow taking a look at, since our fix only treats the symptom.

Contributor guide

Open the contributing guide

Research direction

Reproduce the issue on an unbounded, narrow grid while tracing table.ts's _render_pass and its style_callback around the yield "style" point. Follow how _on_scroll, _calculate_viewport, and _update_sub_cell_offset interact, then identify why the style yield is skipped for this shape or virtual_mode. Done means the sub-cell transform and clip variables update correctly across the full scroll range.

Written by the indexing model from the issue text.

Assessment

Tech stack
javascript
Domain
frontend
Issue type
Bug
Difficulty
4/5
Estimated time
3-5 days
Activity status
Active
Clarity
Mostly clear
Newbie friendliness
48/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.