From 1458ba4fed9278a190bcf1967dfd01af018196aa Mon Sep 17 00:00:00 2001 From: Sami Odeh Date: Tue, 24 Mar 2026 23:11:01 +0200 Subject: [PATCH] fix/data-attr-migration-css-exports --- .github/workflows/deploy-docs.yml | 5 ++- .github/workflows/publish.yml | 40 ++++++++++++++++-- .github/workflows/release.yml | 56 +++++++++++++++++++++++++ index.html | 7 +--- public/robots.txt | 2 +- public/sitemap.xml | 8 ++++ src/App.tsx | 4 +- src/Components/BodyRow.tsx | 5 +-- src/Components/ColumnCell.tsx | 3 +- src/Components/Draggable.tsx | 2 +- src/Components/RowCell.tsx | 2 +- src/Components/TableBody.tsx | 4 +- src/Components/TableContainer/index.tsx | 3 +- src/Components/TableHeader.tsx | 16 +++---- src/Components/style.css | 31 ++++++++------ src/hooks/useDragContextEvents.tsx | 8 ++-- src/hooks/useDropTarget.ts | 2 +- src/hooks/useIndexMaps.ts | 6 +-- vite.config.docs.ts | 4 ++ 19 files changed, 158 insertions(+), 50 deletions(-) create mode 100644 .github/workflows/release.yml create mode 100644 public/sitemap.xml diff --git a/.github/workflows/deploy-docs.yml b/.github/workflows/deploy-docs.yml index 1d47993..8e1a743 100644 --- a/.github/workflows/deploy-docs.yml +++ b/.github/workflows/deploy-docs.yml @@ -1,8 +1,9 @@ name: Deploy Docs to GitHub Pages on: - release: - types: [published] + push: + tags: + - 'v*' workflow_dispatch: permissions: diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index 23fad83..804f461 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -1,14 +1,43 @@ name: Publish to npm on: - release: - types: [published] + push: + tags: + - 'v*' jobs: + ci: + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Setup Node + uses: actions/setup-node@v4 + with: + node-version: 22 + cache: npm + + - name: Install dependencies + run: npm ci + + - name: Check formatting + run: npm run format:check + + - name: Lint + run: npm run lint + + - name: Build library + run: npm run build + + - name: Build docs + run: npm run build:docs + publish: + needs: ci runs-on: ubuntu-latest permissions: - contents: read + contents: write id-token: write steps: - name: Checkout @@ -31,3 +60,8 @@ jobs: run: npm publish --provenance --access public env: NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }} + + - name: Create GitHub Release + uses: softprops/action-gh-release@v2 + with: + generate_release_notes: true diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml new file mode 100644 index 0000000..43d01ff --- /dev/null +++ b/.github/workflows/release.yml @@ -0,0 +1,56 @@ +name: Release + +# Runs when a release/* branch is merged into main. +# Reads the version from package.json, updates index.html, +# commits the change, tags main, and pushes — triggering +# the publish and deploy-docs workflows. + +on: + pull_request: + types: [closed] + branches: [main] + +jobs: + tag: + if: github.event.pull_request.merged == true && startsWith(github.event.pull_request.head.ref, 'release/') + runs-on: ubuntu-latest + permissions: + contents: write + steps: + - name: Checkout + uses: actions/checkout@v4 + with: + ref: main + fetch-depth: 0 + + - name: Read version from package.json + id: version + run: echo "version=$(node -p 'require(\"./package.json\").version')" >> "$GITHUB_OUTPUT" + + - name: Update version in index.html + run: | + VERSION="${{ steps.version.outputs.version }}" + sed -i "s/\"softwareVersion\": \"[^\"]*\"/\"softwareVersion\": \"$VERSION\"/" index.html + + - name: Check for changes + id: diff + run: | + if git diff --quiet; then + echo "changed=false" >> "$GITHUB_OUTPUT" + else + echo "changed=true" >> "$GITHUB_OUTPUT" + fi + + - name: Commit version update + if: steps.diff.outputs.changed == 'true' + run: | + git config user.name "github-actions[bot]" + git config user.email "41898282+github-actions[bot]@users.noreply.github.com" + git add index.html + git commit -m "chore: update version to ${{ steps.version.outputs.version }}" + git push + + - name: Create and push tag + run: | + git tag "v${{ steps.version.outputs.version }}" + git push origin "v${{ steps.version.outputs.version }}" diff --git a/index.html b/index.html index 4e5ad06..4c888ff 100644 --- a/index.html +++ b/index.html @@ -71,7 +71,7 @@ "description": "React component library for drag-and-drop row and column reordering in tables. Features 60fps animations, auto-scroll, virtual scrolling for 100k+ rows, mobile long-press support, drag handles, and full style customization.", "url": "https://samiodeh1337.github.io/react-table-dnd/", "downloadUrl": "https://www.npmjs.com/package/react-table-dnd", - "softwareVersion": "1.0.0", + "softwareVersion": "2.0.8", "programmingLanguage": ["TypeScript", "React"], "license": "https://opensource.org/licenses/MIT", "author": { @@ -83,11 +83,6 @@ "@type": "Offer", "price": "0", "priceCurrency": "USD" - }, - "aggregateRating": { - "@type": "AggregateRating", - "ratingValue": "5", - "ratingCount": "1" } } diff --git a/public/robots.txt b/public/robots.txt index 5b8bb0a..d857c2f 100644 --- a/public/robots.txt +++ b/public/robots.txt @@ -1,4 +1,4 @@ User-agent: * Allow: / -Sitemap: https://samiodeh1337.github.io/sortable-table/sitemap.xml +Sitemap: https://samiodeh1337.github.io/react-table-dnd/sitemap.xml diff --git a/public/sitemap.xml b/public/sitemap.xml new file mode 100644 index 0000000..29df658 --- /dev/null +++ b/public/sitemap.xml @@ -0,0 +1,8 @@ + + + + https://samiodeh1337.github.io/react-table-dnd/ + weekly + 1.0 + + diff --git a/src/App.tsx b/src/App.tsx index 5b76d04..2336e0e 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -25,6 +25,8 @@ import { } from './Components' import './docs.css' +declare const __LIB_VERSION__: string + // Raw source imports for code preview (TSX — from real example files) import srcFixedTsx from './examples/example-fixed.tsx?raw' import srcFlexTsx from './examples/example-flex.tsx?raw' @@ -497,7 +499,7 @@ function App() { {/* Hero */}
-
v2.0.8 · MIT License
+
v{__LIB_VERSION__} · MIT License

