mirror of
https://github.com/Nezumi-2711/react-training.git
synced 2026-09-22 05:32:07 +00:00
Add pagination components and update some other components
This commit is contained in:
@@ -36,7 +36,7 @@ const Button = styled.button<IButtonStyle>`
|
||||
|
||||
&:disabled,
|
||||
&[disabled] {
|
||||
cursor: no-drop;
|
||||
cursor: not-allowed;
|
||||
}
|
||||
`;
|
||||
|
||||
|
||||
@@ -26,7 +26,6 @@ const OrderBy = ({ options }: IOrderProps) => {
|
||||
<OrderButton
|
||||
key={option.value}
|
||||
onClick={() => handleClick(option.value)}
|
||||
active={option.value === currentOrder}
|
||||
disabled={option.value === currentOrder}
|
||||
>
|
||||
{option.label}
|
||||
|
||||
@@ -9,7 +9,7 @@ const StyledOrder = styled.div`
|
||||
`;
|
||||
|
||||
interface IOrderBtn {
|
||||
active: boolean;
|
||||
disabled: boolean;
|
||||
}
|
||||
|
||||
const OrderButton = styled.button<IOrderBtn>`
|
||||
@@ -25,11 +25,11 @@ const OrderButton = styled.button<IOrderBtn>`
|
||||
padding: 5px 10px;
|
||||
|
||||
${(props) =>
|
||||
props.active &&
|
||||
props.disabled &&
|
||||
css`
|
||||
background-color: var(--primary-color);
|
||||
color: var(--light-text);
|
||||
cursor: no-drop;
|
||||
cursor: not-allowed;
|
||||
`}
|
||||
|
||||
&:hover:not(:disabled) {
|
||||
|
||||
@@ -0,0 +1,68 @@
|
||||
import { DEFAULT_PAGE_SIZE } from '@constant/config';
|
||||
import { useCallback, useMemo } from 'react';
|
||||
import { useSearchParams } from 'react-router-dom';
|
||||
import { Buttons, PaginationBtn, StyledPagination } from './styled';
|
||||
import { MdKeyboardArrowRight, MdKeyboardArrowLeft } from 'react-icons/md';
|
||||
|
||||
interface IPagination {
|
||||
count: number;
|
||||
}
|
||||
|
||||
const Pagination = ({ count }: IPagination) => {
|
||||
const [searchParams, setSearchParams] = useSearchParams();
|
||||
const currentPage = !searchParams.get('page')
|
||||
? 1
|
||||
: Number(searchParams.get('page'));
|
||||
|
||||
const totalPage = Math.ceil(count / DEFAULT_PAGE_SIZE);
|
||||
|
||||
const nextPage = useCallback(() => {
|
||||
const next = currentPage === totalPage ? currentPage : currentPage + 1;
|
||||
|
||||
searchParams.set('page', next.toString());
|
||||
setSearchParams(searchParams);
|
||||
}, [currentPage, searchParams, totalPage, setSearchParams]);
|
||||
|
||||
const previousPage = useCallback(() => {
|
||||
const previous = currentPage === 1 ? currentPage : currentPage - 1;
|
||||
|
||||
searchParams.set('page', previous.toString());
|
||||
setSearchParams(searchParams);
|
||||
}, [currentPage, searchParams, setSearchParams]);
|
||||
|
||||
const fromIndex = useMemo(
|
||||
() => (currentPage - 1) * DEFAULT_PAGE_SIZE + 1,
|
||||
[currentPage]
|
||||
);
|
||||
|
||||
const toIndex = useMemo(
|
||||
() => (currentPage === totalPage ? count : currentPage * DEFAULT_PAGE_SIZE),
|
||||
[currentPage, count, totalPage]
|
||||
);
|
||||
|
||||
return (
|
||||
totalPage > 1 && (
|
||||
<StyledPagination>
|
||||
<p>
|
||||
Showing {fromIndex} to {toIndex} of <span>{count}</span> results
|
||||
</p>
|
||||
|
||||
<Buttons>
|
||||
<PaginationBtn onClick={previousPage} disabled={currentPage === 1}>
|
||||
<MdKeyboardArrowLeft /> <span>Previous</span>
|
||||
</PaginationBtn>
|
||||
|
||||
<PaginationBtn
|
||||
onClick={nextPage}
|
||||
disabled={currentPage === totalPage}
|
||||
>
|
||||
<span>Next</span>
|
||||
<MdKeyboardArrowRight />
|
||||
</PaginationBtn>
|
||||
</Buttons>
|
||||
</StyledPagination>
|
||||
)
|
||||
);
|
||||
};
|
||||
|
||||
export default Pagination;
|
||||
@@ -0,0 +1,50 @@
|
||||
import styled from 'styled-components';
|
||||
|
||||
interface IPaginationBtn {
|
||||
disabled: boolean;
|
||||
}
|
||||
|
||||
const StyledPagination = styled.div`
|
||||
width: 100%;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
font-size: var(--fs-sm-x);
|
||||
`;
|
||||
|
||||
const Buttons = styled.div`
|
||||
display: flex;
|
||||
gap: 30px;
|
||||
`;
|
||||
|
||||
const PaginationBtn = styled.button<IPaginationBtn>`
|
||||
background-color: var(--secondary-btn-color);
|
||||
border: none;
|
||||
border-radius: var(--radius-sm);
|
||||
font-weight: 500;
|
||||
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
gap: 10px;
|
||||
|
||||
padding: 5px 10px;
|
||||
|
||||
transition: all 0.3s;
|
||||
|
||||
& svg {
|
||||
height: 20px;
|
||||
width: 20px;
|
||||
}
|
||||
|
||||
&:hover:not(:disabled) {
|
||||
background-color: var(--primary-color);
|
||||
color: var(--light-text);
|
||||
}
|
||||
|
||||
&:disabled {
|
||||
cursor: not-allowed;
|
||||
}
|
||||
`;
|
||||
|
||||
export { StyledPagination, PaginationBtn, Buttons };
|
||||
@@ -1,7 +1,13 @@
|
||||
import { ReactNode, useContext } from 'react';
|
||||
|
||||
// Components
|
||||
import { StyledBody, StyledHeader, StyledRow, StyledTable } from './styled';
|
||||
import {
|
||||
StyledBody,
|
||||
StyledHeader,
|
||||
StyledRow,
|
||||
StyledTable,
|
||||
StyledFooter,
|
||||
} from './styled';
|
||||
|
||||
// Contexts
|
||||
import TableContext from '@context/TableContext';
|
||||
@@ -33,9 +39,13 @@ const Table = ({ columns, children }: ITable) => {
|
||||
const Header = ({ headerColumn }: IHeader) => {
|
||||
const { columns } = useContext(TableContext);
|
||||
|
||||
return <StyledHeader columns={columns}>
|
||||
{headerColumn.map((item) => <div key={item}>{item}</div>)}
|
||||
</StyledHeader>;
|
||||
return (
|
||||
<StyledHeader columns={columns}>
|
||||
{headerColumn.map((item) => (
|
||||
<div key={item}>{item}</div>
|
||||
))}
|
||||
</StyledHeader>
|
||||
);
|
||||
};
|
||||
|
||||
const Body = <T,>({ data, render }: ITableBody<T>) => {
|
||||
@@ -55,5 +65,6 @@ const Row = ({ children }: ITable) => {
|
||||
Table.Header = Header;
|
||||
Table.Body = Body;
|
||||
Table.Row = Row;
|
||||
Table.Footer = StyledFooter;
|
||||
|
||||
export default Table;
|
||||
|
||||
@@ -43,4 +43,19 @@ const StyledHeader = styled(CommonRow)`
|
||||
font-weight: 600;
|
||||
`;
|
||||
|
||||
export { StyledBody, StyledHeader, StyledRow, StyledTable };
|
||||
const StyledFooter = styled.div`
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
padding: 15px;
|
||||
background-color: var(--footer-table-color);
|
||||
border-bottom-left-radius: var(--radius-md);
|
||||
border-bottom-right-radius: var(--radius-md);
|
||||
|
||||
border-top: 1px solid var(--border-color);
|
||||
|
||||
&:not(:has(*)) {
|
||||
display: none;
|
||||
}
|
||||
`;
|
||||
|
||||
export { StyledBody, StyledHeader, StyledRow, StyledTable, StyledFooter };
|
||||
|
||||
@@ -1,6 +1,13 @@
|
||||
const DEFAULT_SORT_BY = 'id';
|
||||
const DEFAULT_ORDER_BY = 'asc';
|
||||
const supabaseUrl = 'https://pjqujsjzzdrlrgqbxepe.supabase.co'
|
||||
const supabaseKey = process.env.VITE_SUPABASE_KEY
|
||||
const DEFAULT_PAGE_SIZE = 5;
|
||||
const supabaseUrl = 'https://pjqujsjzzdrlrgqbxepe.supabase.co';
|
||||
const supabaseKey = process.env.VITE_SUPABASE_KEY;
|
||||
|
||||
export { DEFAULT_SORT_BY, DEFAULT_ORDER_BY, supabaseKey, supabaseUrl };
|
||||
export {
|
||||
DEFAULT_SORT_BY,
|
||||
DEFAULT_ORDER_BY,
|
||||
supabaseKey,
|
||||
supabaseUrl,
|
||||
DEFAULT_PAGE_SIZE,
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user