Mejorada la búsqueda rápida
This commit is contained in:
parent
e76bdd9970
commit
ee97a5800b
@ -20,6 +20,8 @@ type SimpleSearchInputProps = {
|
|||||||
|
|
||||||
const SEARCH_HISTORY_KEY = "search_history";
|
const SEARCH_HISTORY_KEY = "search_history";
|
||||||
|
|
||||||
|
const normalizeSearchValue = (value: string) => value.trim().replace(/\s+/g, " ");
|
||||||
|
|
||||||
export const SimpleSearchInput = ({
|
export const SimpleSearchInput = ({
|
||||||
value,
|
value,
|
||||||
onSearchChange,
|
onSearchChange,
|
||||||
@ -33,84 +35,102 @@ export const SimpleSearchInput = ({
|
|||||||
const [open, setOpen] = useState(false);
|
const [open, setOpen] = useState(false);
|
||||||
const inputRef = useRef<HTMLInputElement>(null);
|
const inputRef = useRef<HTMLInputElement>(null);
|
||||||
|
|
||||||
|
// Evita que el siguiente debounce pise una búsqueda inmediata
|
||||||
|
const skipNextDebouncedEmitRef = useRef(false);
|
||||||
|
|
||||||
const debouncedValue = useDebounce(searchValue, 300);
|
const debouncedValue = useDebounce(searchValue, 300);
|
||||||
|
|
||||||
// Load from localStorage on mount
|
useEffect(() => {
|
||||||
|
setSearchValue(value);
|
||||||
|
}, [value]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const stored = localStorage.getItem(SEARCH_HISTORY_KEY);
|
const stored = localStorage.getItem(SEARCH_HISTORY_KEY);
|
||||||
if (stored) setHistory(JSON.parse(stored));
|
if (!stored) return;
|
||||||
|
|
||||||
|
try {
|
||||||
|
const parsed = JSON.parse(stored) as string[];
|
||||||
|
setHistory(Array.isArray(parsed) ? parsed : []);
|
||||||
|
} catch {
|
||||||
|
setHistory([]);
|
||||||
|
}
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
// Emit changes after debounce
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
onSearchChange(debouncedValue);
|
if (skipNextDebouncedEmitRef.current) {
|
||||||
|
skipNextDebouncedEmitRef.current = false;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
onSearchChange(normalizeSearchValue(debouncedValue));
|
||||||
}, [debouncedValue, onSearchChange]);
|
}, [debouncedValue, onSearchChange]);
|
||||||
|
|
||||||
// Save history to localStorage
|
|
||||||
const saveHistory = (term: string) => {
|
const saveHistory = (term: string) => {
|
||||||
if (!term.trim()) return;
|
const cleaned = normalizeSearchValue(term);
|
||||||
const cleaned = term.trim();
|
if (!cleaned) return;
|
||||||
const newHistory = [cleaned, ...history.filter((h) => h !== cleaned)].slice(0, maxHistory);
|
|
||||||
setHistory(newHistory);
|
setHistory((prev) => {
|
||||||
localStorage.setItem(SEARCH_HISTORY_KEY, JSON.stringify(newHistory));
|
const nextHistory = [cleaned, ...prev.filter((item) => item !== cleaned)].slice(
|
||||||
|
0,
|
||||||
|
maxHistory
|
||||||
|
);
|
||||||
|
localStorage.setItem(SEARCH_HISTORY_KEY, JSON.stringify(nextHistory));
|
||||||
|
return nextHistory;
|
||||||
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
const clearHistory = () => {
|
|
||||||
setHistory([]);
|
|
||||||
localStorage.removeItem(SEARCH_HISTORY_KEY);
|
|
||||||
};
|
|
||||||
|
|
||||||
// Input handlers
|
|
||||||
const handleInputChange = (e: React.ChangeEvent<HTMLInputElement>) => {
|
const handleInputChange = (e: React.ChangeEvent<HTMLInputElement>) => {
|
||||||
const cleaned = e.target.value.trimStart().replace(/\s+/g, " ");
|
setSearchValue(e.target.value);
|
||||||
setSearchValue(cleaned);
|
};
|
||||||
|
|
||||||
|
const handleImmediateSearch = (rawValue: string) => {
|
||||||
|
const nextValue = normalizeSearchValue(rawValue);
|
||||||
|
|
||||||
|
skipNextDebouncedEmitRef.current = true;
|
||||||
|
setSearchValue(nextValue);
|
||||||
|
setLastSearch(nextValue);
|
||||||
|
onSearchChange(nextValue);
|
||||||
|
saveHistory(nextValue);
|
||||||
|
setOpen(false);
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleClear = () => {
|
const handleClear = () => {
|
||||||
setSearchValue("");
|
handleImmediateSearch("");
|
||||||
onSearchChange("");
|
inputRef.current?.focus();
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleKeyDown = (e: React.KeyboardEvent<HTMLInputElement>) => {
|
const handleKeyDown = (e: React.KeyboardEvent<HTMLInputElement>) => {
|
||||||
// Enter → búsqueda inmediata
|
const currentValue = e.currentTarget.value;
|
||||||
|
|
||||||
if (e.key === "Enter" && !e.shiftKey && !e.metaKey && !e.ctrlKey) {
|
if (e.key === "Enter" && !e.shiftKey && !e.metaKey && !e.ctrlKey) {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
onSearchChange(searchValue);
|
handleImmediateSearch(currentValue);
|
||||||
setLastSearch(searchValue);
|
return;
|
||||||
saveHistory(searchValue);
|
|
||||||
setOpen(false);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Shift+Enter → repetir última búsqueda
|
|
||||||
if (e.key === "Enter" && e.shiftKey) {
|
if (e.key === "Enter" && e.shiftKey) {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
if (lastSearch) {
|
if (!lastSearch) return;
|
||||||
onSearchChange(lastSearch);
|
|
||||||
setSearchValue(lastSearch);
|
handleImmediateSearch(lastSearch);
|
||||||
}
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Ctrl/Cmd+Enter → limpiar
|
|
||||||
if (e.key === "Enter" && (e.ctrlKey || e.metaKey)) {
|
if (e.key === "Enter" && (e.ctrlKey || e.metaKey)) {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
handleClear();
|
handleClear();
|
||||||
inputRef.current?.focus();
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleSelectHistory = (term: string) => {
|
const handleSelectHistory = (term: string) => {
|
||||||
setSearchValue(term);
|
handleImmediateSearch(term);
|
||||||
onSearchChange(term);
|
|
||||||
setLastSearch(term);
|
|
||||||
setOpen(false);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="relative flex flex-1 items-center gap-4">
|
<div className="relative flex flex-1 items-center gap-4">
|
||||||
<InputGroup className="bg-background " data-disabled={loading}>
|
<InputGroup className="bg-background">
|
||||||
<InputGroupInput
|
<InputGroupInput
|
||||||
autoComplete="off"
|
autoComplete="off"
|
||||||
disabled={loading}
|
|
||||||
inputMode="search"
|
inputMode="search"
|
||||||
onChange={handleInputChange}
|
onChange={handleInputChange}
|
||||||
onFocus={() => history.length > 0 && setOpen(true)}
|
onFocus={() => history.length > 0 && setOpen(true)}
|
||||||
@ -120,26 +140,33 @@ export const SimpleSearchInput = ({
|
|||||||
spellCheck={false}
|
spellCheck={false}
|
||||||
value={searchValue}
|
value={searchValue}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<InputGroupAddon>
|
<InputGroupAddon>
|
||||||
<SearchIcon aria-hidden />
|
<SearchIcon aria-hidden className="text-muted-foreground" />
|
||||||
</InputGroupAddon>
|
</InputGroupAddon>
|
||||||
|
|
||||||
<InputGroupAddon align="inline-end">
|
<InputGroupAddon align="inline-end">
|
||||||
{loading && (
|
{loading && (
|
||||||
<Spinner aria-label={t("components.simple_search_input.loading", "Loading")} />
|
<Spinner
|
||||||
|
aria-label={t("components.simple_search_input.loading", "Loading")}
|
||||||
|
className="text-muted-foreground"
|
||||||
|
/>
|
||||||
)}
|
)}
|
||||||
</InputGroupAddon>
|
</InputGroupAddon>
|
||||||
</InputGroup>
|
</InputGroup>
|
||||||
|
|
||||||
{!(searchValue || loading) && (
|
{!(searchValue || loading) && (
|
||||||
<Button variant="outline">
|
<Button onClick={() => handleImmediateSearch(searchValue)} type="button" variant="outline">
|
||||||
{t("components.simple_search_input.search_button", "Search")}
|
{t("components.simple_search_input.search_button", "Search")}
|
||||||
</Button>
|
</Button>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{searchValue && !loading && (
|
{searchValue && !loading && (
|
||||||
<Button
|
<Button
|
||||||
aria-label={t("components.simple_search_input.clear_search", "Clear search")}
|
aria-label={t("components.simple_search_input.clear_search", "Clear search")}
|
||||||
className="cursor-pointer"
|
className="cursor-pointer"
|
||||||
onClick={handleClear}
|
onClick={handleClear}
|
||||||
|
type="button"
|
||||||
variant="outline"
|
variant="outline"
|
||||||
>
|
>
|
||||||
<XIcon aria-hidden className="size-4" />
|
<XIcon aria-hidden className="size-4" />
|
||||||
|
|||||||
@ -6,7 +6,7 @@ import { useListCustomersQuery } from "../../shared";
|
|||||||
|
|
||||||
export const useListCustomersController = () => {
|
export const useListCustomersController = () => {
|
||||||
const [pageIndex, setPageIndex] = useState(0);
|
const [pageIndex, setPageIndex] = useState(0);
|
||||||
const [pageSize, setPageSize] = useState(10);
|
const [pageSize, setPageSize] = useState(5);
|
||||||
const [search, setSearch] = useState("");
|
const [search, setSearch] = useState("");
|
||||||
|
|
||||||
const debouncedQ = useDebounce(search, 300);
|
const debouncedQ = useDebounce(search, 300);
|
||||||
@ -24,7 +24,27 @@ export const useListCustomersController = () => {
|
|||||||
const query = useListCustomersQuery({ criteria });
|
const query = useListCustomersQuery({ criteria });
|
||||||
|
|
||||||
const setSearchValue = (value: string) => {
|
const setSearchValue = (value: string) => {
|
||||||
setSearch(value.trim().replace(/\s+/g, " "));
|
const nextValue = value.trim().replace(/\s+/g, " ");
|
||||||
|
|
||||||
|
setSearch((prev) => {
|
||||||
|
if (prev === nextValue) return prev;
|
||||||
|
|
||||||
|
// Sólo si la búsqueda realmente cambia,
|
||||||
|
// reseteamos la página a 0 para evitar inconsistencias
|
||||||
|
setPageIndex(0);
|
||||||
|
return nextValue;
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
const setPageSizeValue = (value: number) => {
|
||||||
|
setPageSize((prev) => {
|
||||||
|
if (prev === value) return prev;
|
||||||
|
|
||||||
|
// Sólo si el tamaño de página realmente cambia,
|
||||||
|
// reseteamos la página a 0 para evitar inconsistencias
|
||||||
|
setPageIndex(0);
|
||||||
|
return value;
|
||||||
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
return {
|
return {
|
||||||
@ -40,7 +60,7 @@ export const useListCustomersController = () => {
|
|||||||
pageIndex,
|
pageIndex,
|
||||||
pageSize,
|
pageSize,
|
||||||
setPageIndex,
|
setPageIndex,
|
||||||
setPageSize,
|
setPageSize: setPageSizeValue,
|
||||||
|
|
||||||
search,
|
search,
|
||||||
setSearchValue,
|
setSearchValue,
|
||||||
|
|||||||
@ -24,53 +24,36 @@ import type { DataTableMeta } from "./data-table.tsx";
|
|||||||
|
|
||||||
interface DataTablePaginationProps<TData> {
|
interface DataTablePaginationProps<TData> {
|
||||||
table: Table<TData>;
|
table: Table<TData>;
|
||||||
onPageChange?: (pageIndex: number) => void;
|
|
||||||
onPageSizeChange?: (pageSize: number) => void;
|
|
||||||
|
|
||||||
className?: string;
|
className?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function DataTablePagination<TData>({
|
export function DataTablePagination<TData>({ table, className }: DataTablePaginationProps<TData>) {
|
||||||
table,
|
|
||||||
onPageChange,
|
|
||||||
onPageSizeChange,
|
|
||||||
className,
|
|
||||||
}: DataTablePaginationProps<TData>) {
|
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
|
|
||||||
const { pageIndex: rawIndex, pageSize: rawSize } = table.getState().pagination;
|
const { pageIndex: rawIndex, pageSize: rawSize } = table.getState().pagination;
|
||||||
const meta = table.options.meta as DataTableMeta<TData>;
|
const meta = table.options.meta as DataTableMeta<TData>;
|
||||||
const totalRows = meta?.totalItems ?? table.getFilteredRowModel().rows.length;
|
const totalRows = meta?.totalItems ?? table.getFilteredRowModel().rows.length;
|
||||||
|
|
||||||
// Normalización segura
|
|
||||||
const pageIndex = Number.isFinite(rawIndex) && rawIndex >= 0 ? rawIndex : 0;
|
const pageIndex = Number.isFinite(rawIndex) && rawIndex >= 0 ? rawIndex : 0;
|
||||||
const pageSize = Number.isFinite(rawSize) && rawSize > 0 ? rawSize : 10;
|
const pageSize = Number.isFinite(rawSize) && rawSize > 0 ? rawSize : 10;
|
||||||
|
const pageCount = Math.max(1, Math.ceil(totalRows / pageSize));
|
||||||
const pageCount = table.getPageCount() || Math.max(1, Math.ceil((totalRows || 0) / pageSize));
|
|
||||||
const hasSelected = table.getFilteredSelectedRowModel().rows.length > 0;
|
const hasSelected = table.getFilteredSelectedRowModel().rows.length > 0;
|
||||||
|
|
||||||
// Rango visible (1-based en UI)
|
|
||||||
const start = totalRows > 0 ? pageIndex * pageSize + 1 : 0;
|
const start = totalRows > 0 ? pageIndex * pageSize + 1 : 0;
|
||||||
const end = totalRows > 0 ? Math.min(start + pageSize - 1, totalRows) : 0;
|
const end = totalRows > 0 ? Math.min(start + pageSize - 1, totalRows) : 0;
|
||||||
|
|
||||||
// Handlers de navegación controlada
|
const gotoPage = (index: number) => {
|
||||||
const notify = (next: Partial<{ pageIndex: number; pageSize: number }>) =>
|
const nextIndex = Math.max(0, Math.min(index, pageCount - 1));
|
||||||
table.options.onPaginationChange?.({ pageIndex, pageSize, ...next });
|
table.setPageIndex(nextIndex);
|
||||||
|
};
|
||||||
|
|
||||||
const gotoPage = (index: number) =>
|
const handlePageSizeChange = (size: string) => {
|
||||||
onPageChange ? onPageChange(index) : notify({ pageIndex: index });
|
table.setPageSize(Number(size));
|
||||||
const handlePageSizeChange = (size: string) =>
|
};
|
||||||
onPageSizeChange ? onPageSizeChange(Number(size)) : notify({ pageSize: Number(size) });
|
|
||||||
|
|
||||||
const gotoPreviousPage = () => gotoPage(pageIndex - 1);
|
|
||||||
const gotoNextPage = () => gotoPage(pageIndex + 1);
|
|
||||||
const gotoFirstPage = () => gotoPage(0);
|
|
||||||
const gotoLastPage = () => gotoPage(pageCount - 1);
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={cn("flex items-center justify-between text-muted-foreground", className)}>
|
<div className={cn("flex items-center justify-between text-muted-foreground", className)}>
|
||||||
{/* Información izquierda */}
|
<div className="flex flex-1 flex-col items-center gap-4 sm:flex-row">
|
||||||
<div className="flex flex-col sm:flex-row items-center gap-4 flex-1 ">
|
|
||||||
<span aria-live="polite">
|
<span aria-live="polite">
|
||||||
{t("components.datatable.pagination.showing_range", { start, end, total: totalRows })}
|
{t("components.datatable.pagination.showing_range", { start, end, total: totalRows })}
|
||||||
</span>
|
</span>
|
||||||
@ -85,12 +68,11 @@ export function DataTablePagination<TData>({
|
|||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Controles derecha */}
|
|
||||||
<div className="flex items-center gap-2">
|
<div className="flex items-center gap-2">
|
||||||
<div className="flex items-center gap-2">
|
<div className="flex items-center gap-2">
|
||||||
<span>{t("components.datatable.pagination.rows_per_page")}</span>
|
<span>{t("components.datatable.pagination.rows_per_page")}</span>
|
||||||
<Select onValueChange={handlePageSizeChange} value={String(pageSize)}>
|
<Select onValueChange={handlePageSizeChange} value={String(pageSize)}>
|
||||||
<SelectTrigger className="w-20 h-8 bg-white border-gray-200">
|
<SelectTrigger className="h-8 w-20 border-gray-200 bg-white">
|
||||||
<SelectValue placeholder={String(pageSize)} />
|
<SelectValue placeholder={String(pageSize)} />
|
||||||
</SelectTrigger>
|
</SelectTrigger>
|
||||||
<SelectContent>
|
<SelectContent>
|
||||||
@ -102,16 +84,15 @@ export function DataTablePagination<TData>({
|
|||||||
</SelectContent>
|
</SelectContent>
|
||||||
</Select>
|
</Select>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<Pagination>
|
<Pagination>
|
||||||
<PaginationContent>
|
<PaginationContent>
|
||||||
<PaginationItem>
|
<PaginationItem>
|
||||||
<PaginationLink
|
<PaginationLink
|
||||||
aria-label={t("components.datatable.pagination.goto_first_page")}
|
aria-label={t("components.datatable.pagination.goto_first_page")}
|
||||||
className="px-2.5 cursor-pointer"
|
className="cursor-pointer px-2.5"
|
||||||
isActive={pageIndex > 0}
|
isActive={pageIndex > 0}
|
||||||
onClick={() => {
|
onClick={() => pageIndex > 0 && gotoPage(0)}
|
||||||
if (pageIndex > 0) gotoFirstPage();
|
|
||||||
}}
|
|
||||||
size="sm"
|
size="sm"
|
||||||
>
|
>
|
||||||
<ChevronsLeftIcon className="size-4" />
|
<ChevronsLeftIcon className="size-4" />
|
||||||
@ -121,29 +102,28 @@ export function DataTablePagination<TData>({
|
|||||||
<PaginationItem>
|
<PaginationItem>
|
||||||
<PaginationLink
|
<PaginationLink
|
||||||
aria-label={t("components.datatable.pagination.goto_previous_page")}
|
aria-label={t("components.datatable.pagination.goto_previous_page")}
|
||||||
className="px-2.5 cursor-pointer"
|
className="cursor-pointer px-2.5"
|
||||||
isActive={pageIndex > 0}
|
isActive={pageIndex > 0}
|
||||||
onClick={() => {
|
onClick={() => pageIndex > 0 && gotoPage(pageIndex - 1)}
|
||||||
if (pageIndex > 0) gotoPreviousPage();
|
|
||||||
}}
|
|
||||||
size="sm"
|
size="sm"
|
||||||
>
|
>
|
||||||
<ChevronLeftIcon className="size-4" />
|
<ChevronLeftIcon className="size-4" />
|
||||||
</PaginationLink>
|
</PaginationLink>
|
||||||
</PaginationItem>
|
</PaginationItem>
|
||||||
|
|
||||||
<span aria-live="polite" className="text-sm text-muted-foreground px-2">
|
<span aria-live="polite" className="px-2 text-sm text-muted-foreground">
|
||||||
{t("components.datatable.pagination.page_of", { page: pageIndex + 1, of: pageCount })}
|
{t("components.datatable.pagination.page_of", {
|
||||||
|
page: pageIndex + 1,
|
||||||
|
of: pageCount,
|
||||||
|
})}
|
||||||
</span>
|
</span>
|
||||||
|
|
||||||
<PaginationItem>
|
<PaginationItem>
|
||||||
<PaginationLink
|
<PaginationLink
|
||||||
aria-label={t("components.datatable.pagination.goto_next_page")}
|
aria-label={t("components.datatable.pagination.goto_next_page")}
|
||||||
className="px-2.5 cursor-pointer"
|
className="cursor-pointer px-2.5"
|
||||||
isActive={pageIndex < pageCount - 1}
|
isActive={pageIndex < pageCount - 1}
|
||||||
onClick={() => {
|
onClick={() => pageIndex < pageCount - 1 && gotoPage(pageIndex + 1)}
|
||||||
if (pageIndex < pageCount - 1) gotoNextPage();
|
|
||||||
}}
|
|
||||||
size="sm"
|
size="sm"
|
||||||
>
|
>
|
||||||
<ChevronRightIcon className="size-4" />
|
<ChevronRightIcon className="size-4" />
|
||||||
@ -153,11 +133,9 @@ export function DataTablePagination<TData>({
|
|||||||
<PaginationItem>
|
<PaginationItem>
|
||||||
<PaginationLink
|
<PaginationLink
|
||||||
aria-label={t("components.datatable.pagination.goto_last_page")}
|
aria-label={t("components.datatable.pagination.goto_last_page")}
|
||||||
className="px-2.5 cursor-pointer"
|
className="cursor-pointer px-2.5"
|
||||||
isActive={pageIndex < pageCount - 1}
|
isActive={pageIndex < pageCount - 1}
|
||||||
onClick={() => {
|
onClick={() => pageIndex < pageCount - 1 && gotoPage(pageCount - 1)}
|
||||||
if (pageIndex < pageCount - 1) gotoLastPage();
|
|
||||||
}}
|
|
||||||
size="sm"
|
size="sm"
|
||||||
>
|
>
|
||||||
<ChevronsRightIcon className="size-4" />
|
<ChevronsRightIcon className="size-4" />
|
||||||
|
|||||||
@ -137,10 +137,10 @@ export function DataTable<TData, TValue>({
|
|||||||
|
|
||||||
getRowId:
|
getRowId:
|
||||||
getRowId ??
|
getRowId ??
|
||||||
((originalRow: TData, i: number) =>
|
((originalRow: TData, i: number) => {
|
||||||
typeof (originalRow as any).id !== "undefined"
|
const row = originalRow as { id?: string | number };
|
||||||
? String((originalRow as any).id)
|
return row.id !== undefined ? String(row.id) : String(i);
|
||||||
: String(i)),
|
}),
|
||||||
|
|
||||||
state: {
|
state: {
|
||||||
columnSizing: colSizes,
|
columnSizing: colSizes,
|
||||||
@ -152,16 +152,21 @@ export function DataTable<TData, TValue>({
|
|||||||
},
|
},
|
||||||
|
|
||||||
manualPagination,
|
manualPagination,
|
||||||
pageCount: manualPagination
|
autoResetPageIndex: false,
|
||||||
? Math.ceil((totalItems ?? data.length) / (pageSize ?? 25))
|
|
||||||
: undefined,
|
pageCount: manualPagination ? Math.max(1, Math.ceil((totalItems ?? 0) / pageSize)) : undefined,
|
||||||
|
|
||||||
// Propagar cambios al padre
|
// Propagar cambios al padre
|
||||||
onPaginationChange: (updater) => {
|
onPaginationChange: (updater) => {
|
||||||
const next = typeof updater === "function" ? updater({ pageIndex, pageSize }) : updater;
|
const next = typeof updater === "function" ? updater({ pageIndex, pageSize }) : updater;
|
||||||
|
|
||||||
if (typeof next.pageIndex === "number") onPageChange?.(next.pageIndex);
|
if (typeof next.pageIndex === "number" && next.pageIndex !== pageIndex) {
|
||||||
if (typeof next.pageSize === "number") onPageSizeChange?.(next.pageSize);
|
onPageChange?.(next.pageIndex);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (typeof next.pageSize === "number" && next.pageSize !== pageSize) {
|
||||||
|
onPageSizeChange?.(next.pageSize);
|
||||||
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
enableRowSelection,
|
enableRowSelection,
|
||||||
@ -272,11 +277,7 @@ export function DataTable<TData, TValue>({
|
|||||||
<TableFooter className="bg-background">
|
<TableFooter className="bg-background">
|
||||||
<TableRow>
|
<TableRow>
|
||||||
<TableCell colSpan={100}>
|
<TableCell colSpan={100}>
|
||||||
<DataTablePagination
|
<DataTablePagination table={table} />
|
||||||
onPageChange={onPageChange}
|
|
||||||
onPageSizeChange={onPageSizeChange}
|
|
||||||
table={table}
|
|
||||||
/>
|
|
||||||
</TableCell>
|
</TableCell>
|
||||||
</TableRow>
|
</TableRow>
|
||||||
</TableFooter>
|
</TableFooter>
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user