From 268029559425e3cefdc483e69f04cd6052260cf1 Mon Sep 17 00:00:00 2001 From: mckervinc Date: Thu, 19 Dec 2024 14:03:13 -0500 Subject: [PATCH] bump to 1.2.2 --- CHANGELOG.md | 10 +++++++ example/package.json | 6 ++-- example/vite.config.ts | 11 ++++--- example/yarn.lock | 32 ++++++++++---------- package.json | 6 ++-- src/Table.tsx | 10 +++++-- src/components/Footer.tsx | 61 ++++----------------------------------- src/components/Header.tsx | 38 ++++++++++++------------ src/components/List.tsx | 35 ++++------------------ src/styles/main.css | 14 ++------- src/util.ts | 3 -- 11 files changed, 80 insertions(+), 146 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index d0444c8..6a43fae 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,15 @@ # CHANGELOG +## 1.2.2 + +_2024-12-19_ + +### Features + +- explicit support for React 19 +- fixes horizontal scroll bug on iOS +- fixes `footerComponent` regression + ## 1.2.1 _2024-12-19_ diff --git a/example/package.json b/example/package.json index 7d9ba4f..d02f6f0 100644 --- a/example/package.json +++ b/example/package.json @@ -30,7 +30,7 @@ "clsx": "^2.1.1", "country-flag-icons": "^1.5.13", "lodash": "^4.17.21", - "lucide-react": "^0.465.0", + "lucide-react": "^0.468.0", "react": "^18.2.0", "react-dom": "^18.2.0", "react-fluid-table": "link:..", @@ -54,9 +54,9 @@ "postcss": "^8.4.49", "prettier": "^3.4.2", "prettier-plugin-tailwindcss": "^0.6.9", - "tailwindcss": "^3.4.16", + "tailwindcss": "^3.4.17", "typescript": "^5.4.5", - "vite": "^6.0.2" + "vite": "^6.0.4" }, "volta": { "node": "18.18.0", diff --git a/example/vite.config.ts b/example/vite.config.ts index 862184a..7d0738f 100644 --- a/example/vite.config.ts +++ b/example/vite.config.ts @@ -1,4 +1,4 @@ -import path from "path" +import path from "path"; import { defineConfig } from "vite"; import react from "@vitejs/plugin-react"; @@ -6,9 +6,12 @@ import react from "@vitejs/plugin-react"; export default defineConfig({ plugins: [react()], base: "/react-fluid-table/", + server: { + host: "0.0.0.0" + }, resolve: { alias: { - "@": path.resolve(__dirname, "./src"), - }, - }, + "@": path.resolve(__dirname, "./src") + } + } }); diff --git a/example/yarn.lock b/example/yarn.lock index aec89d8..8fee5c0 100644 --- a/example/yarn.lock +++ b/example/yarn.lock @@ -2009,10 +2009,10 @@ lru-cache@^5.1.1: dependencies: yallist "^3.0.2" -lucide-react@^0.465.0: - version "0.465.0" - resolved "https://registry.yarnpkg.com/lucide-react/-/lucide-react-0.465.0.tgz#3f98d40f7b7ac5266c055aaf582c303b07f84de2" - integrity sha512-uV7WEqbwaCcc+QjAxIhAvkAr3kgwkkYID3XptCHll72/F7NZlk6ONmJYpk+Xqx5Q0r/8wiOjz73H1BYbl8Z8iw== +lucide-react@^0.468.0: + version "0.468.0" + resolved "https://registry.yarnpkg.com/lucide-react/-/lucide-react-0.468.0.tgz#830c1bfd905575ddd23b832baa420c87db166910" + integrity sha512-6koYRhnM2N0GGZIdXzSeiNwguv1gt/FAjZOiPl76roBi3xKEXa4WmfpxgQwTTL4KipXjefrnf3oV4IsYhi4JFA== merge2@^1.3.0, merge2@^1.4.1: version "1.4.1" @@ -2365,10 +2365,10 @@ react-remove-scroll@2.6.0: use-callback-ref "^1.3.0" use-sidecar "^1.1.2" -react-resize-detector@^11.0.1: - version "11.0.1" - resolved "https://registry.yarnpkg.com/react-resize-detector/-/react-resize-detector-11.0.1.tgz#70684958ae82bc0deb240d133a1ee440e0624532" - integrity sha512-1Tdgu6Ou3vI3RQD+o2/kTvDibb4NRe7Oh83hIjNNEXb6WKKCQT99VQlh3Xlbdq2HtkUoFEMrgMMKkYI83YbD7Q== +react-resize-detector@^12.0.1: + version "12.0.1" + resolved "https://registry.yarnpkg.com/react-resize-detector/-/react-resize-detector-12.0.1.tgz#7d7eba49a538c32ba45f121b6dc9f28bf1dc4077" + integrity sha512-qOzOEn8AC4i60L29sPeUtw3RRsdDYZAGOBDwg3vzUQ34fpP+JOBlf1x0Q8Wv85IPQ3nBNL4NNr12e2caiIK1Lg== dependencies: lodash "^4.17.21" @@ -2645,10 +2645,10 @@ tailwindcss-animate@^1.0.7: resolved "https://registry.yarnpkg.com/tailwindcss-animate/-/tailwindcss-animate-1.0.7.tgz#318b692c4c42676cc9e67b19b78775742388bef4" integrity sha512-bl6mpH3T7I3UFxuvDEXLxy/VuFxBk5bbzplh7tXI68mwMokNYd1t9qPBHlnyTwfa4JGC4zP516I1hYYtQ/vspA== -tailwindcss@^3.4.16: - version "3.4.16" - resolved "https://registry.yarnpkg.com/tailwindcss/-/tailwindcss-3.4.16.tgz#35a7c3030844d6000fc271878db4096b6a8d2ec9" - integrity sha512-TI4Cyx7gDiZ6r44ewaJmt0o6BrMCT5aK5e0rmJ/G9Xq3w7CX/5VXl/zIPEJZFUK5VEqwByyhqNPycPlvcK4ZNw== +tailwindcss@^3.4.17: + version "3.4.17" + resolved "https://registry.yarnpkg.com/tailwindcss/-/tailwindcss-3.4.17.tgz#ae8406c0f96696a631c790768ff319d46d5e5a63" + integrity sha512-w33E2aCvSDP0tW9RZuNXadXlkHXqFzSkQew/aIa2i/Sj8fThxwovwlXHSPXTbAHwEIhBFXAedUhP2tueAKP8Og== dependencies: "@alloc/quick-lru" "^5.2.0" arg "^5.0.2" @@ -2781,10 +2781,10 @@ uuid@8.3.2: resolved "https://registry.yarnpkg.com/uuid/-/uuid-8.3.2.tgz#80d5b5ced271bb9af6c445f21a1a04c606cefbe2" integrity sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg== -vite@^6.0.2: - version "6.0.2" - resolved "https://registry.yarnpkg.com/vite/-/vite-6.0.2.tgz#7a22630c73c7b663335ddcdb2390971ffbc14993" - integrity sha512-XdQ+VsY2tJpBsKGs0wf3U/+azx8BBpYRHFAyKm5VeEZNOJZRB63q7Sc8Iup3k0TrN3KO6QgyzFf+opSbfY1y0g== +vite@^6.0.4: + version "6.0.4" + resolved "https://registry.yarnpkg.com/vite/-/vite-6.0.4.tgz#fe7cfaedff7c701d5582be5c4ed6a2150538ea9d" + integrity sha512-zwlH6ar+6o6b4Wp+ydhtIKLrGM/LoqZzcdVmkGAFun0KHTzIzjh+h0kungEx7KJg/PYnC80I4TII9WkjciSR6Q== dependencies: esbuild "^0.24.0" postcss "^8.4.49" diff --git a/package.json b/package.json index d26ed4c..1b1700c 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "react-fluid-table", - "version": "1.2.1", + "version": "1.2.2", "description": "A React table inspired by @tanstack/react-virtual", "author": "Mckervin Ceme ", "license": "MIT", @@ -35,8 +35,8 @@ "site:build": "cd example && yarn install && yarn build" }, "peerDependencies": { - "react": ">=16.8.0", - "react-dom": ">=16.8.0" + "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0", + "react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" }, "devDependencies": { "@babel/core": "^7.26.0", diff --git a/src/Table.tsx b/src/Table.tsx index b32c4ce..b843777 100644 --- a/src/Table.tsx +++ b/src/Table.tsx @@ -6,7 +6,7 @@ import { positive, randomString } from "./util"; let warned = false; -const Table = forwardRef(function ( +function BaseTable( { id, rowHeight, @@ -83,6 +83,7 @@ const Table = forwardRef(function ( rowRenderer={rowRenderer} onExpandRow={onExpandRow} subComponent={subComponent} + footerComponent={footerComponent} columns={columns} {...props} /> @@ -90,8 +91,11 @@ const Table = forwardRef(function ( }} ); -}); +} -Table.displayName = "Table"; +const Table = forwardRef(BaseTable) as ( + props: TableProps & { ref?: React.ForwardedRef } +) => React.JSX.Element; +(Table as React.FC).displayName = "Table"; export default Table; diff --git a/src/components/Footer.tsx b/src/components/Footer.tsx index 70e36ec..2b703ca 100644 --- a/src/components/Footer.tsx +++ b/src/components/Footer.tsx @@ -1,6 +1,6 @@ -import React, { memo, useCallback, useEffect, useRef } from "react"; +import React, { memo } from "react"; import { ColumnProps, FooterProps } from "../.."; -import { cx, findTableByUuid } from "../util"; +import { cx } from "../util"; type InnerFooterCellProps = { width: number; @@ -32,6 +32,7 @@ const FooterCell = memo(BaseFooterCell) as (props: InnerFooterCellProps) = type InnerFooterProps = { uuid: string; rows: T[]; + isScrollHorizontal: boolean; columns: ColumnProps[]; pixelWidths: number[]; className?: string; @@ -47,12 +48,10 @@ function Footer({ pixelWidths, sticky, className, + isScrollHorizontal, style: footerStyle = {}, component: Component }: InnerFooterProps) { - const scroll = useRef(false); - const ref = useRef(null); - // constants const hasFooter = !!Component || columns.some(c => !!c.footer); const style: React.CSSProperties = { @@ -64,60 +63,14 @@ function Footer({ style.minWidth = 0; } - // functions - const onScroll = useCallback( - (target: HTMLElement | null, element: HTMLElement | null) => { - if (scroll.current || !element || !sticky) { - return; - } - - const left = target?.scrollLeft || 0; - scroll.current = true; - element.scrollLeft = left; - setTimeout(() => { - scroll.current = false; - }, 0); - }, - [uuid, sticky] - ); - - const listener = useCallback( - (ev: Event) => { - onScroll(ev.target as HTMLDivElement, ref.current); - }, - [onScroll] - ); - - // effects - // add scroll listener - useEffect(() => { - if (uuid) { - const e = findTableByUuid(uuid); - if (e) { - e.addEventListener("scroll", listener); - } - } - - return () => { - if (uuid) { - const e = findTableByUuid(uuid); - if (e) { - e.removeEventListener("scroll", listener); - } - } - }; - }, [uuid, listener]); - // render if (!Component) { const hasFooter = columns.some(c => !!c.footer); return (
onScroll(e.target as HTMLDivElement, findTableByUuid(uuid))} + className={cx("rft-footer", sticky && "sticky", isScrollHorizontal && "scroll", className)} >
{columns.map((c, i) => ( @@ -136,11 +89,9 @@ function Footer({ return (
onScroll(e.target as HTMLDivElement, findTableByUuid(uuid))} + className={cx("rft-footer", sticky && "sticky", isScrollHorizontal && "scroll", className)} >
diff --git a/src/components/Header.tsx b/src/components/Header.tsx index e62fdfd..94eb29f 100644 --- a/src/components/Header.tsx +++ b/src/components/Header.tsx @@ -1,4 +1,4 @@ -import React, { forwardRef, memo, useCallback, useEffect, useState } from "react"; +import React, { memo, useCallback, useEffect, useState } from "react"; import { ColumnProps, SortDirection } from "../.."; import { cx } from "../util"; @@ -62,7 +62,7 @@ type HeaderProps = { uuid: string; columns: ColumnProps[]; pixelWidths: number[]; - showRowWrapper: boolean; + isScrollHorizontal: boolean; className?: string; style?: React.CSSProperties; sortColumn?: string; @@ -70,20 +70,17 @@ type HeaderProps = { onSort?: (col: string, dir: SortDirection | null) => void; }; -const Header = forwardRef(function ( - { - uuid, - columns, - showRowWrapper, - pixelWidths, - className, - style, - sortColumn, - sortDirection, - onSort - }: HeaderProps, - ref: React.ForwardedRef -) { +function Header({ + uuid, + columns, + isScrollHorizontal, + pixelWidths, + className, + style, + sortColumn, + sortDirection, + onSort +}: HeaderProps) { // hooks const [sortedCol, setSortedCol] = useState(sortColumn); const [sortedDir, setSortedDir] = useState(sortDirection); @@ -116,8 +113,11 @@ const Header = forwardRef(function ( }, [sortColumn, sortDirection]); return ( -
-
+
+
{columns.map((c, i) => ( (
); -}); +} Header.displayName = "Header"; diff --git a/src/components/List.tsx b/src/components/List.tsx index 2843a39..e65f152 100644 --- a/src/components/List.tsx +++ b/src/components/List.tsx @@ -9,7 +9,7 @@ import React, { useState } from "react"; import { useResizeDetector } from "react-resize-detector"; -import { ColumnProps, ScrollAlignment, TableProps, TableRef } from "../.."; +import { ScrollAlignment, TableProps, TableRef } from "../.."; import { DEFAULT_ROW_HEIGHT } from "../constants"; import { arraysMatch, calculateColumnWidths, cx, findColumnWidthConstants } from "../util"; import Footer from "./Footer"; @@ -22,12 +22,6 @@ type ListProps = TableProps & { width: number; }; -const syncScroll = (source: HTMLElement | null, target: HTMLElement | null) => { - if (source && target) { - target.scrollLeft = source.scrollLeft; - } -}; - function BaseList( { id, @@ -62,7 +56,6 @@ function BaseList( ) { // hooks const parentRef = useRef(null); - const headerRef = useRef(null); const { ref: innerRef, width: innerWidth = 0 } = useResizeDetector(); const [widthConstants, setWidthConstants] = useState(findColumnWidthConstants(columns)); const [pixelWidths, setPixelWidths] = useState(() => { @@ -82,7 +75,7 @@ function BaseList( }); // constants - const showRowWrapper = (innerRef.current?.scrollWidth || 0) > innerWidth; + const isScrollHorizontal = (innerRef.current?.scrollWidth || 0) > innerWidth; const items = virtualizer.getVirtualItems(); const { fixedWidth, remainingCols } = widthConstants; @@ -105,10 +98,6 @@ function BaseList( [expandedCache] ); - // Event listeners for scroll events - const parentScroll = useCallback(() => syncScroll(parentRef.current, headerRef.current), []); - const headerScroll = useCallback(() => syncScroll(headerRef.current, parentRef.current), []); - // effects // update pixel widths every time the width changes useLayoutEffect(() => { @@ -134,18 +123,6 @@ function BaseList( } }, [expandedRows]); - // Attach scroll listeners when component mounts - useEffect(() => { - parentRef.current?.addEventListener("scroll", parentScroll); - headerRef.current?.addEventListener("scroll", headerScroll); - - // Clean up event listeners when component unmounts - return () => { - parentRef.current?.removeEventListener("scroll", parentScroll); - headerRef.current?.removeEventListener("scroll", headerScroll); - }; - }, []); - // provide access to window functions useImperativeHandle(ref, () => ({ scrollTo: (scrollOffset: number): void => virtualizer.scrollToOffset(scrollOffset), @@ -162,11 +139,10 @@ function BaseList( style={{ ...style, height, width }} >
[]} + columns={columns} className={headerClassname} style={headerStyle} onSort={onSort} @@ -181,7 +157,7 @@ function BaseList( transform: `translateY(${items[0]?.start ?? 0}px)` }} > -
+
{items.map(({ index }) => { const row = data[index]; const fargs = { row, index }; @@ -222,6 +198,7 @@ function BaseList( pixelWidths={pixelWidths} className={footerClassname} style={footerStyle} + isScrollHorizontal={isScrollHorizontal} component={footerComponent} />
diff --git a/src/styles/main.css b/src/styles/main.css index 67026bc..81f9260 100644 --- a/src/styles/main.css +++ b/src/styles/main.css @@ -20,13 +20,11 @@ top: 0; left: 0; z-index: 1; - overflow-x: auto; - -ms-overflow-style: none; - scrollbar-width: none; } -.rft-sticky-header::-webkit-scrollbar { - display: none; +.rft-sticky-header.scroll, +.rft-footer.scroll { + width: fit-content; } .rft-header { @@ -92,12 +90,6 @@ bottom: 0; left: 0; z-index: 1; - overflow-x: auto; - scrollbar-width: none; /* Firefox */ -} - -.rft-footer.sticky::-webkit-scrollbar { - display: none; /* Safari and Chrome */ } .rft-cell { diff --git a/src/util.ts b/src/util.ts index b3add93..6993385 100644 --- a/src/util.ts +++ b/src/util.ts @@ -47,8 +47,6 @@ const randomString = (num: number) => { const findElementByValue = (value: string) => document.querySelector(value); -const findTableByUuid = (uuid: string) => findElementByValue(`[data-table-key='${uuid}']`); - const findHeaderByUuid = (uuid: string) => findElementByValue(`[data-header-key='${uuid}-header']`); const findFooterByUuid = (uuid: string) => findElementByValue(`[data-footer-key='${uuid}-footer']`); @@ -116,6 +114,5 @@ export { findColumnWidthConstants, findFooterByUuid, findHeaderByUuid, - findTableByUuid, randomString };