From 6befa63d93799890fb42fefd44bdc8162f0d52f8 Mon Sep 17 00:00:00 2001 From: mckervinc Date: Thu, 19 Dec 2024 11:17:38 -0500 Subject: [PATCH] bump to 1.2.1 --- CHANGELOG.md | 9 +++++++ example/src/Props.tsx | 3 ++- package.json | 2 +- src/Table.tsx | 14 +++++----- src/components/Footer.tsx | 11 ++++---- src/components/Header.tsx | 11 ++++---- src/components/List.tsx | 54 ++++++++++++++++++++++----------------- src/components/Row.tsx | 18 ++++++++----- src/util.ts | 39 +++++++++++++++++----------- 9 files changed, 97 insertions(+), 64 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 7a848c2..d0444c8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,14 @@ # CHANGELOG +## 1.2.1 + +_2024-12-19_ + +### Features + +- on initial render, the widths of the column are initialized correctly. +- small typescript changes to remove usage of `any` + ## 1.2.0 _2024-12-18_ diff --git a/example/src/Props.tsx b/example/src/Props.tsx index df196f3..1d7f93b 100644 --- a/example/src/Props.tsx +++ b/example/src/Props.tsx @@ -30,7 +30,7 @@ const columns: ColumnProps[] = [ key: "type", header: "Type", minWidth: 120, - maxWidth: 170, + maxWidth: 270, content: ({ row }: { row: PropData }) => {row.type} }, { @@ -53,6 +53,7 @@ const columns: ColumnProps[] = [ { key: "description", header: "Description", + minWidth: 200, content: ({ row }: { row: PropData }) => row.description } ]; diff --git a/package.json b/package.json index 748487a..d26ed4c 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "react-fluid-table", - "version": "1.2.0", + "version": "1.2.1", "description": "A React table inspired by @tanstack/react-virtual", "author": "Mckervin Ceme ", "license": "MIT", diff --git a/src/Table.tsx b/src/Table.tsx index 4ae378e..b32c4ce 100644 --- a/src/Table.tsx +++ b/src/Table.tsx @@ -1,5 +1,5 @@ import React, { forwardRef, useEffect, useMemo, useState } from "react"; -import { ColumnProps, TableProps, TableRef } from "../index"; +import { TableProps, TableRef } from "../index"; import AutoSizer from "./AutoSizer"; import List from "./components/List"; import { positive, randomString } from "./util"; @@ -78,12 +78,12 @@ const Table = forwardRef(function ( height={height} data={data} rowHeight={rowHeight} - itemKey={itemKey as any} - onRowClick={onRowClick as any} - rowRenderer={rowRenderer as any} - onExpandRow={onExpandRow as any} - subComponent={subComponent as any} - columns={columns as ColumnProps[]} + itemKey={itemKey} + onRowClick={onRowClick} + rowRenderer={rowRenderer} + onExpandRow={onExpandRow} + subComponent={subComponent} + columns={columns} {...props} /> ); diff --git a/src/components/Footer.tsx b/src/components/Footer.tsx index 313c798..70e36ec 100644 --- a/src/components/Footer.tsx +++ b/src/components/Footer.tsx @@ -1,4 +1,4 @@ -import React, { useCallback, useEffect, useRef } from "react"; +import React, { memo, useCallback, useEffect, useRef } from "react"; import { ColumnProps, FooterProps } from "../.."; import { cx, findTableByUuid } from "../util"; @@ -9,7 +9,7 @@ type InnerFooterCellProps = { rows: T[]; }; -const FooterCell = React.memo(function ({ prevWidth, rows, ...rest }: InnerFooterCellProps) { +function BaseFooterCell({ prevWidth, rows, ...rest }: InnerFooterCellProps) { // instance const { width, column } = rest; const style: React.CSSProperties = { @@ -24,9 +24,10 @@ const FooterCell = React.memo(function ({ prevWidth, rows, ...rest }: InnerFo {!!column.footer && } ); -}); +} -FooterCell.displayName = "FooterCell"; +const FooterCell = memo(BaseFooterCell) as (props: InnerFooterCellProps) => React.JSX.Element; +(FooterCell as React.FC).displayName = "FooterCell"; type InnerFooterProps = { uuid: string; @@ -122,7 +123,7 @@ function Footer({ {columns.map((c, i) => ( pv + c, 0) : 0} diff --git a/src/components/Header.tsx b/src/components/Header.tsx index 230f3ab..e62fdfd 100644 --- a/src/components/Header.tsx +++ b/src/components/Header.tsx @@ -11,7 +11,7 @@ type HeaderCellProps = { onHeaderClick: (column: ColumnProps) => void; }; -const HeaderCell = memo(function ({ +function BaseHeaderCell({ column, width, prevWidth, @@ -53,9 +53,10 @@ const HeaderCell = memo(function ({ style={{ ...style, ...frozenStyle }} /> ); -}); +} -HeaderCell.displayName = "HeaderCell"; +const HeaderCell = memo(BaseHeaderCell) as (props: HeaderCellProps) => React.JSX.Element; +(HeaderCell as React.FC).displayName = "HeaderCell"; type HeaderProps = { uuid: string; @@ -124,8 +125,8 @@ const Header = forwardRef(function ( width={pixelWidths[i]} sortedCol={sortedCol} sortedDir={sortedDir} - column={c as ColumnProps} - onHeaderClick={onHeaderClick as any} + column={c} + onHeaderClick={onHeaderClick} prevWidth={c.frozen ? pixelWidths.slice(0, i).reduce((pv, c) => pv + c, 0) : 0} /> ))} diff --git a/src/components/List.tsx b/src/components/List.tsx index 890192d..2843a39 100644 --- a/src/components/List.tsx +++ b/src/components/List.tsx @@ -4,6 +4,7 @@ import React, { useCallback, useEffect, useImperativeHandle, + useLayoutEffect, useRef, useState } from "react"; @@ -27,7 +28,7 @@ const syncScroll = (source: HTMLElement | null, target: HTMLElement | null) => { } }; -const List = forwardRef(function ( +function BaseList( { id, uuid, @@ -63,8 +64,11 @@ const List = forwardRef(function ( const parentRef = useRef(null); const headerRef = useRef(null); const { ref: innerRef, width: innerWidth = 0 } = useResizeDetector(); - const [pixelWidths, setPixelWidths] = useState([]); const [widthConstants, setWidthConstants] = useState(findColumnWidthConstants(columns)); + const [pixelWidths, setPixelWidths] = useState(() => { + const { fixedWidth, remainingCols } = widthConstants; + return calculateColumnWidths(width, remainingCols, fixedWidth, minColumnWidth, columns); + }); const [expandedCache, setExpandedCache] = useState>({}); const generateKeyFromRow = useCallback( (row: T, defaultValue: number) => itemKey?.(row) ?? defaultValue, @@ -83,18 +87,6 @@ const List = forwardRef(function ( const { fixedWidth, remainingCols } = widthConstants; // functions - const updatePixelWidths = useCallback(() => { - const widths = calculateColumnWidths( - parentRef.current, - remainingCols, - fixedWidth, - minColumnWidth, - columns - ); - if (!arraysMatch(widths, pixelWidths)) { - setPixelWidths(widths); - } - }, [remainingCols, fixedWidth, minColumnWidth, pixelWidths, columns]); const isRowExpanded = typeof expandedRows === "function" ? expandedRows : undefined; const onExpand = useCallback( @@ -119,7 +111,18 @@ const List = forwardRef(function ( // effects // update pixel widths every time the width changes - useEffect(() => updatePixelWidths(), [width]); + useLayoutEffect(() => { + const widths = calculateColumnWidths( + parentRef.current?.clientWidth ?? width, + remainingCols, + fixedWidth, + minColumnWidth, + columns + ); + if (!arraysMatch(widths, pixelWidths)) { + setPixelWidths(widths); + } + }, [width, remainingCols, fixedWidth, minColumnWidth, pixelWidths, columns]); // set the width constants useEffect(() => setWidthConstants(findColumnWidthConstants(columns)), [columns]); @@ -198,13 +201,13 @@ const List = forwardRef(function ( style={style} className={className} isExpanded={isExpanded} - onRowClick={onRowClick as any} - rowRenderer={rowRenderer as any} - onExpand={onExpand as any} + onRowClick={onRowClick} + rowRenderer={rowRenderer} + onExpand={onExpand} index={index} - columns={columns as any} + columns={columns} pixelWidths={pixelWidths} - subComponent={subComponent as any} + subComponent={subComponent} /> ); })} @@ -215,14 +218,19 @@ const List = forwardRef(function ( uuid={uuid} rows={data} sticky={stickyFooter} - columns={columns as ColumnProps[]} + columns={columns} pixelWidths={pixelWidths} className={footerClassname} style={footerStyle} - component={footerComponent as any} + component={footerComponent} /> ); -}); +} + +const List = forwardRef(BaseList) as ( + props: ListProps & { ref?: React.ForwardedRef } +) => React.JSX.Element; +(List as React.FC).displayName = "List"; export default List; diff --git a/src/components/Row.tsx b/src/components/Row.tsx index 11d7136..f8aa42a 100644 --- a/src/components/Row.tsx +++ b/src/components/Row.tsx @@ -14,7 +14,7 @@ type TableCellProps = { onExpanderClick: (event?: React.MouseEvent) => void; }; -const TableCell = memo(function ({ +function BaseTableCell({ row, index, width, @@ -93,9 +93,10 @@ const TableCell = memo(function ({ // custom cell styling const frozenStyle: React.CSSProperties = column.frozen ? { position: "sticky", zIndex: 2 } : {}; return ; -}); +} -TableCell.displayName = "TableCell"; +const TableCell = memo(BaseTableCell) as (props: TableCellProps) => React.JSX.Element; +(TableCell as React.FC).displayName = "TableCell"; type RowComponentProps = { row: T; @@ -167,7 +168,7 @@ type RowProps = { rowRenderer?: (props: RowRenderProps) => JSX.Element; }; -const Row = forwardRef(function ( +function BaseRow( { uuid, index, @@ -205,7 +206,7 @@ const Row = forwardRef(function ( ( )} ); -}); +} -Row.displayName = "Row"; +const Row = forwardRef(BaseRow) as ( + props: RowProps & { ref?: React.ForwardedRef } +) => React.JSX.Element; +(Row as React.FC).displayName = "Row"; export default Row; diff --git a/src/util.ts b/src/util.ts index 5f2f380..b3add93 100644 --- a/src/util.ts +++ b/src/util.ts @@ -5,7 +5,7 @@ import { ColumnProps } from ".."; * @param classes list of potential className strings * @returns a combined className string */ -export const cx = (...args: (string | number | null | boolean | undefined)[]) => { +const cx = (...args: (string | number | null | boolean | undefined)[]) => { return args .flat() .filter((x): x is Exclude => !!x) @@ -13,7 +13,7 @@ export const cx = (...args: (string | number | null | boolean | undefined)[]) => .join(" "); }; -export const arraysMatch = (arr1: T[] | null | undefined, arr2: T[] | null | undefined) => { +const arraysMatch = (arr1: T[] | null | undefined, arr2: T[] | null | undefined) => { if (arr1 == null && arr2 == null) { return true; } @@ -35,7 +35,7 @@ export const arraysMatch = (arr1: T[] | null | undefined, arr2: T[] | null | return false; }; -export const randomString = (num: number) => { +const randomString = (num: number) => { let result = ""; const characters = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"; const length = characters.length; @@ -45,25 +45,22 @@ export const randomString = (num: number) => { return result; }; -export const findTableByUuid = (uuid: string): HTMLElement | null => - document.querySelector(`[data-table-key='${uuid}']`); +const findElementByValue = (value: string) => document.querySelector(value); -export const findHeaderByUuid = (uuid: string): HTMLElement | null => - document.querySelector(`[data-header-key='${uuid}-header']`); +const findTableByUuid = (uuid: string) => findElementByValue(`[data-table-key='${uuid}']`); -export const findFooterByUuid = (uuid: string): HTMLElement | null => - document.querySelector(`[data-footer-key='${uuid}-footer']`); +const findHeaderByUuid = (uuid: string) => findElementByValue(`[data-header-key='${uuid}-header']`); + +const findFooterByUuid = (uuid: string) => findElementByValue(`[data-footer-key='${uuid}-footer']`); // table utilities -export const calculateColumnWidths = ( - element: HTMLElement | null, +const calculateColumnWidths = ( + clientWidth: number, numColumns: number, fixedColumnWidths: number, minColumnWidth: number, columns: ColumnProps[] ) => { - if (!element) return columns.map(() => minColumnWidth); - const clientWidth = element.clientWidth; let n = Math.max(numColumns, 1); let usedSpace = fixedColumnWidths; let freeSpace = Math.max(clientWidth - usedSpace, 0); @@ -99,7 +96,7 @@ export const calculateColumnWidths = ( }); }; -export const findColumnWidthConstants = (columns: ColumnProps[]) => { +const findColumnWidthConstants = (columns: ColumnProps[]) => { return columns.reduce( (pv, c) => ({ fixedWidth: pv.fixedWidth + (c.width || 0), @@ -109,4 +106,16 @@ export const findColumnWidthConstants = (columns: ColumnProps[]) => { ); }; -export const positive = (x: number | null | undefined): x is number => x != null && x > 0; +const positive = (x: number | null | undefined): x is number => x != null && x > 0; + +export { + arraysMatch, + calculateColumnWidths, + cx, + positive, + findColumnWidthConstants, + findFooterByUuid, + findHeaderByUuid, + findTableByUuid, + randomString +};