개발로 부자되기/next.js

react-table 너비 (width) 조정하기, 예제코드

야나부짱 2023. 8. 31. 01:37
import {
useTable,
useRowSelect,
useSortBy,
useGlobalFilter,
usePagination,
} from "react-table";

 

예제 가져와서 구현해보고 있는데, 너비 조정할 필요가 있었다.

 

너비 조정하는 과정은 다음과 같은데,

 

1. 먼저 columns 배열에 minWidth정보를 채운다.

2. columns는 useTable에 사용된다.

3. <th/>에서 각 column마다 설정된 minWidth정보가 사용된다.

 

각 순서에 대해 코드를 보면서 설명하면

 

컬럼 변수에 텍스트로 들어갈 부분, key값(accessor), minWidth를 넣어둔다. Cell을 보면 Cell엔 어떻게 표시될지가 표현된다.

const COLUMNS = [
{
Header: "customer",
accessor: "customer",
minWidth: 100,

Cell: (row) => {
return (
<div>
<span className="inline-flex items-center">
<span className="text-sm text-slate-600 dark:text-slate-300 capitalize">
{row?.cell?.value.name}
</span>
</span>
</div>
);
},
},

 

react-table이서 useTable을 쓰면 table을 만들때 필요한 데이터들을 만들어낼 수 있습니다. useTable의 input은 columns과 data인데, 여기서 columns은 header에 해당하는 데이터이고, data는 테이블에 들어갈 데이터가 됩니다.

 


const tableInstance = useTable(
{
columns,
data,
},

useGlobalFilter,
useSortBy,
usePagination,
useRowSelect,

(hooks) => {
hooks.visibleColumns.push((columns) => [
...columns,
]);
}
);
const {
getTableProps,
getTableBodyProps,
headerGroups,
footerGroups,
page,
nextPage,
previousPage,
canNextPage,
canPreviousPage,
pageOptions,
state,
gotoPage,
pageCount,
setPageSize,
setGlobalFilter,
prepareRow,
} = tableInstance;

 

render쪽에 있는 table의 th에다가 style을 넣고, style에 minWidth를 설정할수있도록 한다.

<th
{...column.getHeaderProps(
column.getSortByToggleProps()
)}
style={{ minWidth: column.minWidth}}
scope="col"
className=" table-th "
key={`ex-th-${column.id}`}
>

 

예제 코드도 첨부한다.

 

/* eslint-disable react/display-name */
import React, { useState, useMemo } from "react";
// import { advancedTable } from "../../../constant/table-data";
import Card from "@/components/ui/Card";
import Icon from "@/components/ui/Icon";
import Tooltip from "@/components/ui/Tooltip";
import {
useTable,
useRowSelect,
useSortBy,
useGlobalFilter,
usePagination,
} from "react-table";
import GlobalFilter from "./GlobalFilter";

const advancedTable = [
{
id: 1,
order: 951,
customer: {
name: "Jenny Wilson",
image: "/assets/images/all-img/customer_1.png",
},
date: "3/26/2022",
quantity: 13,
amount: "$1779.53",
status: "paid",
action: null,
},
{
id: 2,
order: 238,
customer: {
name: "Jenny Wilson",
image: "/assets/images/all-img/customer_1.png",
},
date: "2/6/2022",
quantity: 13,
amount: "$2215.78",
status: "due",
action: null,
}
]

const COLUMNS = [
{
Header: "Id",
accessor: "id",
Cell: (row) => {
return <span>{row?.cell?.value}</span>;
},
},
{
Header: "Order",
accessor: "order",
Cell: (row) => {
return <span>#{row?.cell?.value}</span>;
},
},
{
Header: "customer",
accessor: "customer",
Cell: (row) => {
return (
<div>
<span className="inline-flex items-center">
<span className="w-7 h-7 rounded-full ltr:mr-3 rtl:ml-3 flex-none bg-slate-600">
<img
src={row?.cell?.value.image}
alt=""
className="object-cover w-full h-full rounded-full"
/>
</span>
<span className="text-sm text-slate-600 dark:text-slate-300 capitalize">
{row?.cell?.value.name}
</span>
</span>
</div>
);
},
},
{
Header: "date",
accessor: "date",
Cell: (row) => {
return <span>{row?.cell?.value}</span>;
},
},
{
Header: "quantity",
accessor: "quantity",
Cell: (row) => {
return <span>{row?.cell?.value}</span>;
},
},
{
Header: "amount",
accessor: "amount",
Cell: (row) => {
return <span>{row?.cell?.value}</span>;
},
},
{
Header: "status",
accessor: "status",
Cell: (row) => {
return (
<span className="block w-full">
<span
className={` inline-block px-3 min-w-[90px] text-center mx-auto py-1 rounded-[999px] bg-opacity-25 ${
row?.cell?.value === "paid"
? "text-success-500 bg-success-500"
: ""
}
${
row?.cell?.value === "due"
? "text-warning-500 bg-warning-500"
: ""
}
${
row?.cell?.value === "cancled"
? "text-danger-500 bg-danger-500"
: ""
}
 
`}
>
{row?.cell?.value}
</span>
</span>
);
},
},
{
Header: "action",
accessor: "action",
Cell: (row) => {
return (
<div className="flex space-x-3 rtl:space-x-reverse">
<Tooltip content="View" placement="top" arrow animation="shift-away">
<button className="action-btn" type="button">
<Icon icon="heroicons:eye" />
</button>
</Tooltip>
<Tooltip content="Edit" placement="top" arrow animation="shift-away">
<button className="action-btn" type="button">
<Icon icon="heroicons:pencil-square" />
</button>
</Tooltip>
<Tooltip
content="Delete"
placement="top"
arrow
animation="shift-away"
theme="danger"
>
<button className="action-btn" type="button">
<Icon icon="heroicons:trash" />
</button>
</Tooltip>
</div>
);
},
},
];

const IndeterminateCheckbox = React.forwardRef(
({ indeterminate, ...rest }, ref) => {
const defaultRef = React.useRef();
const resolvedRef = ref || defaultRef;

React.useEffect(() => {
resolvedRef.current.indeterminate = indeterminate;
}, [resolvedRef, indeterminate]);

return (
<>
<input
type="checkbox"
ref={resolvedRef}
{...rest}
className="table-checkbox"
/>
</>
);
}
);

const ExampleTwo = ({ title = "Advanced Table Two" }) => {
const columns = useMemo(() => COLUMNS, []);
const data = useMemo(() => advancedTable, []);

const tableInstance = useTable(
{
columns,
data,
},

useGlobalFilter,
useSortBy,
usePagination,
useRowSelect,

(hooks) => {
hooks.visibleColumns.push((columns) => [
{
id: "selection",
Header: ({ getToggleAllRowsSelectedProps }) => (
<div>
<IndeterminateCheckbox {...getToggleAllRowsSelectedProps()} />
</div>
),
Cell: ({ row }) => (
<div>
<IndeterminateCheckbox {...row.getToggleRowSelectedProps()} />
</div>
),
},
...columns,
]);
}
);
const {
getTableProps,
getTableBodyProps,
headerGroups,
footerGroups,
page,
nextPage,
previousPage,
canNextPage,
canPreviousPage,
pageOptions,
state,
gotoPage,
pageCount,
setPageSize,
setGlobalFilter,
prepareRow,
} = tableInstance;

const { globalFilter, pageIndex, pageSize } = state;
return (
<>
<Card>
<div className="md:flex justify-between items-center mb-6">
<h4 className="card-title">{title}</h4>
<div>
<GlobalFilter filter={globalFilter} setFilter={setGlobalFilter} />
</div>
</div>
<div className="overflow-x-auto -mx-6">
<div className="inline-block min-w-full align-middle">
<div className="overflow-hidden ">
<table
className="min-w-full divide-y divide-slate-100 table-fixed dark:divide-slate-700"
{...getTableProps}
>
<thead className="bg-slate-200 dark:bg-slate-700">
{headerGroups.map((headerGroup) => {
const { key, ...restHeaderGroupProps } =
headerGroup.getHeaderGroupProps();
<tr key={key} {...restHeaderGroupProps}>
{headerGroup.headers.map((column) => {
const { key, ...restColumn } = column.getHeaderProps();
<th
key={key}
{...restColumn}
scope="col"
className=" table-th "
>
{column.render("Header")}
<span>
{column.isSorted
? column.isSortedDesc
? " 🔽"
: " 🔼"
: ""}
</span>
</th>;
})}
</tr>;
})}
</thead>
<tbody
className="bg-white divide-y divide-slate-100 dark:bg-slate-800 dark:divide-slate-700"
{...getTableBodyProps}
>
{page.map((row) => {
prepareRow(row);
const { key, ...restRowProps } = row.getRowProps();
return (
<tr key={key} {...restRowProps}>
{row.cells.map((cell) => {
const { key, ...restCellProps } = cell.getCellProps();
return (
<td
key={key}
{...restCellProps}
className="table-td"
>
{cell.render("Cell")}
</td>
);
})}
</tr>
);
})}
</tbody>
</table>
</div>
</div>
</div>
<div className="md:flex md:space-y-0 space-y-5 justify-between mt-6 items-center">
<div className=" flex items-center space-x-3 rtl:space-x-reverse">
<select
className="form-control py-2 w-max"
value={pageSize}
onChange={(e) => setPageSize(Number(e.target.value))}
>
{[10, 25, 50].map((pageSize) => (
<option key={pageSize} value={pageSize}>
Show {pageSize}
</option>
))}
</select>
<span className="text-sm font-medium text-slate-600 dark:text-slate-300">
Page{" "}
<span>
{pageIndex + 1} of {pageOptions.length}
</span>
</span>
</div>
<ul className="flex items-center space-x-3 rtl:space-x-reverse flex-wrap">
<li className="text-xl leading-4 text-slate-900 dark:text-white rtl:rotate-180">
<button
className={` ${
!canPreviousPage ? "opacity-50 cursor-not-allowed" : ""
}`}
onClick={() => gotoPage(0)}
disabled={!canPreviousPage}
>
<Icon icon="heroicons:chevron-double-left-solid" />
</button>
</li>
<li className="text-sm leading-4 text-slate-900 dark:text-white rtl:rotate-180">
<button
className={` ${
!canPreviousPage ? "opacity-50 cursor-not-allowed" : ""
}`}
onClick={() => previousPage()}
disabled={!canPreviousPage}
>
Prev
</button>
</li>
{pageOptions.map((page, pageIdx) => (
<li key={pageIdx}>
<button
href="#"
aria-current="page"
className={` ${
pageIdx === pageIndex
? "bg-slate-900 dark:bg-slate-600 dark:text-slate-200 text-white font-medium "
: "bg-slate-100 dark:bg-slate-700 dark:text-slate-400 text-slate-900 font-normal "
} text-sm rounded leading-[16px] flex h-6 w-6 items-center justify-center transition-all duration-150`}
onClick={() => gotoPage(pageIdx)}
>
{page + 1}
</button>
</li>
))}
<li className="text-sm leading-4 text-slate-900 dark:text-white rtl:rotate-180">
<button
className={` ${
!canNextPage ? "opacity-50 cursor-not-allowed" : ""
}`}
onClick={() => nextPage()}
disabled={!canNextPage}
>
Next
</button>
</li>
<li className="text-xl leading-4 text-slate-900 dark:text-white rtl:rotate-180">
<button
onClick={() => gotoPage(pageCount - 1)}
disabled={!canNextPage}
className={` ${
!canNextPage ? "opacity-50 cursor-not-allowed" : ""
}`}
>
<Icon icon="heroicons:chevron-double-right-solid" />
</button>
</li>
</ul>
</div>
{/*end*/}
</Card>
</>
);
};

export default ExampleTwo;