The drag-and-drop table
diff --git a/src/Components/BodyRow.tsx b/src/Components/BodyRow.tsx index e9661dc..92b2a52 100644 --- a/src/Components/BodyRow.tsx +++ b/src/Components/BodyRow.tsx @@ -1,5 +1,4 @@ import Draggable from './Draggable' -import classNames from 'classnames' import React, { useMemo, memo } from 'react' import type { CSSProperties, ReactNode } from 'react' import type { DraggableProps } from './Draggable' @@ -22,11 +21,9 @@ const BodyRow: React.FC = memo(({ children, style, className, ...p () => (style ? { ...DEFAULT_STYLES, ...style } : DEFAULT_STYLES), [style], ) - const mergedClassName = classNames('tr', className) - return ( -
+
{children}
diff --git a/src/Components/ColumnCell.tsx b/src/Components/ColumnCell.tsx index dbfaea4..e8cbf52 100644 --- a/src/Components/ColumnCell.tsx +++ b/src/Components/ColumnCell.tsx @@ -36,7 +36,8 @@ const ColumnCell: React.FC = memo(({ children, style, className return (
diff --git a/src/Components/Draggable.tsx b/src/Components/Draggable.tsx index 921486c..a2e1ae6 100644 --- a/src/Components/Draggable.tsx +++ b/src/Components/Draggable.tsx @@ -54,7 +54,7 @@ const Draggable: React.FC = memo(({ children, id, index, type, s return (
= memo(({ children, style, className, ...p ) return ( -
+
{children}
) diff --git a/src/Components/TableBody.tsx b/src/Components/TableBody.tsx index f6fe87a..2872a30 100644 --- a/src/Components/TableBody.tsx +++ b/src/Components/TableBody.tsx @@ -60,9 +60,9 @@ const TableBody = forwardRef( }, [dispatch, localRef]) return ( -
+
( onMouseDown={dragStart} onTouchStart={touchStart} style={{ ...TABLE_DEFAULT_STYLES, ...style }} - className={`table ${className ?? ''}`} + data-rtdnd="table" + className={className} > {children}
diff --git a/src/Components/TableHeader.tsx b/src/Components/TableHeader.tsx index 9cda634..08b97ff 100644 --- a/src/Components/TableHeader.tsx +++ b/src/Components/TableHeader.tsx @@ -64,7 +64,9 @@ const TableHeader = forwardRef( useLayoutEffect(() => { const el = getRefCurrent(resolvedRef) if (el) { - const widths: number[] = Array.from(el.querySelectorAll('.th')).map((th) => { + const widths: number[] = Array.from( + el.querySelectorAll('[data-rtdnd="th"]'), + ).map((th) => { const w = th.getAttribute('data-width') return w ? parseInt(w, 10) : 0 }) @@ -75,23 +77,23 @@ const TableHeader = forwardRef( useLayoutEffect(() => { const el = getRefCurrent(resolvedRef) if (el) { - const ids: string[] = Array.from(el.querySelectorAll('.draggable')).map( - (d) => d.getAttribute('data-id') || '', - ) + const ids: string[] = Array.from( + el.querySelectorAll('[data-rtdnd="draggable"]'), + ).map((d) => d.getAttribute('data-id') || '') dispatch({ type: 'setColumnIds', value: ids }) } }, [children, dispatch, getRefCurrent, resolvedRef]) return ( -
+
-
+
{children}
diff --git a/src/Components/style.css b/src/Components/style.css index a72e677..0cbdcb3 100644 --- a/src/Components/style.css +++ b/src/Components/style.css @@ -8,16 +8,23 @@ touch-action: none; } +/* prevent native mobile long-press text selection on draggable elements */ +[data-flexitable-root] [data-id] { + -webkit-user-select: none; + user-select: none; + -webkit-touch-callout: none; +} + /* structural reset: targets only the library's own elements, never bleeds into consumer cell content */ [data-flexitable-root], -[data-flexitable-root] .table, -[data-flexitable-root] .header, -[data-flexitable-root] .thead, -[data-flexitable-root] .body, -[data-flexitable-root] .ibody, -[data-flexitable-root] .draggable, -[data-flexitable-root] .tr { +[data-flexitable-root] [data-rtdnd='table'], +[data-flexitable-root] [data-rtdnd='header'], +[data-flexitable-root] [data-rtdnd='thead'], +[data-flexitable-root] [data-rtdnd='body'], +[data-flexitable-root] [data-rtdnd='ibody'], +[data-flexitable-root] [data-rtdnd='draggable'], +[data-flexitable-root] [data-rtdnd='tr'] { box-sizing: border-box; margin: 0; padding: 0; @@ -29,8 +36,8 @@ /* user-facing cells: box-sizing only — padding/margin intentionally left unset so consumers can style freely via className and style props */ -[data-flexitable-root] .th, -[data-flexitable-root] .td { +[data-flexitable-root] [data-rtdnd='th'], +[data-flexitable-root] [data-rtdnd='td'] { box-sizing: border-box; } @@ -45,17 +52,17 @@ } /* hide webkit scrollbar on header scroll container (scrollLeft synced via JS) */ -[data-flexitable-root] .thead::-webkit-scrollbar { +[data-flexitable-root] [data-rtdnd='thead']::-webkit-scrollbar { display: none; } /* hide webkit scrollbar on virtual column drag body strip */ -[data-flexitable-root] .clone-body-strip::-webkit-scrollbar { +[data-flexitable-root] [data-rtdnd='clone-body-strip']::-webkit-scrollbar { display: none; } /* drag handle: only the handle shows grab cursor when not actively dragging */ -[data-flexitable-root]:not(.is-dragging) .draggable:has([data-drag-handle]) > div { +[data-flexitable-root]:not(.is-dragging) [data-rtdnd='draggable']:has([data-drag-handle]) > div { cursor: default !important; } diff --git a/src/hooks/useDragContextEvents.tsx b/src/hooks/useDragContextEvents.tsx index 1014316..33651b3 100644 --- a/src/hooks/useDragContextEvents.tsx +++ b/src/hooks/useDragContextEvents.tsx @@ -160,7 +160,7 @@ const useDragContextEvents = ( const idx = String(sourceIndex) // Header: clone just the .th element — mirrors React.cloneElement(children) from Draggable - const thEl = draggableEl.querySelector('.th') + const thEl = draggableEl.querySelector('[data-rtdnd="th"]') if (thEl) { const headerWrapper = document.createElement('div') headerWrapper.style.flexShrink = '0' @@ -174,7 +174,7 @@ const useDragContextEvents = ( bodyWrapper.style.flex = '1' const inner = document.createElement('div') - inner.className = 'rbody' + inner.dataset.rtdnd = 'rbody' inner.style.height = `${body.scrollHeight}px` inner.style.position = 'relative' @@ -183,7 +183,7 @@ const useDragContextEvents = ( if (isVirtualRef.current) { // Virtual rows have spacer divs alongside cells. Strip .tr down to just // the target cell so the column strip shows correctly at offset 0. - const trEl = rowClone.querySelector('.tr') + const trEl = rowClone.querySelector('[data-rtdnd="tr"]') if (trEl) { const targetCell = trEl.querySelector(`[data-col-index="${idx}"]`) while (trEl.firstChild) trEl.removeChild(trEl.firstChild) @@ -204,7 +204,7 @@ const useDragContextEvents = ( // The 'clone-body-strip' class hides scrollbar in WebKit via style.css bodyWrapper.style.overflow = 'auto' bodyWrapper.style.scrollbarWidth = 'none' - bodyWrapper.className = 'clone-body-strip' + bodyWrapper.dataset.rtdnd = 'clone-body-strip' } else { bodyWrapper.style.overflow = 'hidden' } diff --git a/src/hooks/useDropTarget.ts b/src/hooks/useDropTarget.ts index 283cccf..3825f68 100644 --- a/src/hooks/useDropTarget.ts +++ b/src/hooks/useDropTarget.ts @@ -30,7 +30,7 @@ const useDropTarget = (refs: HookRefs, options: Options): DropTargetResult => { const scrollTop = body.scrollTop const topOffset = body.getBoundingClientRect().top - const elements = body.querySelectorAll('.draggable[data-type="row"]') + const elements = body.querySelectorAll('[data-rtdnd="draggable"][data-type="row"]') let items: RowItem[] = [] for (let i = 0; i < elements.length; i++) { const el = elements[i] as HTMLElement diff --git a/src/hooks/useIndexMaps.ts b/src/hooks/useIndexMaps.ts index 2d9420d..5d6679c 100644 --- a/src/hooks/useIndexMaps.ts +++ b/src/hooks/useIndexMaps.ts @@ -29,7 +29,7 @@ const useIndexMaps = (refs: HookRefs): IndexMapsResult => { const buildRowMap = useCallback((body: HTMLElement) => { rowIndexMapRef.current.clear() - const rows = body.querySelectorAll('.draggable[data-type="row"]') + const rows = body.querySelectorAll('[data-rtdnd="draggable"][data-type="row"]') for (let i = 0; i < rows.length; i++) { const el = rows[i] as HTMLElement const idx = el.dataset.index @@ -41,7 +41,7 @@ const useIndexMaps = (refs: HookRefs): IndexMapsResult => { const buildColumnMap = useCallback((header: HTMLElement) => { colIndexMapRef.current.clear() - const cols = header.querySelectorAll('.draggable[data-type="column"]') + const cols = header.querySelectorAll('[data-rtdnd="draggable"][data-type="column"]') for (let i = 0; i < cols.length; i++) { const el = cols[i] as HTMLElement const idx = el.dataset.index @@ -53,7 +53,7 @@ const useIndexMaps = (refs: HookRefs): IndexMapsResult => { const buildCellMap = useCallback((body: HTMLElement) => { cellIndexMapRef.current.clear() - const cells = body.querySelectorAll('.td[data-col-index]') + const cells = body.querySelectorAll('[data-rtdnd="td"][data-col-index]') for (let i = 0; i < cells.length; i++) { const cell = cells[i] as HTMLElement const idx = +cell.dataset.colIndex! diff --git a/vite.config.docs.ts b/vite.config.docs.ts index cef089b..f905294 100644 --- a/vite.config.docs.ts +++ b/vite.config.docs.ts @@ -1,8 +1,12 @@ import { defineConfig } from 'vite' import react from '@vitejs/plugin-react' +import pkg from './package.json' with { type: 'json' } export default defineConfig({ plugins: [react()], + define: { + __LIB_VERSION__: JSON.stringify(pkg.version), + }, base: './', build: { outDir: 'docs-dist',