diff --git a/packages/common/src/core/__tests__/slickGrid.spec.ts b/packages/common/src/core/__tests__/slickGrid.spec.ts index 5bcdaf814..0636f950a 100644 --- a/packages/common/src/core/__tests__/slickGrid.spec.ts +++ b/packages/common/src/core/__tests__/slickGrid.spec.ts @@ -5110,6 +5110,7 @@ describe('SlickGrid core file', () => { resizeHandleElm.dispatchEvent(cMouseDownEvent); container.dispatchEvent(cMouseDownEvent); document.body.dispatchEvent(bodyMouseMoveEvent); + expect((grid as any)._columnResizeAutoScrollTimer).toBeUndefined(); document.body.dispatchEvent(bodyMouseUpEvent); expect(scrollToXSpy).not.toHaveBeenCalledWith(400); @@ -5186,15 +5187,12 @@ describe('SlickGrid core file', () => { container.dispatchEvent(cMouseDownEvent); document.body.dispatchEvent(bodyMouseMoveEvent); - const dragCallCountAfterMove = onColumnsDragSpy.mock.calls.length; - - // The minimal auto-scroll logic triggers the callback every 30ms (RESIZE_AUTOSCROLL_MIN_INTERVAL_MS), - // so after 90ms, we expect 4 calls (initial + 3 intervals) + // Resize and reorder auto-scroll share the same 30ms interval. vi.advanceTimersByTime(90); expect(onColumnsDragSpy.mock.calls.length).toBe(4); vi.advanceTimersByTime(40); - expect(onColumnsDragSpy.mock.calls.length).toBeGreaterThan(dragCallCountAfterMove); + expect(onColumnsDragSpy.mock.calls.length).toBe(5); document.body.dispatchEvent(bodyMouseUpEvent); }); @@ -5294,7 +5292,7 @@ describe('SlickGrid core file', () => { document.body.dispatchEvent(upEvt); }); - it('should trigger accelerated auto-scroll when resizing column inside grid (non-browser-edge)', () => { + it('should clamp resizing to the viewport edge before auto-scroll continues at the shared rate', () => { grid = new SlickGrid(container, data, columns, { ...defaultOptions, forceFitColumns: false, @@ -5309,26 +5307,27 @@ describe('SlickGrid core file', () => { Object.defineProperty(viewportX, 'scrollLeft', { configurable: true, writable: true, value: 0 }); const columnElms = container.querySelectorAll('.slick-header-column'); - const lastColumnElm = columnElms[3]; - const resizeHandleElm = lastColumnElm.querySelector('.slick-resizable-handle') as HTMLDivElement; + const resizeHandleElm = columnElms[0].querySelector('.slick-resizable-handle') as HTMLDivElement; const cMouseDownEvent = new CustomEvent('mousedown'); const bodyMouseMoveEvent = new CustomEvent('mousemove'); Object.defineProperty(bodyMouseMoveEvent, 'target', { writable: true, value: resizeHandleElm }); Object.defineProperty(cMouseDownEvent, 'pageX', { writable: true, value: 80 }); Object.defineProperty(cMouseDownEvent, 'pageY', { writable: true, value: 12 }); - Object.defineProperty(bodyMouseMoveEvent, 'pageX', { writable: true, value: 140 }); + Object.defineProperty(bodyMouseMoveEvent, 'pageX', { writable: true, value: 1000 }); Object.defineProperty(bodyMouseMoveEvent, 'pageY', { writable: true, value: 13 }); - // Simulate pointer inside grid, not at browser edge Object.defineProperty(bodyMouseMoveEvent, 'clientX', { writable: true, value: 400 }); resizeHandleElm.dispatchEvent(cMouseDownEvent); container.dispatchEvent(cMouseDownEvent); document.body.dispatchEvent(bodyMouseMoveEvent); - // Advance timers to trigger the accelerated auto-scroll branch - vi.advanceTimersByTime(200); - expect(onColumnsDragSpy).toHaveBeenCalled(); + expect(columns[1].width).toBe(720); + expect(onColumnsDragSpy).toHaveBeenCalledTimes(1); + + vi.advanceTimersByTime(30); + expect(columns[1].width).toBe(730); + expect(onColumnsDragSpy).toHaveBeenCalledTimes(2); document.body.dispatchEvent(new CustomEvent('mouseup')); }); diff --git a/packages/common/src/core/slickGrid.ts b/packages/common/src/core/slickGrid.ts index 7b591ea38..73af2b6e4 100755 --- a/packages/common/src/core/slickGrid.ts +++ b/packages/common/src/core/slickGrid.ts @@ -112,9 +112,8 @@ import { applyHtmlToElement, runOptionalHtmlSanitizer } from './utils.js'; // body scroll range (header width is the header/body scroll-sync floor) and column // drag-reorder has room past the last column const HEADER_WIDTH_SLACK = 1000; -const RESIZE_AUTOSCROLL_MIN_INTERVAL_MS = 30; -const RESIZE_AUTOSCROLL_MAX_INTERVAL_MS = 600; -const RESIZE_AUTOSCROLL_ACCELERATE_INTERVAL = 5; +const COLUMN_AUTOSCROLL_DISTANCE_PX = 10; +const COLUMN_AUTOSCROLL_INTERVAL_MS = 30; const RESIZE_AUTOSCROLL_BROWSER_EDGE_PX = 1; const RESIZE_AUTOSCROLL_BROWSER_EDGE_LEFT_DELAY_MS = 300; const RESIZE_AUTOSCROLL_BROWSER_EDGE_RIGHT_DELAY_MS = 1200; @@ -2180,7 +2179,10 @@ export class SlickGrid = Column, O e stopAutoScroll(); columnScrollDirection = direction; if (direction) { - columnScrollTimer = setInterval(() => (this._viewportScrollContainerX.scrollLeft += direction * 10), 30); + columnScrollTimer = setInterval( + () => (this._viewportScrollContainerX.scrollLeft += direction * COLUMN_AUTOSCROLL_DISTANCE_PX), + COLUMN_AUTOSCROLL_INTERVAL_MS + ); } } } @@ -2304,44 +2306,33 @@ export class SlickGrid = Column, O e if (this._columnResizeAutoScrollTimer) { return; } - let elapsed = 0; this._columnResizeAutoScrollTimer = setInterval(() => { - const isBrowserEdge = Math.abs(autoScrollOffsetX) === 1; const viewportOffset = getOffset(this._viewportScrollContainerX); - const moveDistance = isBrowserEdge ? 4 * autoScrollOffsetX : (this.getAbsoluteColumnMinWidth() / 2) * Math.sign(autoScrollOffsetX); - /* v8 ignore next 5 */ - if (!isBrowserEdge) { - const delay = RESIZE_AUTOSCROLL_MAX_INTERVAL_MS - Math.abs(autoScrollOffsetX) * RESIZE_AUTOSCROLL_ACCELERATE_INTERVAL; - elapsed += RESIZE_AUTOSCROLL_MIN_INTERVAL_MS; - if (elapsed < delay) { - return; - } - elapsed = 0; - } /* v8 ignore next */ const targetPageX = autoScrollOffsetX > 0 - ? viewportOffset.left + this._viewportScrollContainerX.clientWidth + moveDistance - : viewportOffset.left - moveDistance; + ? viewportOffset.left + this._viewportScrollContainerX.clientWidth + COLUMN_AUTOSCROLL_DISTANCE_PX + : viewportOffset.left - COLUMN_AUTOSCROLL_DISTANCE_PX; resizeCallback(targetPageX + resizeAutoScrollDeltaX); - }, RESIZE_AUTOSCROLL_MIN_INTERVAL_MS); + }, COLUMN_AUTOSCROLL_INTERVAL_MS); }; const updateColumnResizeAutoScroll = ( clientX: number | undefined, targetPageX: number, resizeCallback: (targetPageX: number) => void - ) => { + ): number => { // TODO: there is a known bug with auto-scroll in RTL, // so disable it until someone can contribute a fix - if (this._options.rtl) { + if (this._options.rtl || !this._options.autoScrollOnColumnResize) { stopColumnResizeAutoScroll(); - return; + return targetPageX; } autoScrollClientX = isDefinedNumber(clientX) ? clientX : autoScrollClientX; const left = getOffset(this._viewportScrollContainerX).left; - const right = left + this._viewportScrollContainerX.clientWidth; + const viewportWidth = this._viewportScrollContainerX.clientWidth; + const right = left + viewportWidth; const browserW = window.innerWidth || document.documentElement.clientWidth || 0; if (targetPageX <= left) { autoScrollOffsetX = targetPageX - left; @@ -2359,9 +2350,10 @@ export class SlickGrid = Column, O e } if (autoScrollOffsetX) { scheduleColumnResizeAutoScroll(resizeCallback); - } else { - stopColumnResizeAutoScroll(); + return autoScrollOffsetX > 0 && viewportWidth ? Math.min(right, targetPageX) : targetPageX; } + stopColumnResizeAutoScroll(); + return targetPageX; }; for (let i = 0; i < children.length; i++) { @@ -2671,9 +2663,9 @@ export class SlickGrid = Column, O e }, onResize: (e, resizeElms) => { const targetEvent = (e as TouchEvent).touches ? (e as TouchEvent).changedTouches[0] : e; - const targetPageX = (targetEvent as MouseEvent).pageX; + let targetPageX = (targetEvent as MouseEvent).pageX; if (!(this.hasFrozenColumns() && i <= this._options.frozenColumn!)) { - updateColumnResizeAutoScroll((targetEvent as MouseEvent).clientX, targetPageX, (resizePageX) => + targetPageX = updateColumnResizeAutoScroll((targetEvent as MouseEvent).clientX, targetPageX, (resizePageX) => applyColumnResize(resizePageX, resizeElms) ); }