Skip to content

Commit

Permalink
use different approach
Browse files Browse the repository at this point in the history
  • Loading branch information
vursen committed Nov 15, 2024
1 parent 98839b1 commit 4a474bc
Show file tree
Hide file tree
Showing 8 changed files with 80 additions and 60 deletions.
53 changes: 33 additions & 20 deletions packages/react-components/src/Grid.tsx
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
import {
type ComponentType,
createContext,
type ForwardedRef,
forwardRef,
type ReactElement,
type RefAttributes,
type RefObject,
useCallback,
useEffect,
useRef,
} from 'react';
Expand All @@ -17,7 +19,7 @@ import {
import type { GridRowDetailsReactRendererProps } from './renderers/grid.js';
import { useModelRenderer } from './renderers/useModelRenderer.js';
import useMergedRefs from './utils/useMergedRefs.js';
import { isElementMarkedAsRendered } from './utils/markElementAsRendered.js';
import { isElementMarkedAsRendered, markElementAsRendered } from './utils/markElementAsRendered.js';

export * from './generated/Grid.js';

Expand All @@ -26,18 +28,11 @@ export type GridProps<TItem> = Partial<Omit<_GridProps<TItem>, 'rowDetailsRender
rowDetailsRenderer?: ComponentType<GridRowDetailsReactRendererProps<TItem>> | null;
}>;

function overrideRecalculateColumnWidths(grid: GridElement) {
const originalRecalculateColumnWidths = grid.recalculateColumnWidths;
grid.recalculateColumnWidths = function (...args) {
originalRecalculateColumnWidths.apply(this, args);
type GridContext = {
onColumnRendered(column: HTMLElement): void;
};

// @ts-ignore
if (this._getColumns().some((column) => !column.hidden && !isElementMarkedAsRendered(column))) {
// @ts-ignore
this.__pendingRecalculateColumnWidths = true;
}
};
}
export const GridContext = createContext<GridContext | null>(null);

function Grid<TItem = GridDefaultItem>(
props: GridProps<TItem>,
Expand All @@ -49,16 +44,34 @@ function Grid<TItem = GridDefaultItem>(
const finalRef = useMergedRefs(innerRef, ref);

useEffect(() => {
if (innerRef.current) {
overrideRecalculateColumnWidths(innerRef.current);
}
}, [innerRef.current]);
innerRef.current!.recalculateColumnWidths = function (...args) {
// @ts-ignore
const autoWidthColumns: HTMLElement[] = this._getColumns().filter((col) => col.autoWidth && !col.hidden);
if (autoWidthColumns.some((col) => !isElementMarkedAsRendered(col))) {
// @ts-ignore
this.__pendingRecalculateColumnWidths = true;
return;
}

// console.log('rendered');

Object.getPrototypeOf(this).recalculateColumnWidths.apply(this, args);
};
}, []);

const onColumnRendered = useCallback((column: HTMLElement) => {
markElementAsRendered(column);
// @ts-ignore
innerRef.current!.__tryToRecalculateColumnWidthsIfPending();
}, []);

return (
<_Grid<TItem> {...props} ref={finalRef} rowDetailsRenderer={rowDetailsRenderer}>
{props.children}
{portals}
</_Grid>
<GridContext.Provider value={{ onColumnRendered }}>
<_Grid<TItem> {...props} ref={finalRef} rowDetailsRenderer={rowDetailsRenderer}>
{props.children}
{portals}
</_Grid>
</GridContext.Provider>
);
}

Expand Down
14 changes: 9 additions & 5 deletions packages/react-components/src/GridColumn.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import {
type ReactElement,
type ReactNode,
type RefAttributes,
useContext,
useEffect,
useRef,
} from 'react';
Expand All @@ -18,7 +19,7 @@ import type { GridBodyReactRendererProps, GridEdgeReactRendererProps } from './r
import { useModelRenderer } from './renderers/useModelRenderer.js';
import { useSimpleOrChildrenRenderer } from './renderers/useSimpleOrChildrenRenderer.js';
import useMergedRefs from './utils/useMergedRefs.js';
import { markElementAsRendered } from './utils/markElementAsRendered.js';
import { GridContext } from './Grid.js';

export * from './generated/GridColumn.js';

