From 77ff20314e19fabd4ce1c9a3aa4320f693a505d6 Mon Sep 17 00:00:00 2001 From: machinedramon Date: Thu, 13 Feb 2025 07:45:57 -0300 Subject: [PATCH 1/4] feat: added slider prop to invert position --- src/components/ui/slider.tsx | 56 +++++++++++++++++++++++++++--------- 1 file changed, 42 insertions(+), 14 deletions(-) diff --git a/src/components/ui/slider.tsx b/src/components/ui/slider.tsx index d9bf0c3..7b36868 100644 --- a/src/components/ui/slider.tsx +++ b/src/components/ui/slider.tsx @@ -6,23 +6,51 @@ interface CustomProps { trackClassName?: string; rangeClassName?: string; thumbClassName?: string; + invertBorder?: boolean; } const Slider = React.forwardRef, React.ComponentPropsWithoutRef & CustomProps>( - ({ className, trackClassName, rangeClassName, thumbClassName, ...props }, ref) => ( - - - - - -
- - ), + ({ className, trackClassName, rangeClassName, thumbClassName, invertBorder = false, value, min = 0, max = 100, onValueChange, ...props }, ref) => { + // Ajusta o valor para manter a mesma posição visual quando invertido + const adjustedValue = React.useMemo(() => { + return invertBorder ? value?.map((v) => max + min - v) : value; + }, [value, invertBorder, max, min]); + + // Handler que re-ajusta o valor antes de enviá-lo para o callback original + const handleValueChange = React.useCallback( + (newValue: number[]) => { + if (onValueChange) { + const readjustedValue = invertBorder ? newValue.map((v) => max + min - v) : newValue; + onValueChange(readjustedValue); + } + }, + [onValueChange, invertBorder, max, min], + ); + + return ( + + + + + +
+ + ); + }, ); Slider.displayName = SliderPrimitive.Root.displayName; From 8df59585be19637f613059130c2160b3edad2ebc Mon Sep 17 00:00:00 2001 From: semantic-release-bot Date: Thu, 13 Feb 2025 11:03:46 +0000 Subject: [PATCH 2/4] chore(release): 1.6.0-dev.1 # [1.6.0-dev.1](https://github.com/betfinio/components/compare/v1.5.12...v1.6.0-dev.1) (2025-02-13) ### Features * added slider prop to invert position ([77ff203](https://github.com/betfinio/components/commit/77ff20314e19fabd4ce1c9a3aa4320f693a505d6)) --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index f7fb159..4e71645 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@betfinio/components", - "version": "1.5.12", + "version": "1.6.0-dev.1", "type": "module", "exports": { ".": { From a824afb6f6e6daff1494641e9da2692bc5b8e6f9 Mon Sep 17 00:00:00 2001 From: Plane Date: Mon, 17 Feb 2025 23:16:55 +0100 Subject: [PATCH 3/4] feat: add server-side pagination support to tables --- package.json | 5 +- src/components/shared/DataTable.tsx | 125 +++++++++++++++++++--------- src/components/ui/table.tsx | 57 ++++++++----- 3 files changed, 120 insertions(+), 67 deletions(-) diff --git a/package.json b/package.json index 4e71645..c9e6324 100644 --- a/package.json +++ b/package.json @@ -31,10 +31,7 @@ }, "module": "./dist/index.js", "types": "./dist/src/index.d.ts", - "files": [ - "dist", - "tailwind.config.js" - ], + "files": ["dist", "tailwind.config.js"], "scripts": { "build": "rslib build", "check": "biome check --write", diff --git a/src/components/shared/DataTable.tsx b/src/components/shared/DataTable.tsx index 1e4f497..3363a0e 100644 --- a/src/components/shared/DataTable.tsx +++ b/src/components/shared/DataTable.tsx @@ -1,12 +1,17 @@ -import type { InitialTableState, Row, Table as TanstackTable } from '@tanstack/react-table'; +import type { InitialTableState, OnChangeFn, Row, Table as TanstackTable } from '@tanstack/react-table'; import { type ColumnDef, type TableMeta, flexRender, getCoreRowModel, getPaginationRowModel, getSortedRowModel, useReactTable } from '@tanstack/react-table'; import { cva } from 'class-variance-authority'; -import { ArrowDown, ArrowDownIcon, ArrowUp, ArrowUpDown, ArrowUpIcon, ChevronsUpDown, Loader, MoveDown } from 'lucide-react'; +import { ArrowDownIcon, ArrowUpDownIcon, ArrowUpIcon, Loader } from 'lucide-react'; import * as React from 'react'; import { cn, cn as cx } from '../../lib/utils'; -import { DataTablePagination, Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from '../ui/table'; +import { DataTablePagination, DataTablePaginationProps, Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from '../ui/table'; -interface DataTableProps { +interface PaginationState { + pageIndex: number; + pageSize: number; +} + +type BaseDataTableProps = { columns: ColumnDef[]; data: TData[]; isLoading?: boolean; @@ -20,7 +25,24 @@ interface DataTableProps { enableSorting?: boolean; withZebra?: boolean; className?: string; -} + serverPagination?: boolean; +}; + +type TableWithClientPaginationProps = BaseDataTableProps & { + serverPagination?: false | undefined; + totalCount?: number; + pagination?: PaginationState; + onPaginationChange?: (pagination: PaginationState) => void; +}; + +type TableWithServerPaginationProps = BaseDataTableProps & { + serverPagination: true; + totalCount: number; + pagination: PaginationState; + onPaginationChange: (pagination: PaginationState) => void; +}; + +export type DataTableProps = TableWithClientPaginationProps | TableWithServerPaginationProps; export function DataTable({ columns, @@ -32,29 +54,50 @@ export function DataTable({ onRowClick, loaderClassName, noResultsClassName, - tableRef, // Accept the tableRef prop - enableSorting = false, // Add default value + tableRef, + enableSorting = false, withZebra = true, className = '', + serverPagination = false, + totalCount, + pagination: controlledPagination, + onPaginationChange: controlledOnPaginationChange, }: DataTableProps) { - React.useImperativeHandle(tableRef, () => table); + const [internalPagination, setInternalPagination] = React.useState({ + pageIndex: 0, + pageSize: 5, + }); + const effectivePagination = controlledPagination || internalPagination; + + const handlePaginationChange = React.useCallback( + (updater: (prev: PaginationState) => PaginationState) => { + const newPagination = updater(effectivePagination); + if (serverPagination && controlledOnPaginationChange) { + controlledOnPaginationChange(newPagination); + } else { + setInternalPagination(newPagination); + } + }, + [effectivePagination, serverPagination, controlledOnPaginationChange], + ); + const table = useReactTable({ data, columns, getCoreRowModel: getCoreRowModel(), - getPaginationRowModel: getPaginationRowModel(), + getPaginationRowModel: serverPagination ? undefined : getPaginationRowModel(), getSortedRowModel: getSortedRowModel(), enableSorting, - meta: meta, - initialState: { - pagination: { - pageSize: 5, - pageIndex: 0, - }, - ...state, - }, + meta, + manualPagination: serverPagination, + rowCount: serverPagination && totalCount !== undefined ? totalCount : undefined, + state: { pagination: effectivePagination }, + onPaginationChange: handlePaginationChange as OnChangeFn, + initialState: { pagination: { pageIndex: 0, pageSize: 5 }, ...state }, }); + React.useImperativeHandle(tableRef, () => table, [table]); + return (
@@ -62,26 +105,24 @@ export function DataTable({ {table.getHeaderGroups().map((headerGroup) => ( - {headerGroup.headers.map((header) => { - return ( - -
- {header.isPlaceholder ? null : flexRender(header.column.columnDef.header, header.getContext())} - {header.column.getCanSort() && ( -
- {header.column.getIsSorted() === 'asc' && } - {header.column.getIsSorted() === 'desc' && } - {header.column.getIsSorted() === false && } -
- )} -
-
- ); - })} + {headerGroup.headers.map((header) => ( + +
+ {header.isPlaceholder ? null : flexRender(header.column.columnDef.header, header.getContext())} + {header.column.getCanSort() && ( +
+ {header.column.getIsSorted() === 'asc' && } + {header.column.getIsSorted() === 'desc' && } + {header.column.getIsSorted() === false && } +
+ )} +
+
+ ))}
))}
@@ -89,16 +130,18 @@ export function DataTable({ {isLoading ? ( -
- +
+
) : table.getRowModel().rows?.length ? ( table.getRowModel().rows.map((row, index) => ( onRowClick?.(row.original, row)} data-row-id={row.id} @@ -120,7 +163,7 @@ export function DataTable({
- {!hidePagination && } + {!hidePagination && }
); } diff --git a/src/components/ui/table.tsx b/src/components/ui/table.tsx index 661f928..5456e58 100644 --- a/src/components/ui/table.tsx +++ b/src/components/ui/table.tsx @@ -1,12 +1,11 @@ import type { Table as ReactTable } from '@tanstack/react-table'; import * as React from 'react'; -import { Button } from './button.tsx'; - import { motion } from 'framer-motion'; import { ChevronLeft, ChevronRight, ChevronsLeft, ChevronsRight } from 'lucide-react'; import { useTranslation } from 'react-i18next'; import { cn } from '../../lib/utils'; +import { Button } from './button.tsx'; import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from './select'; const Table = React.forwardRef>(({ className, ...props }, ref) => ( @@ -54,13 +53,32 @@ TableCaption.displayName = 'TableCaption'; interface DataTablePaginationProps { table: ReactTable; className?: string; + isLoading?: boolean; } -function DataTablePagination({ table, className = '' }: DataTablePaginationProps) { +function DataTablePagination({ table, className = '', isLoading = false }: DataTablePaginationProps) { const { t } = useTranslation('shared', { keyPrefix: 'tables' }); + const { pageIndex, pageSize } = table.getState().pagination; + + const computedPageCount = table.getPageCount(); + const canPreviousPage = pageIndex > 0; + const canNextPage = table.getCanNextPage(); + + const handlePageChange = (newPageIndex: number) => { + table.setPageIndex(newPageIndex); + }; - if (table.getFilteredRowModel().rows.length === 0) { - return
; + const handlePageSizeChange = (value: string) => { + table.setPageSize(Number(value)); + }; + + const totalCount = table.options.rowCount as number | undefined; + + // rowCount is 'undfined' on client-side pagination, we can use it as a flag to pick rows count from filtered model + const resultsCount = totalCount !== undefined ? totalCount : table.getFilteredRowModel().rows.length; + + if (resultsCount === 0) { + return
; } return ( ({ table, className = '' }: DataTablePaginati className={cn('flex items-center justify-between py-2 mt-2', className)} >
- {table.getFilteredRowModel().rows.length} {t('results')}. + {resultsCount} {t('results')}.

{t('resultsPerPage')}

- - + {[5, 10, 20, 30, 40, 50].map((pageSize) => ( @@ -94,28 +107,28 @@ function DataTablePagination({ table, className = '' }: DataTablePaginati
- {t('page')} {table.getState().pagination.pageIndex + 1} - {t('of')} - {table.getPageCount()} + {t('page')} {pageIndex + 1} + {t('of')} + {computedPageCount}
- - -