Skip to content

Commit e67abc3

Browse files
committed
fix: Grid Multi-Body Stabilization (Header Sync & Pinning) (#9615)
1 parent 977163b commit e67abc3

5 files changed

Lines changed: 36 additions & 19 deletions

File tree

resources/scss/src/grid/Body.scss

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@
1313

1414
.neo-grid-body {
1515
height : 100%;
16-
overflow-x: visible;
16+
overflow-x: hidden;
1717

1818
.neo-grid-cell {
1919
&.neo-is-modified {
@@ -39,6 +39,10 @@
3939
pointer-events: none !important;
4040
}
4141

42+
.neo-grid-body-content {
43+
position: relative;
44+
}
45+
4246
.neo-center {
4347
justify-content: center;
4448
}

resources/scss/src/grid/HorizontalScrollbar.scss

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,9 @@
11
.neo-grid-horizontal-scrollbar {
2-
overflow-x: auto;
3-
overflow-y: hidden;
2+
flex : none; // Ensure it's not collapsed by flex-direction column on container
3+
min-height : 17px;
4+
overflow-x : auto;
5+
overflow-y : hidden;
6+
position : relative;
47

58
.neo-grid-horizontal-scrollbar-content {
69
height: 1px;

src/grid/ScrollManager.mjs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -233,7 +233,7 @@ class ScrollManager extends Base {
233233

234234
if (active) {
235235
addon.register({
236-
bodyId : me.gridBody.id,
236+
bodyIds : [me.gridContainer.bodyStart?.id, me.gridContainer.body?.id, me.gridContainer.bodyEnd?.id].filter(Boolean),
237237
bodyWrapperId: me.gridContainer.bodyWrapper?.id,
238238
id : me.id,
239239
windowId
@@ -254,14 +254,14 @@ class ScrollManager extends Base {
254254

255255
if (active) {
256256
let scrollerId = me.gridContainer.horizontalScrollbar?.id,
257-
bodyWrapperId = me.gridContainer.bodyWrapper?.id,
257+
bodyId = me.gridContainer.body?.id,
258258
headerId = me.gridContainer.headerWrapper?.id;
259259

260-
if (scrollerId && bodyWrapperId && headerId) {
260+
if (scrollerId && bodyId && headerId) {
261261
addon.register({
262262
id : me.id + '__h_scroll',
263263
scrollerId,
264-
bodyId : bodyWrapperId,
264+
bodyId,
265265
headerId,
266266
windowId
267267
});

src/main/addon/GridHorizontalScrollSync.mjs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,7 @@ class GridHorizontalScrollSync extends Base {
4444
scrollLeft = scrollerNode.scrollLeft;
4545

4646
// Synchronously update both nodes in the identical animation frame
47-
if (bodyNode) { bodyNode.scrollLeft = scrollLeft; }
47+
if (bodyNode) { bodyNode.style.setProperty('--grid-scroll-left', scrollLeft + 'px'); }
4848
if (headerNode) { headerNode.scrollLeft = scrollLeft; }
4949
}
5050

src/main/addon/GridRowScrollPinning.mjs

Lines changed: 21 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -108,7 +108,7 @@ class GridRowScrollPinning extends Base {
108108
let me = this,
109109
state = me.registrations.get(id);
110110

111-
if (!state || !state.wrapperNode || !state.contentNode) {
111+
if (!state || !state.wrapperNode || !state.contentNodes?.length) {
112112
return
113113
}
114114

@@ -119,9 +119,13 @@ class GridRowScrollPinning extends Base {
119119
// We explicitly ignore Wheel, Trackpad, Keyboard, and Body Drag scrolling,
120120
// allowing their native/custom physics to operate perfectly.
121121
if (state.isThumbDragging) {
122-
state.contentNode.style.setProperty('--grid-row-pin-offset', `${deltaY}px`);
123-
} else if (state.contentNode.style.getPropertyValue('--grid-row-pin-offset') !== '0px') {
124-
state.contentNode.style.setProperty('--grid-row-pin-offset', '0px');
122+
state.contentNodes.forEach(node => {
123+
node.style.setProperty('--grid-row-pin-offset', `${deltaY}px`);
124+
});
125+
} else if (state.contentNodes[0].style.getPropertyValue('--grid-row-pin-offset') !== '0px') {
126+
state.contentNodes.forEach(node => {
127+
node.style.setProperty('--grid-row-pin-offset', '0px');
128+
});
125129
}
126130
}
127131

@@ -139,7 +143,13 @@ class GridRowScrollPinning extends Base {
139143
}
140144

141145
me.registrations.forEach(state => {
142-
let bodyMeta = meta[state.bodyId];
146+
let bodyMeta;
147+
148+
state.bodyIds.forEach(bodyId => {
149+
if (meta[bodyId]) {
150+
bodyMeta = meta[bodyId];
151+
}
152+
});
143153

144154
if (bodyMeta) {
145155
// Silently update the baseline state.
@@ -235,21 +245,21 @@ class GridRowScrollPinning extends Base {
235245
/**
236246
* Registers a grid for row scroll pinning and attaches native scroll listener.
237247
* @param {Object} data
238-
* @param {String} data.bodyId The ID of the grid body
248+
* @param {String[]} data.bodyIds The IDs of the grid body nodes
239249
* @param {String} data.bodyWrapperId The ID of the vertical scroll wrapper
240250
* @param {String} data.id Unique identifier for the registration (e.g. ScrollManager id)
241251
*/
242-
register({bodyId, bodyWrapperId, id}) {
252+
register({bodyIds, bodyWrapperId, id}) {
243253
let me = this,
244254
wrapperNode = DomAccess.getElement(bodyWrapperId),
245-
contentNode = DomAccess.getElement(bodyId);
255+
contentNodes = bodyIds.map(bodyId => DomAccess.getElement(bodyId)).filter(Boolean);
246256

247-
if (wrapperNode && contentNode) {
257+
if (wrapperNode && contentNodes.length > 0) {
248258
me.registrations.set(id, {
249259
id,
250-
bodyId,
260+
bodyIds,
251261
wrapperNode,
252-
contentNode,
262+
contentNodes,
253263
isThumbDragging: false,
254264
scrollTimeoutId: null,
255265
workerScrollTop: 0

0 commit comments

Comments
 (0)