Expand Down Expand Up @@ -58,19 +59,22 @@ function GridColumn<TItem = GridDefaultItem>(
{ children, footer, header, ...props }: GridColumnProps<TItem>,
ref: ForwardedRef<GridColumnElement<TItem>>,
): ReactElement | null {
const gridContext = useContext(GridContext)!;

const [headerPortals, headerRenderer, isHeaderRendered] = useSimpleOrChildrenRenderer(props.headerRenderer, header);
const [footerPortals, footerRenderer, isFooterRendered] = useSimpleOrChildrenRenderer(props.footerRenderer, footer);
const [bodyPortals, bodyRenderer, isBodyRendered] = useModelRenderer(props.renderer ?? children);
const isRendered = !!(isHeaderRendered && isFooterRendered && isBodyRendered);
const isRendered =
(!headerRenderer || isHeaderRendered) && (!footerRenderer || isFooterRendered) && (!bodyRenderer || isBodyRendered);

const innerRef = useRef<GridColumnElement<TItem>>(null);
const finalRef = useMergedRefs(innerRef, ref);

useEffect(() => {
if (innerRef.current && isRendered) {
markElementAsRendered(innerRef.current);
if (isRendered) {
gridContext.onColumnRendered(innerRef.current!);
}
}, [innerRef.current, isRendered]);
}, [isRendered]);

