Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

port-changes - frozen columns #1

Open
wants to merge 30 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 8 commits
Commits
Show all changes
30 commits
Select commit Hold shift + click to select a range
75851b7
port-changes - frozen columns
Matangub Jan 12, 2025
4244830
this fixes the scroll behavior
Matangub Jan 12, 2025
042ac99
small adjustment to the hover color
Matangub Jan 13, 2025
6cef148
removed /lib and /dist from the gitignore file
Matangub Jan 13, 2025
b431e54
Merge branch 'main' into port-changes
Matangub Jan 22, 2025
249c111
update types
Matangub Jan 22, 2025
72028d5
fix attempt
Matangub Jan 22, 2025
2ab2c16
remove changes for this
Matangub Jan 22, 2025
a58c40f
added PaddingInlineEnd and fixed comments
Matangub Jan 28, 2025
6049589
prettier
Matangub Jan 28, 2025
b8ae53f
fixed useMemo dependencies
Matangub Jan 28, 2025
876a7e0
updated ci.yml
Matangub Jan 29, 2025
7867829
name update to trigger workflow again
Matangub Jan 29, 2025
2220b8c
attempt to fix with node version
Matangub Jan 29, 2025
0a53eaf
attempt to fix broken typecheck
Matangub Jan 29, 2025
1f41abc
remove typecheck
Matangub Jan 29, 2025
209c687
attempt to fix ci with legacy peer deps and npmrc
Matangub Jan 29, 2025
af5477b
update strategy to only run for 18
Matangub Jan 29, 2025
64a1ef2
attempt to make the CI work
Matangub Jan 29, 2025
f132378
attempt to skip some steps just to see if it works
Matangub Jan 29, 2025
ea384f7
remove react 18 things
Matangub Jan 29, 2025
d070667
version 1.0.0 and changed description
Matangub Jan 30, 2025
bd3df0b
fix: frozen right columns should not focus on click
Matangub Jan 30, 2025
0383f88
added a CI script to publish it directly to github packages
Matangub Feb 2, 2025
ae3ee11
added workflow_dispatch
Matangub Feb 2, 2025
1c51267
changed version to 1.0.2-beta
Matangub Feb 2, 2025
3ed35c4
update nodejs version
Matangub Feb 2, 2025
4140400
1.0.3-beta version bump
Matangub Feb 2, 2025
b98c6fc
remove github publish workflow
Matangub Feb 5, 2025
790edb5
cr fixes + bump version to 1.0.2
Matangub Feb 5, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
{
"name": "react-data-grid",
"name": "port-custom-react-data-grid",
"version": "7.0.0-beta.47",
"license": "MIT",
"description": "Feature-rich and customizable data grid React component",
Expand Down
7 changes: 5 additions & 2 deletions src/DataGrid.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -324,7 +324,9 @@ function DataGrid<R, SR, K extends Key>(
colOverscanEndIdx,
templateColumns,
layoutCssVars,
totalFrozenColumnWidth
totalFrozenColumnWidth,
rightFrozenColumnCount,
totalRightFrozenColumnWidth
} = useCalculatedColumns({
rawColumns,
defaultColumnOptions,
Expand Down Expand Up @@ -424,7 +426,8 @@ function DataGrid<R, SR, K extends Key>(
rowOverscanEndIdx,
rows,
topSummaryRows,
bottomSummaryRows
bottomSummaryRows,
rightFrozenColumnCount
});

const { gridTemplateColumns, handleColumnResize } = useColumnWidths(
Expand Down
7 changes: 6 additions & 1 deletion src/HeaderCell.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,12 @@ export const resizeHandleClassname = css`
inset-block-start: 0;
inset-inline-end: 0;
inset-block-end: 0;
inline-size: 10px;
inline-size: 4px;

&:hover {
background: var(--rdg-selection-color);
padding: 5px 0;

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's do duplication of 2/4 (so let's use 4px or 6px here)

}
}
`;

Expand Down
57 changes: 46 additions & 11 deletions src/hooks/useCalculatedColumns.ts
Original file line number Diff line number Diff line change
Expand Up @@ -52,13 +52,15 @@ export function useCalculatedColumns<R, SR>({
const defaultResizable = defaultColumnOptions?.resizable ?? false;
const defaultDraggable = defaultColumnOptions?.draggable ?? false;

const { columns, colSpanColumns, lastFrozenColumnIndex, headerRowsCount } = useMemo((): {
const { columns, colSpanColumns, lastFrozenColumnIndex, rightFrozenColumnCount, headerRowsCount } = useMemo((): {
readonly columns: readonly CalculatedColumn<R, SR>[];
readonly colSpanColumns: readonly CalculatedColumn<R, SR>[];
readonly lastFrozenColumnIndex: number;
readonly rightFrozenColumnCount: number;
readonly headerRowsCount: number;
} => {
let lastFrozenColumnIndex = -1;
let rightFrozenColumnCount = 0;
let headerRowsCount = 1;
const columns: MutableCalculatedColumn<R, SR>[] = [];

Expand All @@ -85,13 +87,15 @@ export function useCalculatedColumns<R, SR>({
}

const frozen = rawColumn.frozen ?? false;
const rightFrozen = rawColumn.rightFrozen ?? false;

const column: MutableCalculatedColumn<R, SR> = {
...rawColumn,
parent,
idx: 0,
level: 0,
frozen,
rightFrozen,
width: rawColumn.width ?? defaultWidth,
minWidth: rawColumn.minWidth ?? defaultMinWidth,
maxWidth: rawColumn.maxWidth ?? defaultMaxWidth,
Expand All @@ -107,13 +111,17 @@ export function useCalculatedColumns<R, SR>({
lastFrozenColumnIndex++;
}

if (rightFrozen) {
rightFrozenColumnCount++;
}

if (level > headerRowsCount) {
headerRowsCount = level;
}
}
}

columns.sort(({ key: aKey, frozen: frozenA }, { key: bKey, frozen: frozenB }) => {
columns.sort(({ key: aKey, frozen: frozenA, rightFrozen: rightFrozenA }, { key: bKey, frozen: frozenB, rightFrozen: rightFrozenB }) => {
// Sort select column first:
if (aKey === SELECT_COLUMN_KEY) return -1;
if (bKey === SELECT_COLUMN_KEY) return 1;
Expand All @@ -125,6 +133,13 @@ export function useCalculatedColumns<R, SR>({
}
if (frozenB) return 1;

// Sort right frozen columns second:
if (rightFrozenA) {
if (rightFrozenB) return 0;
return 1;
}
if (rightFrozenB) return -1;

// TODO: sort columns to keep them grouped if they have a parent

// Sort other columns last:
Expand All @@ -145,6 +160,7 @@ export function useCalculatedColumns<R, SR>({
columns,
colSpanColumns,
lastFrozenColumnIndex,
rightFrozenColumnCount,
headerRowsCount
};
}, [
Expand All @@ -158,15 +174,17 @@ export function useCalculatedColumns<R, SR>({
defaultDraggable
]);

const { templateColumns, layoutCssVars, totalFrozenColumnWidth, columnMetrics } = useMemo((): {
const { templateColumns, layoutCssVars, totalFrozenColumnWidth, totalRightFrozenColumnWidth, columnMetrics } = useMemo((): {
templateColumns: readonly string[];
layoutCssVars: Readonly<Record<string, string>>;
totalFrozenColumnWidth: number;
totalRightFrozenColumnWidth: number;
columnMetrics: ReadonlyMap<CalculatedColumn<R, SR>, ColumnMetric>;
} => {
const columnMetrics = new Map<CalculatedColumn<R, SR>, ColumnMetric>();
let left = 0;
let totalFrozenColumnWidth = 0;
let totalRightFrozenColumnWidth = 0;
const templateColumns: string[] = [];

for (const column of columns) {
Expand All @@ -191,24 +209,38 @@ export function useCalculatedColumns<R, SR>({

const layoutCssVars: Record<string, string> = {};

if (rightFrozenColumnCount !== 0) {
let rightEnd = 0;
for (let i = columns.length - 1; i >= columns.length - rightFrozenColumnCount; i--) {
const column = columns[i];
const columnMetric = columnMetrics.get(column)!;
totalRightFrozenColumnWidth += columnMetric.width;
layoutCssVars[`--rdg-frozen-right-${column.idx}`] = `${rightEnd}px`;
rightEnd += columnMetric.width;
}
}



for (let i = 0; i <= lastFrozenColumnIndex; i++) {
const column = columns[i];
layoutCssVars[`--rdg-frozen-left-${column.idx}`] = `${columnMetrics.get(column)!.left}px`;
}

return { templateColumns, layoutCssVars, totalFrozenColumnWidth, columnMetrics };
}, [getColumnWidth, columns, lastFrozenColumnIndex]);
return { templateColumns, layoutCssVars, totalFrozenColumnWidth, totalRightFrozenColumnWidth, columnMetrics };
}, [getColumnWidth, columns, lastFrozenColumnIndex, rightFrozenColumnCount]);

const [colOverscanStartIdx, colOverscanEndIdx] = useMemo((): [number, number] => {
if (!enableVirtualization) {
return [0, columns.length - 1];
}
// get the viewport's left side and right side positions for non-frozen columns
const viewportLeft = scrollLeft + totalFrozenColumnWidth;
const viewportRight = scrollLeft + viewportWidth;
const viewportRight = scrollLeft + viewportWidth - totalRightFrozenColumnWidth;
// get first and last non-frozen column indexes
const lastColIdx = columns.length - 1;
const firstUnfrozenColumnIdx = min(lastFrozenColumnIndex + 1, lastColIdx);
const lastUnfrozonColumnIdx = min(columns.length - rightFrozenColumnCount - 1, lastColIdx);

// skip rendering non-frozen columns if the frozen columns cover the entire viewport
if (viewportLeft >= viewportRight) {
Expand All @@ -217,7 +249,7 @@ export function useCalculatedColumns<R, SR>({

// get the first visible non-frozen column index
let colVisibleStartIdx = firstUnfrozenColumnIdx;
while (colVisibleStartIdx < lastColIdx) {
while (colVisibleStartIdx < lastUnfrozonColumnIdx) {
const { left, width } = columnMetrics.get(columns[colVisibleStartIdx])!;
// if the right side of the columnn is beyond the left side of the available viewport,
// then it is the first column that's at least partially visible
Expand All @@ -229,7 +261,7 @@ export function useCalculatedColumns<R, SR>({

// get the last visible non-frozen column index
let colVisibleEndIdx = colVisibleStartIdx;
while (colVisibleEndIdx < lastColIdx) {
while (colVisibleEndIdx < lastUnfrozonColumnIdx) {
const { left, width } = columnMetrics.get(columns[colVisibleEndIdx])!;
// if the right side of the column is beyond or equal to the right side of the available viewport,
// then it the last column that's at least partially visible, as the previous column's right side is not beyond the viewport.
Expand All @@ -240,7 +272,7 @@ export function useCalculatedColumns<R, SR>({
}

const colOverscanStartIdx = max(firstUnfrozenColumnIdx, colVisibleStartIdx - 1);
const colOverscanEndIdx = min(lastColIdx, colVisibleEndIdx + 1);
const colOverscanEndIdx = min(lastUnfrozonColumnIdx, colVisibleEndIdx + 1);

return [colOverscanStartIdx, colOverscanEndIdx];
}, [
Expand All @@ -249,6 +281,7 @@ export function useCalculatedColumns<R, SR>({
lastFrozenColumnIndex,
scrollLeft,
totalFrozenColumnWidth,
totalRightFrozenColumnWidth,
viewportWidth,
enableVirtualization
]);
Expand All @@ -262,7 +295,9 @@ export function useCalculatedColumns<R, SR>({
layoutCssVars,
headerRowsCount,
lastFrozenColumnIndex,
totalFrozenColumnWidth
totalFrozenColumnWidth,
rightFrozenColumnCount,
totalRightFrozenColumnWidth
};
}

Expand All @@ -283,4 +318,4 @@ function updateColumnParent<R, SR>(
parent.colSpan += 1;
updateColumnParent(parent, index, level - 1);
}
}
}
9 changes: 8 additions & 1 deletion src/hooks/useViewportColumns.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ interface ViewportColumnsArgs<R, SR> {
colOverscanStartIdx: number;
colOverscanEndIdx: number;
lastFrozenColumnIndex: number;
rightFrozenColumnCount: number;
rowOverscanStartIdx: number;
rowOverscanEndIdx: number;
}
Expand All @@ -25,6 +26,7 @@ export function useViewportColumns<R, SR>({
colOverscanStartIdx,
colOverscanEndIdx,
lastFrozenColumnIndex,
rightFrozenColumnCount,
rowOverscanStartIdx,
rowOverscanEndIdx
}: ViewportColumnsArgs<R, SR>) {
Expand Down Expand Up @@ -110,6 +112,11 @@ export function useViewportColumns<R, SR>({
viewportColumns.push(column);
}

for (let colIdx = columns.length - rightFrozenColumnCount; colIdx < columns.length; colIdx++) {
const column = columns[colIdx];
viewportColumns.push(column);
}

return viewportColumns;
}, [startIdx, colOverscanEndIdx, columns]);
}
}
16 changes: 16 additions & 0 deletions src/style/cell.ts
Original file line number Diff line number Diff line change
Expand Up @@ -45,3 +45,19 @@ export const cellFrozen = css`
`;

export const cellFrozenClassname = `rdg-cell-frozen ${cellFrozen}`;


export const cellRightFrozen = css`
@layer rdg.Cell {
position: sticky;
/* Should have a higher value than 0 to show up above unfrozen cells */
z-index: 1;
right: 0;
/* Add box-shadow on the last frozen cell */
&:nth-child(1 of &) {
box-shadow: var(--rdg-cell-right-frozen-box-shadow);
}
}
`;

export const cellRightFrozenClassname = `rdg-cell-frozen ${cellRightFrozen}`;
2 changes: 2 additions & 0 deletions src/style/core.ts
Original file line number Diff line number Diff line change
Expand Up @@ -45,9 +45,11 @@ const root = css`
--rdg-selection-color: #66afe9;
--rdg-font-size: 14px;
--rdg-cell-frozen-box-shadow: 2px 0 5px -2px rgba(136, 136, 136, 0.3);
--rdg-cell-right-frozen-box-shadow: -2px 0 5px -2px rgba(136, 136, 136, 0.3);

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just check if we can override it in the frontend app, so we can apply theming if needed (needed for dark theme already)

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've checked and we can, you can take a look at table.css


&:dir(rtl) {
--rdg-cell-frozen-box-shadow: -2px 0 5px -2px rgba(136, 136, 136, 0.3);
--rdg-cell-right-frozen-box-shadow: 2px 0 5px -2px rgba(136, 136, 136, 0.3);
}

display: grid;
Expand Down
38 changes: 17 additions & 21 deletions src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,25 +13,19 @@ export interface Column<TRow, TSummaryRow = unknown> {
readonly name: string | ReactElement;
/** A unique key to distinguish each column */
readonly key: string;
/**
* Column width. If not specified, it will be determined automatically based on grid width and specified widths of other columns
* @default 'auto'
*/
/** Column width. If not specified, it will be determined automatically based on grid width and specified widths of other columns */
readonly width?: Maybe<number | string>;
/**
* Minimum column width in px
* @default '50px'
*/
/** Minimum column width in px. */
readonly minWidth?: Maybe<number>;
/** Maximum column width in px. */
readonly maxWidth?: Maybe<number>;
readonly cellClass?: Maybe<string | ((row: TRow) => Maybe<string>)>;
readonly headerCellClass?: Maybe<string>;
readonly summaryCellClass?: Maybe<string | ((row: TSummaryRow) => Maybe<string>)>;
/** Render function used to render the content of cells */
readonly renderCell?: Maybe<(props: RenderCellProps<TRow, TSummaryRow>) => ReactNode>;
/** Render function used to render the content of the column's header cell */
readonly renderHeaderCell?: Maybe<(props: RenderHeaderCellProps<TRow, TSummaryRow>) => ReactNode>;
/** Render function used to render the content of cells */
readonly renderCell?: Maybe<(props: RenderCellProps<TRow, TSummaryRow>) => ReactNode>;
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Revert

/** Render function used to render the content of summary cells */
readonly renderSummaryCell?: Maybe<
(props: RenderSummaryCellProps<TSummaryRow, TRow>) => ReactNode
Expand All @@ -45,6 +39,8 @@ export interface Column<TRow, TSummaryRow = unknown> {
readonly colSpan?: Maybe<(args: ColSpanArgs<TRow, TSummaryRow>) => Maybe<number>>;
/** Determines whether column is frozen or not */
readonly frozen?: Maybe<boolean>;
/** Determines whether column is right frozen or not */
readonly rightFrozen?: Maybe<boolean>;
/** Enable resizing of a column */
readonly resizable?: Maybe<boolean>;
/** Enable sorting of a column */
Expand Down Expand Up @@ -149,10 +145,10 @@ export interface RenderHeaderCellProps<TRow, TSummaryRow = unknown> {

export interface CellRendererProps<TRow, TSummaryRow>
extends Pick<RenderRowProps<TRow, TSummaryRow>, 'row' | 'rowIdx' | 'selectCell'>,
Omit<
React.HTMLAttributes<HTMLDivElement>,
'children' | 'onClick' | 'onDoubleClick' | 'onContextMenu'
> {
Omit<
React.HTMLAttributes<HTMLDivElement>,
'children' | 'onClick' | 'onDoubleClick' | 'onContextMenu'
> {
column: CalculatedColumn<TRow, TSummaryRow>;
colSpan: number | undefined;
isCopied: boolean;
Expand Down Expand Up @@ -209,10 +205,10 @@ export interface CellSelectArgs<TRow, TSummaryRow = unknown> {

export interface BaseRenderRowProps<TRow, TSummaryRow = unknown>
extends Omit<React.HTMLAttributes<HTMLDivElement>, 'style' | 'children'>,
Pick<
DataGridProps<TRow, TSummaryRow>,
'onCellClick' | 'onCellDoubleClick' | 'onCellContextMenu'
> {
Pick<
DataGridProps<TRow, TSummaryRow>,
'onCellClick' | 'onCellDoubleClick' | 'onCellContextMenu'
> {
viewportColumns: readonly CalculatedColumn<TRow, TSummaryRow>[];
rowIdx: number;
selectedCellIdx: number | undefined;
Expand Down Expand Up @@ -304,7 +300,7 @@ export interface RenderSortPriorityProps {
priority: number | undefined;
}

export interface RenderSortStatusProps extends RenderSortIconProps, RenderSortPriorityProps {}
export interface RenderSortStatusProps extends RenderSortIconProps, RenderSortPriorityProps { }

export interface RenderCheckboxProps
extends Pick<
Expand All @@ -316,11 +312,11 @@ export interface RenderCheckboxProps
}

export interface Renderers<TRow, TSummaryRow> {
renderCell?: Maybe<(key: Key, props: CellRendererProps<TRow, TSummaryRow>) => ReactNode>;
renderCheckbox?: Maybe<(props: RenderCheckboxProps) => ReactNode>;
renderRow?: Maybe<(key: Key, props: RenderRowProps<TRow, TSummaryRow>) => ReactNode>;
renderSortStatus?: Maybe<(props: RenderSortStatusProps) => ReactNode>;
renderCell?: Maybe<(key: Key, props: CellRendererProps<TRow, TSummaryRow>) => ReactNode>;
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Revert

noRowsFallback?: Maybe<ReactNode>;
}

export type Direction = 'ltr' | 'rtl';
export type Direction = 'ltr' | 'rtl';
1 change: 1 addition & 0 deletions src/utils/domUtils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,4 +6,5 @@ export function stopPropagation(event: React.SyntheticEvent) {

export function scrollIntoView(element: Maybe<Element>) {
element?.scrollIntoView({ inline: 'nearest', block: 'nearest' });
// element?.scrollIntoView({ inline: 'center', block: 'nearest' });

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Clean

}
Loading