From 565107eac05fdc2d2a62df81306ff35c8ba325b8 Mon Sep 17 00:00:00 2001 From: Rahul Yadav <52163880+rahulyadav5524@users.noreply.github.com> Date: Mon, 1 Jul 2024 14:46:46 +0530 Subject: [PATCH 1/4] Update release.js.yml --- .github/workflows/release.js.yml | 72 ++++++++++++++++---------------- 1 file changed, 36 insertions(+), 36 deletions(-) diff --git a/.github/workflows/release.js.yml b/.github/workflows/release.js.yml index 4924ac7b0..e49555160 100644 --- a/.github/workflows/release.js.yml +++ b/.github/workflows/release.js.yml @@ -28,39 +28,39 @@ jobs: token: ${{ secrets.NPM_TOKEN }} access: public package: ./packages/core/package.json - publish-cells: - defaults: - run: - working-directory: ./packages/cells - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v4 - - uses: actions/setup-node@v4 - with: - node-version: 20.10.0 - - name: Bootstrap - run: npm install && npm run build - working-directory: . - - uses: JS-DevTools/npm-publish@v1 - with: - token: ${{ secrets.NPM_TOKEN }} - access: public - package: ./packages/cells/package.json - publish-source: - defaults: - run: - working-directory: ./packages/source - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v4 - - uses: actions/setup-node@v4 - with: - node-version: 20.10.0 - - name: Bootstrap - run: npm install && npm run build - working-directory: . - - uses: JS-DevTools/npm-publish@v1 - with: - token: ${{ secrets.NPM_TOKEN }} - access: public - package: ./packages/source/package.json + # publish-cells: + # defaults: + # run: + # working-directory: ./packages/cells + # runs-on: ubuntu-latest + # steps: + # - uses: actions/checkout@v4 + # - uses: actions/setup-node@v4 + # with: + # node-version: 20.10.0 + # - name: Bootstrap + # run: npm install && npm run build + # working-directory: . + # - uses: JS-DevTools/npm-publish@v1 + # with: + # token: ${{ secrets.NPM_TOKEN }} + # access: public + # package: ./packages/cells/package.json + # publish-source: + # defaults: + # run: + # working-directory: ./packages/source + # runs-on: ubuntu-latest + # steps: + # - uses: actions/checkout@v4 + # - uses: actions/setup-node@v4 + # with: + # node-version: 20.10.0 + # - name: Bootstrap + # run: npm install && npm run build + # working-directory: . + # - uses: JS-DevTools/npm-publish@v1 + # with: + # token: ${{ secrets.NPM_TOKEN }} + # access: public + # package: ./packages/source/package.json From 8f94f6f23ee8c355043caee314473baf67be5ded Mon Sep 17 00:00:00 2001 From: Rahul Yadav Date: Mon, 1 Jul 2024 17:14:26 +0530 Subject: [PATCH 2/4] Added feature to right side freeze columnj --- packages/core/src/data-editor/data-editor.tsx | 69 ++++++++++---- .../docs/examples/freeze-columns.stories.tsx | 16 +++- .../core/src/internal/data-grid/data-grid.tsx | 95 +++++++++++++++---- .../image-window-loader-interface.ts | 2 +- .../data-grid/render/data-grid-lib.ts | 81 ++++++++++++---- .../data-grid/render/data-grid-render.blit.ts | 52 ++++++---- .../render/data-grid-render.cells.ts | 4 + .../render/data-grid-render.header.ts | 5 +- .../render/data-grid-render.lines.ts | 38 +++++++- .../data-grid/render/data-grid-render.ts | 46 +++++++-- .../data-grid/render/data-grid-render.walk.ts | 15 ++- .../render/data-grid.render.rings.ts | 13 ++- .../data-grid/render/draw-grid-arg.ts | 2 +- .../scrolling-data-grid.tsx | 14 ++- 14 files changed, 341 insertions(+), 111 deletions(-) diff --git a/packages/core/src/data-editor/data-editor.tsx b/packages/core/src/data-editor/data-editor.tsx index 4736b1099..c8d880f24 100644 --- a/packages/core/src/data-editor/data-editor.tsx +++ b/packages/core/src/data-editor/data-editor.tsx @@ -884,6 +884,9 @@ const DataEditorImpl: React.ForwardRefRenderFunction { if (typeof window === "undefined") return { fontSize: "16px" }; return window.getComputedStyle(document.documentElement); @@ -1533,9 +1536,13 @@ const DataEditorImpl: React.ForwardRefRenderFunction= mangledCols.length - freezeRightColumns; i--) { + frozenRightWidth += columns[i].width; } let trailingRowHeight = 0; const freezeTrailingRowsEffective = freezeTrailingRows + (lastRowSticky ? 1 : 0); @@ -1548,8 +1555,8 @@ const DataEditorImpl: React.ForwardRefRenderFunction= mangledCols.length - freezeRightColumns)) + ) { scrollX = 0; } else if ( dir === "horizontal" || @@ -1623,7 +1634,9 @@ const DataEditorImpl: React.ForwardRefRenderFunction 0) { freezeRegions.push({ x: region.x - rowMarkerOffset, @@ -2488,11 +2512,20 @@ const DataEditorImpl: React.ForwardRefRenderFunction 0) { + if (freezeLeftColumns > 0) { freezeRegions.push({ x: 0, y: rows - freezeTrailingRows, - width: freezeColumns, + width: freezeLeftColumns, + height: freezeTrailingRows, + }); + } + + if (freezeRightColumns > 0) { + freezeRegions.push({ + x: columns.length - freezeRightColumns, + y: rows - freezeTrailingRows, + width: freezeRightColumns, height: freezeTrailingRows, }); } @@ -2507,7 +2540,7 @@ const DataEditorImpl: React.ForwardRefRenderFunction = (p: { freezeColumns: number }) => { +export const FreezeColumns: React.VFC = (p: { freezeLeftColumns: number, freezeRightColumns: number }) => { const { cols, getCellContent } = useMockDataGenerator(100); return ( = (p: { freezeColumns: number }) => { ); }; (FreezeColumns as any).argTypes = { - freezeColumns: { + freezeLeftColumns: { + control: { + type: "range", + min: 0, + max: 10, + }, + }, + freezeRightColumns: { control: { type: "range", min: 0, @@ -55,5 +62,6 @@ export const FreezeColumns: React.VFC = (p: { freezeColumns: number }) => { }, }; (FreezeColumns as any).args = { - freezeColumns: 1, + freezeLeftColumns: 1, + freezeRightColumns: 1, }; diff --git a/packages/core/src/internal/data-grid/data-grid.tsx b/packages/core/src/internal/data-grid/data-grid.tsx index 52338a786..06883b837 100644 --- a/packages/core/src/internal/data-grid/data-grid.tsx +++ b/packages/core/src/internal/data-grid/data-grid.tsx @@ -70,7 +70,7 @@ export interface DataGridProps { readonly accessibilityHeight: number; - readonly freezeColumns: number; + readonly freezeColumns: number | [left: number, right: number]; readonly freezeTrailingRows: number; readonly hasAppendRow: boolean; readonly firstColAccessible: boolean; @@ -393,7 +393,9 @@ const DataGrid: React.ForwardRefRenderFunction = (p, } = p; const translateX = p.translateX ?? 0; const translateY = p.translateY ?? 0; - const cellXOffset = Math.max(freezeColumns, Math.min(columns.length - 1, cellXOffsetReal)); + const freezeLeftColumns = typeof freezeColumns === "number" ? freezeColumns : freezeColumns[0]; + const freezeRightColumns = typeof freezeColumns === "number" ? 0 : freezeColumns[1]; + const cellXOffset = Math.max(freezeLeftColumns, Math.min(columns.length - 1, cellXOffsetReal)); const ref = React.useRef(null); const windowEventTargetRef = React.useRef(window); @@ -440,7 +442,10 @@ const DataGrid: React.ForwardRefRenderFunction = (p, }, [cellYOffset, cellXOffset, translateX, translateY, enableFirefoxRescaling, enableSafariRescaling]); const mappedColumns = useMappedColumns(columns, freezeColumns); - const stickyX = fixedShadowX ? getStickyWidth(mappedColumns, dragAndDropState) : 0; + const stickyX = React.useMemo( + () => (fixedShadowX ? getStickyWidth(mappedColumns, dragAndDropState) : [0, 0]), + [fixedShadowX, mappedColumns, dragAndDropState] + ); // row: -1 === columnHeader, -2 === groupHeader const getBoundsForItem = React.useCallback( @@ -465,7 +470,7 @@ const DataGrid: React.ForwardRefRenderFunction = (p, translateX, translateY, rows, - freezeColumns, + freezeLeftColumns, freezeTrailingRows, mappedColumns, rowHeight @@ -493,7 +498,7 @@ const DataGrid: React.ForwardRefRenderFunction = (p, translateX, translateY, rows, - freezeColumns, + freezeLeftColumns, freezeTrailingRows, mappedColumns, rowHeight, @@ -508,7 +513,14 @@ const DataGrid: React.ForwardRefRenderFunction = (p, const y = (posY - rect.top) / scale; const edgeDetectionBuffer = 5; - const effectiveCols = getEffectiveColumns(mappedColumns, cellXOffset, width, undefined, translateX); + const effectiveCols = getEffectiveColumns( + mappedColumns, + cellXOffset, + width, + freezeColumns, + undefined, + translateX + ); let button = 0; let buttons = 0; @@ -518,7 +530,7 @@ const DataGrid: React.ForwardRefRenderFunction = (p, } // -1 === off right edge - const col = getColumnIndexForX(x, effectiveCols, translateX); + const col = getColumnIndexForX(x, effectiveCols, freezeColumns, width, translateX); // -1: header or above // undefined: offbottom @@ -686,6 +698,7 @@ const DataGrid: React.ForwardRefRenderFunction = (p, fillHandle, selection, totalHeaderHeight, + freezeColumns, ] ); @@ -1717,7 +1730,14 @@ const DataGrid: React.ForwardRefRenderFunction = (p, const accessibilityTree = useDebouncedMemo( () => { if (width < 50 || experimental?.disableAccessibilityTree === true) return null; - let effectiveCols = getEffectiveColumns(mappedColumns, cellXOffset, width, dragAndDropState, translateX); + let effectiveCols = getEffectiveColumns( + mappedColumns, + cellXOffset, + width, + freezeColumns, + dragAndDropState, + translateX + ); const colOffset = firstColAccessible ? 0 : -1; if (!firstColAccessible && effectiveCols[0]?.sourceIndex === 0) { effectiveCols = effectiveCols.slice(1); @@ -1851,33 +1871,59 @@ const DataGrid: React.ForwardRefRenderFunction = (p, onKeyDown, getBoundsForItem, onCellFocused, + freezeColumns, ], 200 ); - const opacityX = - freezeColumns === 0 || !fixedShadowX ? 0 : cellXOffset > freezeColumns ? 1 : clamp(-translateX / 100, 0, 1); + const opacityXLeft = + freezeLeftColumns === 0 || !fixedShadowX + ? 0 + : cellXOffset > freezeLeftColumns + ? 1 + : clamp(-translateX / 100, 0, 1); + + const opacityXRight = + freezeRightColumns === 0 || !fixedShadowX + ? 0 + : cellXOffset + width < columns.length - freezeRightColumns + ? 1 + : clamp((translateX - (columns.length - freezeRightColumns - width) * 32) / 100, 0, 1); const absoluteOffsetY = -cellYOffset * 32 + translateY; const opacityY = !fixedShadowY ? 0 : clamp(-absoluteOffsetY / 100, 0, 1); const stickyShadow = React.useMemo(() => { - if (!opacityX && !opacityY) { + if (!opacityXLeft && !opacityY && !opacityXRight) { return null; } - const styleX: React.CSSProperties = { + const transition = "opacity 0.2s"; + + const styleXLeft: React.CSSProperties = { position: "absolute", top: 0, - left: stickyX, - width: width - stickyX, + left: stickyX[0], + width: width - stickyX[0], height: height, - opacity: opacityX, + opacity: opacityXLeft, pointerEvents: "none", - transition: !smoothScrollX ? "opacity 0.2s" : undefined, + transition: !smoothScrollX ? transition : undefined, boxShadow: "inset 13px 0 10px -13px rgba(0, 0, 0, 0.2)", }; + const styleXRight: React.CSSProperties = { + position: "absolute", + top: 0, + right: stickyX[1], + width: width - stickyX[1], + height: height, + opacity: opacityXRight, + pointerEvents: "none", + transition: !smoothScrollX ? transition : undefined, + boxShadow: "inset -13px 0 10px -13px rgba(0, 0, 0, 0.2)", + }; + const styleY: React.CSSProperties = { position: "absolute", top: totalHeaderHeight, @@ -1886,17 +1932,28 @@ const DataGrid: React.ForwardRefRenderFunction = (p, height: height, opacity: opacityY, pointerEvents: "none", - transition: !smoothScrollY ? "opacity 0.2s" : undefined, + transition: !smoothScrollY ? transition : undefined, boxShadow: "inset 0 13px 10px -13px rgba(0, 0, 0, 0.2)", }; return ( <> - {opacityX > 0 &&
} + {opacityXLeft > 0 &&
} + {opacityXRight > 0 &&
} {opacityY > 0 &&
} ); - }, [opacityX, opacityY, stickyX, width, smoothScrollX, totalHeaderHeight, height, smoothScrollY]); + }, [ + opacityXLeft, + opacityY, + stickyX, + width, + smoothScrollX, + totalHeaderHeight, + height, + smoothScrollY, + opacityXRight, + ]); const overlayStyle = React.useMemo( () => ({ diff --git a/packages/core/src/internal/data-grid/image-window-loader-interface.ts b/packages/core/src/internal/data-grid/image-window-loader-interface.ts index e32ef93d1..6495a1ca3 100644 --- a/packages/core/src/internal/data-grid/image-window-loader-interface.ts +++ b/packages/core/src/internal/data-grid/image-window-loader-interface.ts @@ -3,7 +3,7 @@ import type { Rectangle } from "./data-grid-types.js"; /** @category Types */ export interface ImageWindowLoader { - setWindow(newWindow: Rectangle, freezeCols: number, freezeRows: number[]): void; + setWindow(newWindow: Rectangle, freezeCols: number | [left: number, right: number], freezeRows: number[]): void; loadOrGetImage(url: string, col: number, row: number): HTMLImageElement | ImageBitmap | undefined; setCallback(imageLoaded: (locations: CellSet) => void): void; } diff --git a/packages/core/src/internal/data-grid/render/data-grid-lib.ts b/packages/core/src/internal/data-grid/render/data-grid-lib.ts index 5b5d7b310..4ff5025c3 100644 --- a/packages/core/src/internal/data-grid/render/data-grid-lib.ts +++ b/packages/core/src/internal/data-grid/render/data-grid-lib.ts @@ -17,12 +17,16 @@ import type { FullyDefined } from "../../../common/support.js"; export interface MappedGridColumn extends FullyDefined { sourceIndex: number; sticky: boolean; + stickyPosition: "left" | "right" | undefined; } export function useMappedColumns( columns: readonly InnerGridColumn[], - freezeColumns: number + freezeColumns: number | [left: number, right: number] ): readonly MappedGridColumn[] { + const freezeColumnsLeft = typeof freezeColumns === "number" ? freezeColumns : freezeColumns[0]; + const freezeColumnsRight = typeof freezeColumns === "number" ? 0 : freezeColumns[1]; + return React.useMemo( () => columns.map( @@ -35,7 +39,9 @@ export function useMappedColumns( menuIcon: c.menuIcon, overlayIcon: c.overlayIcon, sourceIndex: i, - sticky: i < freezeColumns, + sticky: i < freezeColumnsLeft || i >= columns.length - freezeColumnsRight, + stickyPosition: + i < freezeColumnsLeft ? "left" : i >= columns.length - freezeColumnsRight ? "right" : undefined, indicatorIcon: c.indicatorIcon, style: c.style, themeOverride: c.themeOverride, @@ -50,7 +56,7 @@ export function useMappedColumns( headerRowMarkerDisabled: c.headerRowMarkerDisabled, }) ), - [columns, freezeColumns] + [columns, freezeColumnsLeft, freezeColumnsRight] ); } @@ -174,16 +180,25 @@ export function getStickyWidth( src: number; dest: number; } -): number { - let result = 0; +): [left: number, right: number] { + let lWidth = 0; + let rWidth = 0; const remapped = remapForDnDState(columns, dndState); for (let i = 0; i < remapped.length; i++) { const c = remapped[i]; - if (c.sticky) result += c.width; - else break; + if (c.sticky) { + if (c.stickyPosition === "left") lWidth += c.width; + } else break; } - return result; + for (let i = remapped.length - 1; i >= 0; i--) { + const c = remapped[i]; + if (c.sticky) { + if (c.stickyPosition === "right") rWidth += c.width; + } else break; + } + + return [lWidth, rWidth]; } export function getFreezeTrailingHeight( @@ -206,6 +221,7 @@ export function getEffectiveColumns( columns: readonly MappedGridColumn[], cellXOffset: number, width: number, + freezeColumns: number | [left: number, right: number], dndState?: { src: number; dest: number; @@ -214,14 +230,14 @@ export function getEffectiveColumns( ): readonly MappedGridColumn[] { const mappedCols = remapForDnDState(columns, dndState); + const freezeLeftColumns = typeof freezeColumns === "number" ? freezeColumns : freezeColumns[0]; + const freezeRightColumns = typeof freezeColumns === "number" ? 0 : freezeColumns[1]; + const sticky: MappedGridColumn[] = []; - for (const c of mappedCols) { - if (c.sticky) { - sticky.push(c); - } else { - break; - } + for (let i = 0; i < freezeLeftColumns; i++) { + sticky.push(mappedCols[i]); } + if (sticky.length > 0) { for (const c of sticky) { width -= c.width; @@ -242,22 +258,42 @@ export function getEffectiveColumns( } } + for (let i = mappedCols.length - freezeRightColumns; i < mappedCols.length; i++) { + sticky.push(mappedCols[i]); + } + return sticky; } export function getColumnIndexForX( targetX: number, effectiveColumns: readonly MappedGridColumn[], + freezeColumns: number | [left: number, right: number], + width: number, translateX?: number ): number { + const freezeRightColumns = typeof freezeColumns === "number" ? 0 : freezeColumns[1]; + + let y = width; + for (let fc = 0; fc < freezeRightColumns; fc++) { + const colIdx = effectiveColumns.length - 1 - fc; + const col = effectiveColumns[colIdx]; + y -= col.width; + if (targetX <= y) { + return col.sourceIndex; + } + } + let x = 0; - for (const c of effectiveColumns) { + for (let i = 0; i < effectiveColumns.length - freezeRightColumns; i++) { + const c = effectiveColumns[i]; const cx = c.sticky ? x : x + (translateX ?? 0); if (targetX <= cx + c.width) { return c.sourceIndex; } x += c.width; } + return -1; } @@ -760,7 +796,7 @@ export function computeBounds( translateX: number, translateY: number, rows: number, - freezeColumns: number, + freezeColumns: number | [left: number, right: number], freezeTrailingRows: number, mappedColumns: readonly MappedGridColumn[], rowHeight: number | ((index: number) => number) @@ -772,16 +808,19 @@ export function computeBounds( height: 0, }; + const freezeLeftColumns = typeof freezeColumns === "number" ? freezeColumns : freezeColumns[0]; + const freezeRightColumns = typeof freezeColumns === "number" ? 0 : freezeColumns[1]; + if (col >= mappedColumns.length || row >= rows || row < -2 || col < 0) { return result; } const headerHeight = totalHeaderHeight - groupHeaderHeight; - if (col >= freezeColumns) { + if (col >= freezeLeftColumns) { const dir = cellXOffset > col ? -1 : 1; - const freezeWidth = getStickyWidth(mappedColumns); - result.x += freezeWidth + translateX; + const [freezeLeftWidth, freezeRightWidth] = getStickyWidth(mappedColumns); + result.x += freezeLeftWidth + translateX; for (let i = cellXOffset; i !== col; i += dir) { result.x += mappedColumns[dir === 1 ? i : i - 1].width * dir; } @@ -824,8 +863,8 @@ export function computeBounds( end++; } if (!sticky) { - const freezeWidth = getStickyWidth(mappedColumns); - const clip = result.x - freezeWidth; + const [freezeLeftWidth, freezeRightWidth] = getStickyWidth(mappedColumns); + const clip = result.x - freezeLeftWidth; if (clip < 0) { result.x -= clip; result.width += clip; diff --git a/packages/core/src/internal/data-grid/render/data-grid-render.blit.ts b/packages/core/src/internal/data-grid/render/data-grid-render.blit.ts index 4f5600837..a6c9806d2 100644 --- a/packages/core/src/internal/data-grid/render/data-grid-render.blit.ts +++ b/packages/core/src/internal/data-grid/render/data-grid-render.blit.ts @@ -70,7 +70,7 @@ export function blitLastFrame( } deltaX += translateX - last.translateX; - const stickyWidth = getStickyWidth(effectiveCols); + const [stickyLeftWidth, stickyRightWidth] = getStickyWidth(effectiveCols); if (deltaX !== 0 && deltaY !== 0) { return { @@ -81,7 +81,7 @@ export function blitLastFrame( const freezeTrailingRowsHeight = freezeTrailingRows > 0 ? getFreezeTrailingHeight(rows, freezeTrailingRows, getRowHeight) : 0; - const blitWidth = width - stickyWidth - Math.abs(deltaX); + const blitWidth = width - stickyLeftWidth - Math.abs(deltaX) - stickyRightWidth; const blitHeight = height - totalHeaderHeight - freezeTrailingRowsHeight - Math.abs(deltaY) - 1; if (blitWidth > 150 && blitHeight > 150) { @@ -128,22 +128,22 @@ export function blitLastFrame( // blit X if (deltaX > 0) { // pixels moving right - args.sx = stickyWidth * dpr; + args.sx = stickyLeftWidth * dpr; args.sw = blitWidth * dpr; - args.dx = (deltaX + stickyWidth) * dpr; + args.dx = (deltaX + stickyLeftWidth) * dpr; args.dw = blitWidth * dpr; drawRegions.push({ - x: stickyWidth - 1, + x: stickyLeftWidth - 1, y: 0, width: deltaX + 2, // extra width to account for first col not drawing a left side border height: height, }); } else if (deltaX < 0) { // pixels moving left - args.sx = (stickyWidth - deltaX) * dpr; + args.sx = (stickyLeftWidth - deltaX) * dpr; args.sw = blitWidth * dpr; - args.dx = stickyWidth * dpr; + args.dx = stickyLeftWidth * dpr; args.dw = blitWidth * dpr; drawRegions.push({ @@ -157,14 +157,14 @@ export function blitLastFrame( ctx.setTransform(1, 0, 0, 1, 0, 0); if (doubleBuffer) { if ( - stickyWidth > 0 && + stickyLeftWidth > 0 && deltaX !== 0 && deltaY === 0 && (targetScroll === undefined || blitSourceScroll?.[1] !== false) ) { // When double buffering the freeze columns can be offset by a couple pixels vertically between the two // buffers. We don't want to redraw them so we need to make sure to copy them between the buffers. - const w = stickyWidth * dpr; + const w = stickyLeftWidth * dpr; const h = height * dpr; ctx.drawImage(blitSource, 0, 0, w, h, 0, 0, w, h); } @@ -200,7 +200,8 @@ export function blitResizedCol( height: number, totalHeaderHeight: number, effectiveCols: readonly MappedGridColumn[], - resizedIndex: number + resizedIndex: number, + freezeTrailingColumns: number ) { const drawRegions: Rectangle[] = []; @@ -215,18 +216,27 @@ export function blitResizedCol( return drawRegions; } - walkColumns(effectiveCols, cellYOffset, translateX, translateY, totalHeaderHeight, (c, drawX, _drawY, clipX) => { - if (c.sourceIndex === resizedIndex) { - const x = Math.max(drawX, clipX) + 1; - drawRegions.push({ - x, - y: 0, - width: width - x, - height, - }); - return true; + walkColumns( + effectiveCols, + width, + cellYOffset, + translateX, + translateY, + totalHeaderHeight, + freezeTrailingColumns, + (c, drawX, _drawY, clipX) => { + if (c.sourceIndex === resizedIndex) { + const x = Math.max(drawX, clipX) + 1; + drawRegions.push({ + x, + y: 0, + width: width - x, + height, + }); + return true; + } } - }); + ); return drawRegions; } diff --git a/packages/core/src/internal/data-grid/render/data-grid-render.cells.ts b/packages/core/src/internal/data-grid/render/data-grid-render.cells.ts index dc84326c2..fed1f21f4 100644 --- a/packages/core/src/internal/data-grid/render/data-grid-render.cells.ts +++ b/packages/core/src/internal/data-grid/render/data-grid-render.cells.ts @@ -77,6 +77,7 @@ export function drawCells( effectiveColumns: readonly MappedGridColumn[], allColumns: readonly MappedGridColumn[], height: number, + width: number, totalHeaderHeight: number, translateX: number, translateY: number, @@ -90,6 +91,7 @@ export function drawCells( isFocused: boolean, drawFocus: boolean, freezeTrailingRows: number, + freezeTrailingColumns: number, hasAppendRow: boolean, drawRegions: readonly Rectangle[], damage: CellSet | undefined, @@ -124,10 +126,12 @@ export function drawCells( walkColumns( effectiveColumns, + width, cellYOffset, translateX, translateY, totalHeaderHeight, + freezeTrailingColumns, (c, drawX, colDrawStartY, clipX, startRow) => { const diff = Math.max(0, clipX - drawX); diff --git a/packages/core/src/internal/data-grid/render/data-grid-render.header.ts b/packages/core/src/internal/data-grid/render/data-grid-render.header.ts index 5828a4273..d96aa11e6 100644 --- a/packages/core/src/internal/data-grid/render/data-grid-render.header.ts +++ b/packages/core/src/internal/data-grid/render/data-grid-render.header.ts @@ -38,7 +38,8 @@ export function drawGridHeaders( getGroupDetails: GroupDetailsCallback, damage: CellSet | undefined, drawHeaderCallback: DrawHeaderCallback | undefined, - touchMode: boolean + touchMode: boolean, + freezeTrailingColumns: number ) { const totalHeaderHeight = headerHeight + groupHeaderHeight; if (totalHeaderHeight <= 0) return; @@ -54,7 +55,7 @@ export function drawGridHeaders( const font = outerTheme.headerFontFull; // Assinging the context font too much can be expensive, it can be worth it to minimze this ctx.font = font; - walkColumns(effectiveCols, 0, translateX, 0, totalHeaderHeight, (c, x, _y, clipX) => { + walkColumns(effectiveCols, width, 0, translateX, 0, totalHeaderHeight, freezeTrailingColumns, (c, x, _y, clipX) => { if (damage !== undefined && !damage.has([c.sourceIndex, -1])) return; const diff = Math.max(0, clipX - x); ctx.save(); diff --git a/packages/core/src/internal/data-grid/render/data-grid-render.lines.ts b/packages/core/src/internal/data-grid/render/data-grid-render.lines.ts index cd4b4cf8d..c1a58191e 100644 --- a/packages/core/src/internal/data-grid/render/data-grid-render.lines.ts +++ b/packages/core/src/internal/data-grid/render/data-grid-render.lines.ts @@ -26,6 +26,7 @@ export function drawBlanks( selectedRows: CompactSelection, disabledRows: CompactSelection, freezeTrailingRows: number, + freezeTrailingColumns: number, hasAppendRow: boolean, drawRegions: readonly Rectangle[], damage: CellSet | undefined, @@ -41,10 +42,12 @@ export function drawBlanks( walkColumns( effectiveColumns, + width, cellYOffset, translateX, translateY, totalHeaderHeight, + freezeTrailingColumns, (c, drawX, colDrawY, clipX, startRow) => { if (c !== effectiveColumns[effectiveColumns.length - 1]) return; drawX += c.width; @@ -123,18 +126,27 @@ export function overdrawStickyBoundaries( } const hColor = theme.horizontalBorderColor ?? theme.borderColor; const vColor = theme.borderColor; - const drawX = drawFreezeBorder ? getStickyWidth(effectiveCols) : 0; + const [drawXLeft, drawXRight] = drawFreezeBorder ? getStickyWidth(effectiveCols) : [0, 0]; let vStroke: string | undefined; - if (drawX !== 0) { + if (drawXLeft !== 0) { vStroke = blendCache(vColor, theme.bgCell); ctx.beginPath(); - ctx.moveTo(drawX + 0.5, 0); - ctx.lineTo(drawX + 0.5, height); + ctx.moveTo(drawXLeft + 0.5, 0); + ctx.lineTo(drawXLeft + 0.5, height); ctx.strokeStyle = vStroke; ctx.stroke(); } + if (drawXRight !== 0) { + const hStroke = vColor === hColor && vStroke !== undefined ? vStroke : blendCache(hColor, theme.bgCell); + ctx.beginPath(); + ctx.moveTo(width - drawXRight + 0.5, 0); + ctx.lineTo(width - drawXRight + 0.5, height); + ctx.strokeStyle = hStroke; + ctx.stroke(); + } + if (freezeTrailingRows > 0) { const hStroke = vColor === hColor && vStroke !== undefined ? vStroke : blendCache(hColor, theme.bgCell); const h = getFreezeTrailingHeight(rows, freezeTrailingRows, getRowHeight); @@ -332,6 +344,24 @@ export function drawGridLines( } } + // let rightX = 0.5; + // for (let index = effectiveCols.length - 1; index >= 0; index--) { + // const c = effectiveCols[index]; + // if (c.width === 0) continue; + // if (!c.sticky) break; + // rightX += c.width; + // const tx = c.sticky ? rightX : rightX + translateX; + // if (tx >= minX && tx <= maxX && verticalBorder(index + 1)) { + // toDraw.push({ + // x1: tx, + // y1: Math.max(groupHeaderHeight, minY), + // x2: tx, + // y2: Math.min(height, maxY), + // color: vColor, + // }); + // } + // } + let freezeY = height + 0.5; for (let i = rows - freezeTrailingRows; i < rows; i++) { const rh = getRowHeight(i); diff --git a/packages/core/src/internal/data-grid/render/data-grid-render.ts b/packages/core/src/internal/data-grid/render/data-grid-render.ts index 4946b1557..754d09c6a 100644 --- a/packages/core/src/internal/data-grid/render/data-grid-render.ts +++ b/packages/core/src/internal/data-grid/render/data-grid-render.ts @@ -33,6 +33,7 @@ function clipHeaderDamage( translateX: number, translateY: number, cellYOffset: number, + freezeTrailingColumns: number, damage: CellSet | undefined ): void { if (damage === undefined || damage.size === 0) return; @@ -53,10 +54,12 @@ function clipHeaderDamage( walkColumns( effectiveColumns, + width, cellYOffset, translateX, translateY, totalHeaderHeight, + freezeTrailingColumns, (c, drawX, _colDrawY, clipX) => { const diff = Math.max(0, clipX - drawX); @@ -73,6 +76,7 @@ function clipHeaderDamage( function getLastRow( effectiveColumns: readonly MappedGridColumn[], height: number, + width: number, totalHeaderHeight: number, translateX: number, translateY: number, @@ -80,15 +84,18 @@ function getLastRow( rows: number, getRowHeight: (row: number) => number, freezeTrailingRows: number, - hasAppendRow: boolean + hasAppendRow: boolean, + freezeTrailingColumns: number ): number { let result = 0; walkColumns( effectiveColumns, + width, cellYOffset, translateX, translateY, totalHeaderHeight, + freezeTrailingColumns, (_c, __drawX, colDrawY, _clipX, startRow) => { walkRowsInCol( startRow, @@ -171,6 +178,9 @@ export function drawGrid(arg: DrawGridArg, lastArg: DrawGridArg | undefined) { const doubleBuffer = renderStrategy === "double-buffer"; const dpr = Math.min(maxScaleFactor, Math.ceil(window.devicePixelRatio ?? 1)); + const freezeLeftColumns = typeof freezeColumns === "number" ? freezeColumns : freezeColumns[0]; + const freezeRightColumns = typeof freezeColumns === "number" ? 0 : freezeColumns[1]; + // if we are double buffering we need to make sure we can blit. If we can't we need to redraw the whole thing const canBlit = renderStrategy !== "direct" && computeCanBlit(arg, lastArg); @@ -253,7 +263,14 @@ export function drawGrid(arg: DrawGridArg, lastArg: DrawGridArg | undefined) { targetCtx.scale(dpr, dpr); } - const effectiveCols = getEffectiveColumns(mappedColumns, cellXOffset, width, dragAndDropState, translateX); + const effectiveCols = getEffectiveColumns( + mappedColumns, + cellXOffset, + width, + freezeColumns, + dragAndDropState, + translateX + ); let drawRegions: Rectangle[] = []; @@ -287,7 +304,8 @@ export function drawGrid(arg: DrawGridArg, lastArg: DrawGridArg | undefined) { getGroupDetails, damage, drawHeaderCallback, - touchMode + touchMode, + freezeRightColumns ); drawGridLines( @@ -357,6 +375,7 @@ export function drawGrid(arg: DrawGridArg, lastArg: DrawGridArg | undefined) { getRowHeight, getCellContent, freezeTrailingRows, + freezeRightColumns, hasAppendRow, fillHandle, rows @@ -383,13 +402,13 @@ export function drawGrid(arg: DrawGridArg, lastArg: DrawGridArg | undefined) { { x: 0, y: cellYOffset, - width: freezeColumns, + width: freezeLeftColumns, height: 300, }, { x: 0, y: -2, - width: freezeColumns, + width: freezeLeftColumns, height: 2, }, { @@ -407,6 +426,7 @@ export function drawGrid(arg: DrawGridArg, lastArg: DrawGridArg | undefined) { effectiveCols, mappedColumns, height, + width, totalHeaderHeight, translateX, translateY, @@ -420,6 +440,7 @@ export function drawGrid(arg: DrawGridArg, lastArg: DrawGridArg | undefined) { isFocused, drawFocus, freezeTrailingRows, + freezeRightColumns, hasAppendRow, drawRegions, damage, @@ -463,6 +484,7 @@ export function drawGrid(arg: DrawGridArg, lastArg: DrawGridArg | undefined) { getRowHeight, getCellContent, freezeTrailingRows, + freezeRightColumns, hasAppendRow, fillHandle, rows @@ -491,6 +513,7 @@ export function drawGrid(arg: DrawGridArg, lastArg: DrawGridArg | undefined) { translateX, translateY, cellYOffset, + freezeRightColumns, damage ); drawHeaderTexture(); @@ -550,7 +573,8 @@ export function drawGrid(arg: DrawGridArg, lastArg: DrawGridArg | undefined) { height, totalHeaderHeight, effectiveCols, - resizedCol + resizedCol, + freezeRightColumns ); } @@ -602,6 +626,7 @@ export function drawGrid(arg: DrawGridArg, lastArg: DrawGridArg | undefined) { getRowHeight, getCellContent, freezeTrailingRows, + freezeRightColumns, hasAppendRow, fillHandle, rows @@ -626,6 +651,7 @@ export function drawGrid(arg: DrawGridArg, lastArg: DrawGridArg | undefined) { effectiveCols, mappedColumns, height, + width, totalHeaderHeight, translateX, translateY, @@ -639,6 +665,7 @@ export function drawGrid(arg: DrawGridArg, lastArg: DrawGridArg | undefined) { isFocused, drawFocus, freezeTrailingRows, + freezeRightColumns, hasAppendRow, drawRegions, damage, @@ -675,6 +702,7 @@ export function drawGrid(arg: DrawGridArg, lastArg: DrawGridArg | undefined) { selection.rows, disabledRows, freezeTrailingRows, + freezeRightColumns, hasAppendRow, drawRegions, damage, @@ -723,7 +751,7 @@ export function drawGrid(arg: DrawGridArg, lastArg: DrawGridArg | undefined) { focusRedraw?.(); if (isResizing && resizeIndicator !== "none") { - walkColumns(effectiveCols, 0, translateX, 0, totalHeaderHeight, (c, x) => { + walkColumns(effectiveCols, width, 0, translateX, 0, totalHeaderHeight, freezeRightColumns, (c, x) => { if (c.sourceIndex === resizeCol) { drawColumnResizeOutline( overlayCtx, @@ -756,6 +784,7 @@ export function drawGrid(arg: DrawGridArg, lastArg: DrawGridArg | undefined) { const lastRowDrawn = getLastRow( effectiveCols, height, + width, totalHeaderHeight, translateX, translateY, @@ -763,7 +792,8 @@ export function drawGrid(arg: DrawGridArg, lastArg: DrawGridArg | undefined) { rows, getRowHeight, freezeTrailingRows, - hasAppendRow + hasAppendRow, + freezeRightColumns ); imageLoader?.setWindow( diff --git a/packages/core/src/internal/data-grid/render/data-grid-render.walk.ts b/packages/core/src/internal/data-grid/render/data-grid-render.walk.ts index dbb3c8867..e62721d67 100644 --- a/packages/core/src/internal/data-grid/render/data-grid-render.walk.ts +++ b/packages/core/src/internal/data-grid/render/data-grid-render.walk.ts @@ -64,16 +64,20 @@ export type WalkColsCallback = ( export function walkColumns( effectiveCols: readonly MappedGridColumn[], + width: number, cellYOffset: number, translateX: number, translateY: number, totalHeaderHeight: number, + freezeTrailingColumns: number, cb: WalkColsCallback ): void { let x = 0; let clipX = 0; // this tracks the total width of sticky cols const drawY = totalHeaderHeight + translateY; - for (const c of effectiveCols) { + + for (let i = 0; i < effectiveCols.length - freezeTrailingColumns; i++) { + const c = effectiveCols[i]; const drawX = c.sticky ? clipX : x + translateX; if (cb(c, drawX, drawY, c.sticky ? 0 : clipX, cellYOffset) === true) { break; @@ -82,6 +86,15 @@ export function walkColumns( x += c.width; clipX += c.sticky ? c.width : 0; } + + x = width; + for (let fc = 0; fc < freezeTrailingColumns; fc++) { + const c = effectiveCols[effectiveCols.length - 1 - fc]; + const drawX = x - c.width; + + x -= c.width; + cb(c, drawX, drawY, clipX, cellYOffset); + } } // this should not be item, it is [startInclusive, endInclusive] diff --git a/packages/core/src/internal/data-grid/render/data-grid.render.rings.ts b/packages/core/src/internal/data-grid/render/data-grid.render.rings.ts index 4f57ab119..0008173ea 100644 --- a/packages/core/src/internal/data-grid/render/data-grid.render.rings.ts +++ b/packages/core/src/internal/data-grid/render/data-grid.render.rings.ts @@ -17,7 +17,7 @@ export function drawHighlightRings( translateX: number, translateY: number, mappedColumns: readonly MappedGridColumn[], - freezeColumns: number, + freezeColumns: number | [left: number, right: number], headerHeight: number, groupHeaderHeight: number, rowHeight: number | ((index: number) => number), @@ -27,19 +27,21 @@ export function drawHighlightRings( theme: FullTheme ): (() => void) | undefined { const highlightRegions = allHighlightRegions?.filter(x => x.style !== "no-outline"); + const freezeLeftColumns = typeof freezeColumns === "number" ? freezeColumns : freezeColumns[0]; + const freezeRightColumns = typeof freezeColumns === "number" ? 0 : freezeColumns[1]; if (highlightRegions === undefined || highlightRegions.length === 0) return undefined; - const freezeLeft = getStickyWidth(mappedColumns); + const [freezeLeft, freezeRight] = getStickyWidth(mappedColumns); const freezeBottom = getFreezeTrailingHeight(rows, freezeTrailingRows, rowHeight); - const splitIndicies = [freezeColumns, 0, mappedColumns.length, rows - freezeTrailingRows] as const; + const splitIndices = [freezeLeftColumns, 0, mappedColumns.length, rows - freezeTrailingRows] as const; const splitLocations = [freezeLeft, 0, width, height - freezeBottom] as const; const drawRects = highlightRegions.map(h => { const r = h.range; const style = h.style ?? "dashed"; - return splitRectIntoRegions(r, splitIndicies, width, height, splitLocations).map(arg => { + return splitRectIntoRegions(r, splitIndices, width, height, splitLocations).map(arg => { const rect = arg.rect; const topLeftBounds = computeBounds( rect.x, @@ -185,6 +187,7 @@ export function drawFillHandle( getRowHeight: (row: number) => number, getCellContent: (cell: Item) => InnerGridCell, freezeTrailingRows: number, + freezeTrailingColumns: number, hasAppendRow: boolean, fillHandle: boolean, rows: number @@ -216,10 +219,12 @@ export function drawFillHandle( walkColumns( effectiveCols, + width, cellYOffset, translateX, translateY, totalHeaderHeight, + freezeTrailingColumns, (col, drawX, colDrawY, clipX, startRow) => { clipX; if (col.sticky && targetCol > col.sourceIndex) return; diff --git a/packages/core/src/internal/data-grid/render/draw-grid-arg.ts b/packages/core/src/internal/data-grid/render/draw-grid-arg.ts index 9221f91f7..fbb45eeb2 100644 --- a/packages/core/src/internal/data-grid/render/draw-grid-arg.ts +++ b/packages/core/src/internal/data-grid/render/draw-grid-arg.ts @@ -39,7 +39,7 @@ export interface DrawGridArg { readonly translateY: number; readonly mappedColumns: readonly MappedGridColumn[]; readonly enableGroups: boolean; - readonly freezeColumns: number; + readonly freezeColumns: number | [left: number, right: number]; readonly dragAndDropState: DragAndDropState | undefined; readonly theme: FullTheme; readonly headerHeight: number; diff --git a/packages/core/src/internal/scrolling-data-grid/scrolling-data-grid.tsx b/packages/core/src/internal/scrolling-data-grid/scrolling-data-grid.tsx index 1013c7ea9..f935b1c01 100644 --- a/packages/core/src/internal/scrolling-data-grid/scrolling-data-grid.tsx +++ b/packages/core/src/internal/scrolling-data-grid/scrolling-data-grid.tsx @@ -102,6 +102,9 @@ const GridScroller: React.FunctionComponent = p => { const lastY = React.useRef(); const lastSize = React.useRef(); + const freezeLeftColumns = typeof freezeColumns === "number" ? freezeColumns : freezeColumns[0]; + const freezeRightColumns = typeof freezeColumns === "number"? 0 : freezeColumns[1]; + const width = nonGrowWidth + Math.max(0, overscrollX ?? 0); let height = enableGroups ? headerHeight + groupHeaderHeight : headerHeight; @@ -130,7 +133,7 @@ const GridScroller: React.FunctionComponent = p => { args.x = args.x < 0 ? 0 : args.x; let stickyColWidth = 0; - for (let i = 0; i < freezeColumns; i++) { + for (let i = 0; i < freezeLeftColumns; i++) { stickyColWidth += columns[i].width; } @@ -215,12 +218,7 @@ const GridScroller: React.FunctionComponent = p => { args.height !== lastSize.current?.[1] ) { onVisibleRegionChanged?.( - { - x: cellX, - y: cellY, - width: cellRight - cellX, - height: cellBottom - cellY, - }, + rect, args.width, args.height, args.paddingRight ?? 0, @@ -232,7 +230,7 @@ const GridScroller: React.FunctionComponent = p => { lastY.current = ty; lastSize.current = [args.width, args.height]; } - }, [columns, rowHeight, rows, onVisibleRegionChanged, freezeColumns, smoothScrollX, smoothScrollY]); + }, [columns, rowHeight, rows, onVisibleRegionChanged, freezeLeftColumns, smoothScrollX, smoothScrollY]); const onScrollUpdate = React.useCallback( (args: Rectangle & { paddingRight: number }) => { From cd454e0f76bddcba4f0f3bc7ee922d51f60bac01 Mon Sep 17 00:00:00 2001 From: Rahul Yadav Date: Mon, 1 Jul 2024 17:17:09 +0530 Subject: [PATCH 3/4] revert workflow file --- .github/workflows/release.js.yml | 72 ++++++++++++++++---------------- 1 file changed, 36 insertions(+), 36 deletions(-) diff --git a/.github/workflows/release.js.yml b/.github/workflows/release.js.yml index e49555160..4924ac7b0 100644 --- a/.github/workflows/release.js.yml +++ b/.github/workflows/release.js.yml @@ -28,39 +28,39 @@ jobs: token: ${{ secrets.NPM_TOKEN }} access: public package: ./packages/core/package.json - # publish-cells: - # defaults: - # run: - # working-directory: ./packages/cells - # runs-on: ubuntu-latest - # steps: - # - uses: actions/checkout@v4 - # - uses: actions/setup-node@v4 - # with: - # node-version: 20.10.0 - # - name: Bootstrap - # run: npm install && npm run build - # working-directory: . - # - uses: JS-DevTools/npm-publish@v1 - # with: - # token: ${{ secrets.NPM_TOKEN }} - # access: public - # package: ./packages/cells/package.json - # publish-source: - # defaults: - # run: - # working-directory: ./packages/source - # runs-on: ubuntu-latest - # steps: - # - uses: actions/checkout@v4 - # - uses: actions/setup-node@v4 - # with: - # node-version: 20.10.0 - # - name: Bootstrap - # run: npm install && npm run build - # working-directory: . - # - uses: JS-DevTools/npm-publish@v1 - # with: - # token: ${{ secrets.NPM_TOKEN }} - # access: public - # package: ./packages/source/package.json + publish-cells: + defaults: + run: + working-directory: ./packages/cells + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - uses: actions/setup-node@v4 + with: + node-version: 20.10.0 + - name: Bootstrap + run: npm install && npm run build + working-directory: . + - uses: JS-DevTools/npm-publish@v1 + with: + token: ${{ secrets.NPM_TOKEN }} + access: public + package: ./packages/cells/package.json + publish-source: + defaults: + run: + working-directory: ./packages/source + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - uses: actions/setup-node@v4 + with: + node-version: 20.10.0 + - name: Bootstrap + run: npm install && npm run build + working-directory: . + - uses: JS-DevTools/npm-publish@v1 + with: + token: ${{ secrets.NPM_TOKEN }} + access: public + package: ./packages/source/package.json From 05a0817e79b122a16b8cc84405e831b06ef38765 Mon Sep 17 00:00:00 2001 From: Rahul Yadav Date: Tue, 2 Jul 2024 15:27:12 +0530 Subject: [PATCH 4/4] fixed selection issue and highlight fixes --- packages/core/src/data-editor/data-editor.tsx | 5 +-- .../core/src/internal/data-grid/data-grid.tsx | 7 +++- .../data-grid/render/data-grid-lib.ts | 16 ++++++--- .../data-grid/render/data-grid-render.blit.ts | 15 ++++++-- .../render/data-grid-render.cells.ts | 2 +- .../render/data-grid-render.header.ts | 5 +++ .../render/data-grid-render.lines.ts | 35 ++++++++++--------- .../data-grid/render/data-grid-render.ts | 12 +++++++ .../render/data-grid.render.rings.ts | 9 +++-- .../scrolling-data-grid.tsx | 10 +----- 10 files changed, 77 insertions(+), 39 deletions(-) diff --git a/packages/core/src/data-editor/data-editor.tsx b/packages/core/src/data-editor/data-editor.tsx index c8d880f24..5cabe3958 100644 --- a/packages/core/src/data-editor/data-editor.tsx +++ b/packages/core/src/data-editor/data-editor.tsx @@ -884,7 +884,7 @@ const DataEditorImpl: React.ForwardRefRenderFunction { @@ -1555,7 +1555,8 @@ const DataEditorImpl: React.ForwardRefRenderFunction = (p, let isEdge = bounds !== undefined && bounds.x + bounds.width - posX <= edgeDetectionBuffer; const previousCol = col - 1; - if (posX - bounds.x <= edgeDetectionBuffer && previousCol >= 0) { + if ( + posX - bounds.x <= edgeDetectionBuffer && + previousCol >= 0 && + col < mappedColumns.length - freezeRightColumns + ) { isEdge = true; bounds = getBoundsForItem(canvas, previousCol, row); assert(bounds !== undefined); @@ -699,6 +703,7 @@ const DataGrid: React.ForwardRefRenderFunction = (p, selection, totalHeaderHeight, freezeColumns, + freezeRightColumns, ] ); diff --git a/packages/core/src/internal/data-grid/render/data-grid-lib.ts b/packages/core/src/internal/data-grid/render/data-grid-lib.ts index 4ff5025c3..1bade3211 100644 --- a/packages/core/src/internal/data-grid/render/data-grid-lib.ts +++ b/packages/core/src/internal/data-grid/render/data-grid-lib.ts @@ -279,7 +279,7 @@ export function getColumnIndexForX( const colIdx = effectiveColumns.length - 1 - fc; const col = effectiveColumns[colIdx]; y -= col.width; - if (targetX <= y) { + if (targetX >= y) { return col.sourceIndex; } } @@ -810,6 +810,7 @@ export function computeBounds( const freezeLeftColumns = typeof freezeColumns === "number" ? freezeColumns : freezeColumns[0]; const freezeRightColumns = typeof freezeColumns === "number" ? 0 : freezeColumns[1]; + const column = mappedColumns[col]; if (col >= mappedColumns.length || row >= rows || row < -2 || col < 0) { return result; @@ -817,17 +818,22 @@ export function computeBounds( const headerHeight = totalHeaderHeight - groupHeaderHeight; - if (col >= freezeLeftColumns) { + if (col >= freezeLeftColumns && col < mappedColumns.length - freezeRightColumns) { const dir = cellXOffset > col ? -1 : 1; - const [freezeLeftWidth, freezeRightWidth] = getStickyWidth(mappedColumns); + const [freezeLeftWidth] = getStickyWidth(mappedColumns); result.x += freezeLeftWidth + translateX; for (let i = cellXOffset; i !== col; i += dir) { result.x += mappedColumns[dir === 1 ? i : i - 1].width * dir; } - } else { + } else if (column.stickyPosition === "left") { for (let i = 0; i < col; i++) { result.x += mappedColumns[i].width; } + } else if (column.stickyPosition === "right") { + result.x = width; + for (let i = col; i < mappedColumns.length; i++) { + result.x -= mappedColumns[i].width; + } } result.width = mappedColumns[col].width + 1; @@ -863,7 +869,7 @@ export function computeBounds( end++; } if (!sticky) { - const [freezeLeftWidth, freezeRightWidth] = getStickyWidth(mappedColumns); + const [freezeLeftWidth] = getStickyWidth(mappedColumns); const clip = result.x - freezeLeftWidth; if (clip < 0) { result.x -= clip; diff --git a/packages/core/src/internal/data-grid/render/data-grid-render.blit.ts b/packages/core/src/internal/data-grid/render/data-grid-render.blit.ts index a6c9806d2..dbf777cfd 100644 --- a/packages/core/src/internal/data-grid/render/data-grid-render.blit.ts +++ b/packages/core/src/internal/data-grid/render/data-grid-render.blit.ts @@ -147,9 +147,9 @@ export function blitLastFrame( args.dw = blitWidth * dpr; drawRegions.push({ - x: width + deltaX, + x: width + deltaX - stickyRightWidth, y: 0, - width: -deltaX, + width: -deltaX + stickyRightWidth, height: height, }); } @@ -168,6 +168,17 @@ export function blitLastFrame( const h = height * dpr; ctx.drawImage(blitSource, 0, 0, w, h, 0, 0, w, h); } + if ( + stickyRightWidth > 0 && + deltaX !== 0 && + deltaY === 0 && + (targetScroll === undefined || blitSourceScroll?.[1] !== false) + ) { + const x = (width - stickyRightWidth) * dpr; + const w = stickyRightWidth * dpr; + const h = height * dpr; + ctx.drawImage(blitSource, x, 0, w, h, x, 0, w, h); + } if ( freezeTrailingRowsHeight > 0 && deltaX === 0 && diff --git a/packages/core/src/internal/data-grid/render/data-grid-render.cells.ts b/packages/core/src/internal/data-grid/render/data-grid-render.cells.ts index fed1f21f4..a5cd1a46d 100644 --- a/packages/core/src/internal/data-grid/render/data-grid-render.cells.ts +++ b/packages/core/src/internal/data-grid/render/data-grid-render.cells.ts @@ -299,7 +299,7 @@ export function drawCells( const bgCell = cell.kind === GridCellKind.Protected ? theme.bgCellMedium : theme.bgCell; let fill: string | undefined; - if (isSticky || bgCell !== outerTheme.bgCell) { + if (isSticky || bgCell !== outerTheme.bgCell || c.sticky) { fill = blend(bgCell, fill); } diff --git a/packages/core/src/internal/data-grid/render/data-grid-render.header.ts b/packages/core/src/internal/data-grid/render/data-grid-render.header.ts index d96aa11e6..0240f6455 100644 --- a/packages/core/src/internal/data-grid/render/data-grid-render.header.ts +++ b/packages/core/src/internal/data-grid/render/data-grid-render.header.ts @@ -69,6 +69,11 @@ export function drawGridHeaders( ? outerTheme : mergeAndRealizeTheme(outerTheme, groupTheme, c.themeOverride); + if (c.sticky) { + ctx.fillStyle = theme.bgHeader; + ctx.fill(); + } + if (theme.bgHeader !== outerTheme.bgHeader) { ctx.fillStyle = theme.bgHeader; ctx.fill(); diff --git a/packages/core/src/internal/data-grid/render/data-grid-render.lines.ts b/packages/core/src/internal/data-grid/render/data-grid-render.lines.ts index c1a58191e..a4e0fb8aa 100644 --- a/packages/core/src/internal/data-grid/render/data-grid-render.lines.ts +++ b/packages/core/src/internal/data-grid/render/data-grid-render.lines.ts @@ -331,6 +331,7 @@ export function drawGridLines( for (let index = 0; index < effectiveCols.length; index++) { const c = effectiveCols[index]; if (c.width === 0) continue; + if (c.sticky && c.stickyPosition !== "left") break; x += c.width; const tx = c.sticky ? x : x + translateX; if (tx >= minX && tx <= maxX && verticalBorder(index + 1)) { @@ -344,23 +345,23 @@ export function drawGridLines( } } - // let rightX = 0.5; - // for (let index = effectiveCols.length - 1; index >= 0; index--) { - // const c = effectiveCols[index]; - // if (c.width === 0) continue; - // if (!c.sticky) break; - // rightX += c.width; - // const tx = c.sticky ? rightX : rightX + translateX; - // if (tx >= minX && tx <= maxX && verticalBorder(index + 1)) { - // toDraw.push({ - // x1: tx, - // y1: Math.max(groupHeaderHeight, minY), - // x2: tx, - // y2: Math.min(height, maxY), - // color: vColor, - // }); - // } - // } + let rightX = width + 0.5; + for (let index = effectiveCols.length - 1; index >= 0; index--) { + const c = effectiveCols[index]; + if (c.width === 0) continue; + if (!c.sticky) break; + rightX -= c.width; + const tx = rightX; + if (tx >= minX && tx <= maxX && verticalBorder(index + 1)) { + toDraw.push({ + x1: tx, + y1: Math.max(groupHeaderHeight, minY), + x2: tx, + y2: Math.min(height, maxY), + color: vColor, + }); + } + } let freezeY = height + 0.5; for (let i = rows - freezeTrailingRows; i < rows; i++) { diff --git a/packages/core/src/internal/data-grid/render/data-grid-render.ts b/packages/core/src/internal/data-grid/render/data-grid-render.ts index 754d09c6a..752b62a4e 100644 --- a/packages/core/src/internal/data-grid/render/data-grid-render.ts +++ b/packages/core/src/internal/data-grid/render/data-grid-render.ts @@ -418,6 +418,18 @@ export function drawGrid(arg: DrawGridArg, lastArg: DrawGridArg | undefined) { height: freezeTrailingRows, when: freezeTrailingRows > 0, }, + { + x: viewRegionWidth - freezeRightColumns, + y: cellYOffset, + width: freezeRightColumns, + height: 300, + }, + { + x: viewRegionWidth - freezeRightColumns, + y: -2, + width: freezeRightColumns, + height: 2, + }, ]); const doDamage = (ctx: CanvasRenderingContext2D) => { diff --git a/packages/core/src/internal/data-grid/render/data-grid.render.rings.ts b/packages/core/src/internal/data-grid/render/data-grid.render.rings.ts index 0008173ea..dbd06e453 100644 --- a/packages/core/src/internal/data-grid/render/data-grid.render.rings.ts +++ b/packages/core/src/internal/data-grid/render/data-grid.render.rings.ts @@ -34,8 +34,13 @@ export function drawHighlightRings( const [freezeLeft, freezeRight] = getStickyWidth(mappedColumns); const freezeBottom = getFreezeTrailingHeight(rows, freezeTrailingRows, rowHeight); - const splitIndices = [freezeLeftColumns, 0, mappedColumns.length, rows - freezeTrailingRows] as const; - const splitLocations = [freezeLeft, 0, width, height - freezeBottom] as const; + const splitIndices = [ + freezeLeftColumns, + 0, + mappedColumns.length - freezeRightColumns, + rows - freezeTrailingRows, + ] as const; + const splitLocations = [freezeLeft, 0, width - freezeRight, height - freezeBottom] as const; const drawRects = highlightRegions.map(h => { const r = h.range; diff --git a/packages/core/src/internal/scrolling-data-grid/scrolling-data-grid.tsx b/packages/core/src/internal/scrolling-data-grid/scrolling-data-grid.tsx index f935b1c01..80cccdce3 100644 --- a/packages/core/src/internal/scrolling-data-grid/scrolling-data-grid.tsx +++ b/packages/core/src/internal/scrolling-data-grid/scrolling-data-grid.tsx @@ -103,7 +103,6 @@ const GridScroller: React.FunctionComponent = p => { const lastSize = React.useRef(); const freezeLeftColumns = typeof freezeColumns === "number" ? freezeColumns : freezeColumns[0]; - const freezeRightColumns = typeof freezeColumns === "number"? 0 : freezeColumns[1]; const width = nonGrowWidth + Math.max(0, overscrollX ?? 0); @@ -217,14 +216,7 @@ const GridScroller: React.FunctionComponent = p => { args.width !== lastSize.current?.[0] || args.height !== lastSize.current?.[1] ) { - onVisibleRegionChanged?.( - rect, - args.width, - args.height, - args.paddingRight ?? 0, - tx, - ty - ); + onVisibleRegionChanged?.(rect, args.width, args.height, args.paddingRight ?? 0, tx, ty); last.current = rect; lastX.current = tx; lastY.current = ty;