return (
<_GridColumn<TItem>
Expand Down
12 changes: 8 additions & 4 deletions packages/react-components/src/GridColumnGroup.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import {
forwardRef,
useContext,
useEffect,
useRef,
type ComponentType,
Expand All @@ -18,6 +19,7 @@ import { useSimpleOrChildrenRenderer } from './renderers/useSimpleOrChildrenRend
import type { OmittedGridColumnHTMLAttributes } from './GridColumn.js';
import useMergedRefs from './utils/useMergedRefs.js';
import { markElementAsRendered } from './utils/markElementAsRendered.js';
import { GridContext } from './Grid.js';

export * from './generated/GridColumnGroup.js';

Expand All @@ -44,18 +46,20 @@ function GridColumnGroup(
{ children, footer, header, ...props }: GridColumnGroupProps,
ref: ForwardedRef<GridColumnGroupElement>,
): ReactElement | null {
const gridContext = useContext(GridContext)!;

const [headerPortals, headerRenderer, isHeaderRendered] = useSimpleOrChildrenRenderer(props.headerRenderer, header);
const [footerPortals, footerRenderer, isFooterRendered] = useSimpleOrChildrenRenderer(props.footerRenderer, footer);
const isRendered = !!(isHeaderRendered && isFooterRendered);
const isRendered = (!headerRenderer || isHeaderRendered) && (!footerRenderer || isFooterRendered);

const innerRef = useRef<GridColumnGroupElement>(null);
const finalRef = useMergedRefs(innerRef, ref);

useEffect(() => {
if (innerRef.current && isRendered) {
markElementAsRendered(innerRef.current);
if (isRendered) {
gridContext.onColumnRendered(innerRef.current!);
}
}, [innerRef.current, isRendered]);
}, [isRendered]);

return (
<_GridColumnGroup {...props} footerRenderer={footerRenderer} headerRenderer={headerRenderer} ref={finalRef}>
Expand Down
27 changes: 20 additions & 7 deletions packages/react-components/src/GridFilterColumn.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,9 @@ import {
type ReactElement,
type ReactNode,
type RefAttributes,
useContext,
useEffect,
useRef,
} from 'react';
import type { GridDefaultItem } from './generated/Grid.js';
import {
Expand All @@ -16,6 +19,8 @@ import type { GridBodyReactRendererProps, GridEdgeReactRendererProps } from './r
import { useModelRenderer } from './renderers/useModelRenderer.js';
import { useSimpleOrChildrenRenderer } from './renderers/useSimpleOrChildrenRenderer.js';
import type { OmittedGridColumnHTMLAttributes } from './GridColumn.js';
import { GridContext } from './Grid.js';
import useMergedRefs from './utils/useMergedRefs.js';

export * from './generated/GridFilterColumn.js';

Expand Down Expand Up @@ -43,15 +48,23 @@ function GridFilterColumn<TItem = GridDefaultItem>(
{ footer, ...props }: GridFilterColumnProps<TItem>,
ref: ForwardedRef<GridFilterColumnElement<TItem>>,
): ReactElement | null {
const [footerPortals, footerRenderer] = useSimpleOrChildrenRenderer(props.footerRenderer, footer, {
renderSync: true,
});
const [bodyPortals, bodyRenderer] = useModelRenderer(props.renderer ?? props.children, {
renderSync: true,
});
const gridContext = useContext(GridContext)!;

const [footerPortals, footerRenderer, isFooterRendered] = useSimpleOrChildrenRenderer(props.footerRenderer, footer);
const [bodyPortals, bodyRenderer, isBodyRendered] = useModelRenderer(props.renderer ?? props.children);
const isRendered = (!footerRenderer || isFooterRendered) && (!bodyRenderer || isBodyRendered);

const innerRef = useRef<GridFilterColumnElement<TItem>>(null);
const finalRef = useMergedRefs(innerRef, ref);

useEffect(() => {
if (isRendered) {
gridContext.onColumnRendered(innerRef.current!);
}
}, [isRendered]);

return (
<_GridFilterColumn<TItem> {...props} footerRenderer={footerRenderer} ref={ref} renderer={bodyRenderer}>
<_GridFilterColumn<TItem> {...props} footerRenderer={footerRenderer} ref={finalRef} renderer={bodyRenderer}>
{footerPortals}
{bodyPortals}
</_GridFilterColumn>
Expand Down
12 changes: 3 additions & 9 deletions packages/react-components/src/GridSelectionColumn.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -49,15 +49,9 @@ function GridSelectionColumn<TItem = GridDefaultItem>(
{ footer, header, ...props }: GridSelectionColumnProps<TItem>,
ref: ForwardedRef<GridSelectionColumnElement<TItem>>,
): ReactElement | null {
const [headerPortals, headerRenderer] = useSimpleOrChildrenRenderer(props.headerRenderer, header, {
renderSync: true,
});
const [footerPortals, footerRenderer] = useSimpleOrChildrenRenderer(props.footerRenderer, footer, {
renderSync: true,
});
const [bodyPortals, bodyRenderer] = useModelRenderer(props.renderer ?? props.children, {
renderSync: true,
});
const [headerPortals, headerRenderer] = useSimpleOrChildrenRenderer(props.headerRenderer, header);
const [footerPortals, footerRenderer] = useSimpleOrChildrenRenderer(props.footerRenderer, footer);
const [bodyPortals, bodyRenderer] = useModelRenderer(props.renderer ?? props.children);

return (
<_GridSelectionColumn<TItem>
Expand Down
8 changes: 2 additions & 6 deletions packages/react-components/src/GridSortColumn.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -42,12 +42,8 @@ function GridSortColumn<TItem = GridDefaultItem>(
{ footer, ...props }: GridSortColumnProps<TItem>,
ref: ForwardedRef<GridSortColumnElement<TItem>>,
): ReactElement | null {
const [footerPortals, footerRenderer] = useSimpleOrChildrenRenderer(props.footerRenderer, footer, {
renderSync: true,
});
const [bodyPortals, bodyRenderer] = useModelRenderer(props.renderer ?? props.children, {
renderSync: true,
});
const [footerPortals, footerRenderer] = useSimpleOrChildrenRenderer(props.footerRenderer, footer);
const [bodyPortals, bodyRenderer] = useModelRenderer(props.renderer ?? props.children);

return (
<_GridSortColumn<TItem> {...props} footerRenderer={footerRenderer} ref={ref} renderer={bodyRenderer}>
Expand Down
8 changes: 2 additions & 6 deletions packages/react-components/src/GridTreeColumn.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -46,12 +46,8 @@ function GridTreeColumn<TItem = GridDefaultItem>(
{ footer, header, ...props }: GridTreeColumnProps<TItem>,
ref: ForwardedRef<GridTreeColumnElement<TItem>>,
): ReactElement | null {
const [headerPortals, headerRenderer] = useSimpleOrChildrenRenderer(props.headerRenderer, header, {
renderSync: true,
});
const [footerPortals, footerRenderer] = useSimpleOrChildrenRenderer(props.footerRenderer, footer, {
renderSync: true,
});
const [headerPortals, headerRenderer] = useSimpleOrChildrenRenderer(props.headerRenderer, header);
const [footerPortals, footerRenderer] = useSimpleOrChildrenRenderer(props.footerRenderer, footer);

return (
<_GridTreeColumn<TItem> {...props} headerRenderer={headerRenderer} footerRenderer={footerRenderer} ref={ref}>
Expand Down
6 changes: 3 additions & 3 deletions packages/react-components/src/utils/markElementAsRendered.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
const ELEMENT_RENDERED = Symbol();

export function markElementAsRendered(element: HTMLElement) {
export function markElementAsRendered(element: HTMLElement & { [ELEMENT_RENDERED]?: boolean }) {
element[ELEMENT_RENDERED] = true;
}

export function isElementMarkedAsRendered(element: HTMLElement) {
return element[ELEMENT_RENDERED];
export function isElementMarkedAsRendered(element: HTMLElement & { [ELEMENT_RENDERED]?: boolean }) {
return !!element[ELEMENT_RENDERED];
}

0 comments on commit 4a474bc

Please sign in to comment.