Frontmatter
| id | 9476 |
| title | DevIndex: Add Contextual Rank Column to Grid |
| state | Closed |
| labels | enhancementai |
| assignees | tobiu |
| createdAt | Mar 14, 2026, 2:03 PM |
| updatedAt | Mar 14, 2026, 5:34 PM |
| githubUrl | https://github.com/neomjs/neo/issues/9476 |
| author | tobiu |
| commentsCount | 1 |
| parentIssue | null |
| subIssues | [] |
| subIssuesCompleted | 0 |
| subIssuesTotal | 0 |
| blockedBy | [] |
| blocking | [] |
| closedAt | Mar 14, 2026, 5:32 PM |
DevIndex: Add Contextual Rank Column to Grid
tobiu assigned to @tobiu on Mar 14, 2026, 5:30 PM
tobiu cross-referenced by PR #9477 on Mar 14, 2026, 5:31 PM
tobiu closed this issue on Mar 14, 2026, 5:32 PM

tobiu
Mar 14, 2026, 5:34 PM
Input from Gemini 3.1 Pro:
✦ ### Architectural Post-Mortem
This issue started as a straightforward task to replace the Grid's
indexcolumn with arankcolumn but evolved into a deep dive into the performance mechanics of Neo.mjs's "Turbo Mode" and VDOM rendering.The Challenge: We needed a contextual rank that recalculated based on "Structural" filters (e.g., Country) but remained static during "Search" filters (e.g., Username) and grid sorting.
The Evolution of the Implementation:
- Naive Override (Failed): We initially tried overriding
Store:filter()to calculate the rank on the fly. This was rejected because executing anO(N)calculation on 50,000 records on every keystroke during a text search is a performance disaster.- Cached Override with
setSilent(Buggy UI): We added aneedsCalculationcache hash so the heavy lifting only occurred when Structural filters changed. To avoid an event storm (50krecordChangeevents), we usedrecord.setSilent({ rank: X }).
- The Flaw:
setSilentincrements the Record'sversion, but if the array length and structure don't change (e.g., filtering a 1-row view to a 1-row view), the Grid's rendering engine might not visually update the row if the reference remains identical and the event is swallowed. The DOM became stale.- The "Map + Renderer" Pivot (Abandoned): We tried decoupling the rank from the Record entirely by storing it in a
Mapand using a Grid columnrenderer.
- The Flaw: This broke reactivity entirely. If the Record itself doesn't change, the Grid row is never re-rendered, meaning the
rendereris never called to fetch the new Map value.- The Final Architecture (Post-Filter Analysis): The breakthrough was realizing we need to know the exact visibility state of the records before deciding how to mutate them. We enhanced the framework base classes (
Neo.collection.BaseandNeo.data.Store) by adding asilentparameter to theirfilter()methods.The Final Workflow (
DevIndex.store.Contributors):
- We intercept
filter().- We calculate the new ranks into a temporary
newRanksMap.- We execute
super.filter(silent: true). This builds the finalme._items(the visible rows) without triggering the Grid.- We iterate the
newRanksMap:
- If the item is a raw object, we mutate it directly (silent by nature).
- If the item is a
RecordAND it is in theme._itemsvisible subset, we execute a loud update (item.rank = X). This firesrecordChangespecifically for the visible rows, guaranteeing the Grid's VDOM updates the DOM immediately.- If the item is a
Recordbut is HIDDEN, we executeitem.setSilent({ rank: X }). This prevents the dreaded 50k event storm while keeping the data accurate for when the row eventually scrolls into view.- Finally, we manually fire the Store's
filterevent.This architecture elegantly solves the "Stale UI vs. Event Storm" dilemma in high-performance virtualized grids.
The current "Index" column in the DevIndex grid reflects the row index of the currently filtered view. While this is technically correct, it causes users to lose their orientation when sorting the grid or searching for specific users.
We need to introduce a dedicated "Rank" column that accurately reflects a user's standing based on the active dataset context.
Rules for Rank:
Refinement: We will keep the existing "Index" column but add the new "Rank" column right after it. This preserves the row counting functionality while providing the necessary contextual ranking data.
Implementation Plan:
rankfield to theDevIndex.model.Contributor.DevIndex.store.Contributorsto calculaterankbased on the initial load order and recalculate it only when structural filters change.DevIndex.view.home.GridContainerto include the newrankcolumn adjacent to theindexcolumn.