1. Introduction
If you need documentation in English, please refer to README(en).md.
tanstack/react-table
μ νμ©ν΄ ꡬνλ ν
μ΄λΈ λΌμ΄λΈλ¬λ¦¬μ
λλ€.
React
κΈ°λ°μ νλ‘μ νΈμμ νμ©μ΄ κ°λ₯ν©λλ€.
- ν
μ΄λΈ column/data μ€μ , pagination, ν
μ΄λΈ λ°μ΄ν° 컀μ€ν
κΈ°λ₯μ μ 곡ν©λλ€.
- Headless UIλ‘ μ μλμ΄ μ€νμΌλ§ 컀μ€ν
μ΄ κ°λ₯ν©λλ€.
2. Quick Start
Installation
- using npm :
npm install rex-web-table
- using yarn :
yarn add rex-web-table
CSS Import
import "rex-web-table/dist/index.css";
Example
const Table = () => {
interface Example {
no: number;
name: string;
}
const columns: ColumnDef<Example>[] = [
{
accessorKey: "no",
header: "No.",
size: 10,
},
{
accessorKey: "name",
header: "Name",
size: 90,
},
];
const data: Array<Example> = [
{
no: 1,
name: "kim",
},
{
no: 2,
name: "park",
},
];
const { table, pagination, setPagination, totalPageNum } = useTable({
data,
columns,
isPagination: true,
});
return (
<div>
<TableProvider>
<TableHeader table={table} /> {/* ν
μ΄λΈ ν€λ λ λλ§ */}
<TableBody table={table} /> {/* ν
μ΄λΈ λ³Έλ¬Έ λ λλ§ */}
</TableProvider>
{/* νμ΄μ§λ€μ΄μ
컨νΈλ‘€μ ν¬ν¨ν νΈν° λ λλ§ */}
<TableFooter
pagination={pagination}
setPagination={setPagination}
totalPageNum={totalPageNum}
/>
</div>
);
};
export default Table;
3. API Reference
3.1 TableProvider
TableHeader
, TableBody
λ₯Ό κ°μΈλ Provider
λ‘, κ° μ»΄ν¬λνΈμ props
λ₯Ό μ λ¬νλ μν μ μνν©λλ€.
useParentRowUi | boolean | SubRow λ₯Ό νμ©ν λ λΆλͺ¨ Rowμ UIλ₯Ό κ·Έλλ‘ μμλ°μ SubRow λ₯Ό μμ±ν μ§ μ¬λΆλ₯Ό κ²°μ ν©λλ€. true μΌ κ²½μ° λΆλͺ¨μ UIλ₯Ό μμλ°μ΅λλ€. | optional |
SubRowComponent | ReactNode | SubRow 컀μ€ν
μ΄ νμν κ²½μ°, μ§μ μ»΄ν¬λνΈλ₯Ό μ λ¬νμ¬ νμ©ν©λλ€. | optional |
subRowContents | Array<object[]> | SubRow μ»΄ν¬λνΈμμ νμ©λλ λ°μ΄ν°μ
λλ€. | optional |
rowClickEvent | function | Tableμ νμ ν΄λ¦νμ λ μ€νλλ ν¨μμ
λλ€. | optional |
cellClickEvent | function | Tableμ κ° μ
μ ν΄λ¦νμ λ μ€νλλ ν¨μμ
λλ€. | optional |
subRowClickEvent | function | Sub Rowμ νμ ν΄λ¦νμ λ μ€νλλ ν¨μμ
λλ€. useParentRowUi κ° true μΌ λλ§ μλν©λλ€. | optional |
subRowCellClickEvent | function | Sub Rowμ κ° μ
μ ν΄λ¦νμ λ μ€νλλ ν¨μμ
λλ€. useParentRowUi κ° true μΌ λλ§ μλν©λλ€. | optional |
borderLeftNone | boolean | Table border μ€ left νμ μ¬λΆ left nav bar μ ν¨κ» νμ© μ border κ²ΉμΉλ μν©μμ νμ© | optional |
borderTopNone | boolean | Table border μ€ top νμ μ¬λΆ
Top nav bar μ ν¨κ» νμ© μ border κ²ΉμΉλ μν©μμ νμ© | optional |
const subRowData: Object[] = [
[
{
no: 1,
name: "kim",
},
{
no: 2,
name: "park",
},
],
[
{
no: 1,
name: "lee",
},
{
no: 2,
name: "heo",
},
],
];
const { subRowContents } = useSubRowContents(subRowData);
return (
<TableProvider
useParentRowUi={true} // λΆλͺ¨ νμ UIλ₯Ό μλΈ νμ μμλ°λλ‘ μ€μ
subRowContents={subRowContents} // μλΈ νμμ μ¬μ©ν λ°μ΄ν°λ₯Ό μ λ¬
borderLeftNone={true} // μΌμͺ½ ν
λ리 νμ μ¬λΆ μ€μ
borderTopNone={true} // μλ¨ ν
λ리 νμ μ¬λΆ μ€μ
>
<TableHeader table={table} /> // ν
μ΄λΈ ν€λ μ»΄ν¬λνΈ λ λλ§
<TableBody table={table} /> // ν
μ΄λΈ λ°λ μ»΄ν¬λνΈ λ λλ§
</TableProvider>
);
const SubRowComponent = ({ contents }: { contents: Array<object> }) => {
return;
};
return (
<TableProvider
SubRowComponent={SubRowComponent} // 컀μ€ν
μλΈ ν μ»΄ν¬λνΈλ₯Ό μ λ¬ν©λλ€.
subRowContents={subRowContents} // μλΈ νμ μ¬μ©ν λ°μ΄ν°λ₯Ό μ λ¬ν©λλ€.
>
<TableHeader table={table} />
<TableBody table={table} />
</TableProvider>
);
const handleClickRow = ({ rowIndex, e }: RowClickEventParam) => {
};
const handleClickCell = ({ cellIndex, rowIndex, e }: CellClickEventParam) => {
};
return (
<TableProvider
useParentRowUi={true} // λΆλͺ¨ νμ UIλ₯Ό μλΈ νμ μμλ°λλ‘ μ€μ ν©λλ€.
subRowContents={subRowContents} // μλΈ νμ μ¬μ©ν λ°μ΄ν°λ₯Ό μ λ¬ν©λλ€.
rowClickEvent={handleClickRow} // ν ν΄λ¦ μ΄λ²€νΈ νΈλ€λ¬λ₯Ό μ λ¬ν©λλ€.
cellClickEvent={handleClickCell} // μ
ν΄λ¦ μ΄λ²€νΈ νΈλ€λ¬λ₯Ό μ λ¬ν©λλ€.
>
<TableHeader table={table} />
<TableBody table={table} />
</TableProvider>
);
- ν
μ΄λΈ μ΄
column
μ λͺ©μ λ λλ§νλ μ»΄ν¬λνΈμ
λλ€.
header option
μ ν΅ν΄ layer
, rowSpan
, colSpan
μ μ μ΄ν μ μμ΅λλ€.
- μ»΄ν¬λνΈ νΈμΆ μ μ λ¬ν΄μΌ νλ
props
λ μλμ κ°μ΅λλ€.
table | Table<TData> | useTable ν
μ΄ λ°ννλ ν
μ΄λΈ λ°μ΄ν° μΈμ€ν΄μ€ | required |
style | CSSProperties | inline Style μ ν΅ν΄ CSS μμ±μ μ€μ | optional |
headerOption | HeaderOptionType | Header λ λλ§ κ΄λ ¨ μΈλΆ μμ± μ€μ | optional |
headerOption
μ type
μ μλμ κ°μ΅λλ€.
accessorKey | string | headerμ header optionμ λ§€ννλ key κ° |
layer | number | headerκ° λͺ λ²μ§Έ μ€μμ μμν μ§ κ²°μ νλ κ° |
rowSpan | number | μ€μ ν layerλ₯Ό κΈ°μ€μΌλ‘ headerκ° μ°¨μ§ν λμ΄λ₯Ό κ²°μ νλ κ° |
colSpan | number | headerκ° μ°¨μ§ν λλΉλ₯Ό κ²°μ νλ κ° |
const headerOption: HeaderOptionType[] = [
{ accessorKey: "no", layer: 1, colSpan: 1, rowSpan: 1 },
{ accessorKey: "name", layer: 1, colSpan: 1, rowSpan: 1 },
];
<TableHeader
table={table} // useTable ν
μμ λ°νλ ν
μ΄λΈ λ°μ΄ν° μΈμ€ν΄μ€
headerOption={headerOption} // κ° μ΄μ λ λλ§ κ΄λ ¨ μ΅μ
μ μ λ¬
style={{
fontSize: "14px", // CSS μμ± μ€μ
padding: "4px", // CSS μμ± μ€μ
border: "1px solid black", // CSS μμ± μ€μ
backgroundColor: "darkgray", // CSS μμ± μ€μ
}}
/>;
3.3 TableBody
- μ€μ ν
μ΄λΈ λ°μ΄ν°λ₯Ό λ λλ§νλ μ»΄ν¬λνΈλ‘, κ° ν
TableBodyRow
μ μ΄λ₯Ό ꡬμ±νλ μ
TableBodyCell
λ‘ κ΅¬μ±λμ΄ μμ΅λλ€.
- μ€μ: rowμ subRowμ hoverColor μ€νμΌμ μ μ©νλ €λ©΄ λ³λλ‘ CSS importκ° νμν©λλ€. hoverColor μΈμ λ€λ₯Έ μ€νμΌμ CSS import μμ΄λ μ μμ μΌλ‘ μλν©λλ€.
- μ»΄ν¬λνΈ νΈμΆ μ μ λ¬ν΄μΌ νλ
props
λ μλμ κ°μ΅λλ€.
table | Table<TData> | useTable ν
μ΄ λ°ννλ ν
μ΄λΈ λ°μ΄ν° μΈμ€ν΄μ€ | required |
interactiveStyles | { hoverColor: string; clickedColor: string; } | ν
μ΄λΈ νμ λ§μ°μ€ hover μμ ν΄λ¦ μ λ°°κ²½μ μ§μ | hoverColor : optional, clickedColor : rowSelectionType μ΄ μ€μ λ κ²½μ°μ νμ |
subRowProps | object | subRow κ΄λ ¨ μ€μ | optional |
rowSelectionType | "single" or "multiple" or "grouped" | ν μ ν νμ
μ μ€μ | optional |
defaultSelectedRowIndex | number | rowSelectionType μ΄ single μΌ λ, κΈ°λ³Έ κ°μΌλ‘ μ νλ νμ μΈλ±μ€λ₯Ό μ€μ ν©λλ€. | rowSelectionType μ΄ single μΌ λ optional |
groupSelectionRange | number | rowSelectionType μ΄ grouped μΌ λ, κ·Έλ£Ή μ ν λ²μλ₯Ό μ€μ ν©λλ€. (μ νν ν κΈ°μ€, range λ§νΌ μ/μλ μΆκ° μ ν) | rowSelectionType μ΄ grouped μΌ λ νμ |
import "rex-web-table/dist/index.css";
1) rowSelectionTypeμ μ€μ νμ§ μμ κ²½μ°
<TableBody
table={table}
style={{
fontSize: "14px",
border: "1px solid black",
textAlign: "center",
}}
interactiveStyles={{
hoverColor: "white",
}}
/>;
2) rowSelectionTypeμ΄ "single"μΈ κ²½μ°
<TableBody
table={table}
style={{
fontSize: "14px",
border: "1px solid black",
textAlign: "center",
}}
interactiveStyles={{
hoverColor: "white",
clickedColor: "black",
}}
rowSelectionType="single"
defaultSelectedRowIndex={0}
/>;
3) rowSelectionTypeμ΄ "multiple"μΈ κ²½μ°
<TableBody
table={table}
style={{
fontSize: "14px",
border: "1px solid black",
textAlign: "center",
}}
interactiveStyles={{
hoverColor: "white",
clickedColor: "black",
}}
rowSelectionType="multiple"
/>;
4) rowSelectionTypeμ΄ "grouped"μΈ κ²½μ°
<TableBody
table={table}
style={{
fontSize: "14px",
border: "1px solid black",
textAlign: "center",
}}
interactiveStyles={{
hoverColor: "white",
clickedColor: "black",
}}
rowSelectionType="grouped",
groupSelectionRange={1}
/>;
subRowProps
μ ꡬμ±μ μλμ κ°μ΅λλ€.
expandState | Array<boolean> | subRow νμ₯κ³Ό κ΄λ ¨λ μνμ
λλ€. | optional |
style | CSSProperties | inline Style μ ν΅ν΄ CSS μμ±μ μ€μ ν μ μμ΅λλ€. | optional |
hoverColor | string | subRowμ λ§μ°μ€λ₯Ό hover νμ λ λ°μνλ λ°°κ²½μμ μ€μ ν μ μμ΅λλ€. | optional |
import "rex-web-table/dist/index.css";
const { expandState, changeSubRowExpandState } = useSubRowExpand();
const handleClickRow = ({ rowIndex }: { rowIndex: number }) => {
changeSubRowExpandState(rowIndex);
};
<TableProvider
useParentRowUi={true}
subRowContents={subRowContents}
rowClickEvent={handleClickRow} // ν ν΄λ¦ μ μμμ μ μν ν¨μλ₯Ό νΈμΆ
>
<TableBody
table={table}
subRowProps={{
expandState, // μλΈ νμ νμ₯ μνλ₯Ό μ λ¬
style: {
backgroundColor: "ivory", // ν
μ΄λΈμ μλΈ νμ μ μ©ν CSS μμ±μ μ€μ
},
hoverColor: "red", // μλΈ νμ λ§μ°μ€ hover μ λ°°κ²½μμ μ€μ
}}
/>
</TableProvider>;
- νμ΄μ§λ€μ΄μ
κΈ°λ₯μ λ΄λΉνλ μ»΄ν¬λνΈλ‘, ν΄λΉ κΈ°λ₯μ΄ νμν κ²½μ° μ νμ μΌλ‘ νμ© κ°λ₯ν©λλ€.
- μ»΄ν¬λνΈ νΈμΆ μ μ λ¬ν΄μΌ νλ
props
λ μλμ κ°μ΅λλ€.
pagination | PaginationState | νμ΄μ§λ€μ΄μ
κ΄λ ¨ μν | required |
setPagination | Dispatch<SetStateAction<PaginationState>> | νμ΄μ§λ€μ΄μ
μνκ΄λ¦¬ ν¨μ | required |
totalPageNum | number | μ 체 νμ΄μ§ κ°μ κ΄λ ¨ λ°μ΄ν° | required |
pageSizeList | Array<number> | ν νμ΄μ§ λΉ νμν 컨ν
μΈ κ°μ μ΅μ
리μ€νΈ | optional |
styles | { containerStyle: CSSproperties; pageSizeSelectStyle: PageSelectStlyeProps; pageNumButtonStyle: PageButtonStyleProps; } | TableFooter λ΄λΆ κ΅¬μ± μμμ λν css μ€νμΌ μ€μ | optional |
const { table, totalPageNum, pagination, setPagination } = useTable<Example>({
data,
columns,
isPagination: true,
});
<TableFooter
pagination={pagination} // νμ¬ νμ΄μ§λ€μ΄μ
μνλ₯Ό μ λ¬
setPagination={setPagination} // νμ΄μ§λ€μ΄μ
μνλ₯Ό κ΄λ¦¬νλ ν¨μ μ λ¬
totalPageNum={totalPageNum} // μ 체 νμ΄μ§ μλ₯Ό μ λ¬
styles={{
containerStyle: {
// TableFooterμ 컨ν
μ΄λ μ€νμΌ μ€μ
padding: "2px 3px",
border: "1px solid darkgray",
borderLeft: "none",
},
pageSizeSelectStyle: {
// νμ΄μ§ μ¬μ΄μ¦ μ νμ μ€νμΌ μ€μ
fontColor: "black",
},
pageNumButtonStyle: {
// νμ΄μ§ λ²νΈ λ²νΌμ μ€νμΌ μ€μ
backgroundColor: "transparent",
disabledArrowButtonColor: "darkgray",
},
}}
/>;
3.5 useTable
TableHeader
, TableBody
, TableFooter
μ»΄ν¬λνΈμ props
λ‘ μ λ¬ν λ°μ΄ν°λ₯Ό λ°ννλ 컀μ€ν
ν
μ
λλ€.
- ν
νΈμΆ μ μ λ¬ν΄μΌ νλ
props
λ μλμ κ°μ΅λλ€.
data | Array<T> | ν
μ΄λΈ body λ₯Ό ꡬμ±νλ λ°μ΄ν°μ
λλ€. | required |
columns | Array<ColumnDef<T>> | ν
μ΄λΈ column μ€μ μ νμ©λλ λ°μ΄ν°μ
λλ€. | required |
isPagination | boolean | νμ΄μ§λ€μ΄μ
μ€μ μ¬λΆλ₯Ό κ²°μ νλ λ°μ΄ν°μ
λλ€. | optional |
- ν
μ΄ λ°ννλ κ°μ μλμ κ°μ΅λλ€.
table | Table<TData> | ν
μ΄λΈ μ€μ μ νμ©λλ μΈμ€ν΄μ€ κ°μ²΄. TableHeader , TableBody μ props λ‘ νμ©λ©λλ€. |
pagination | PaginationState | νμ΄μ§λ€μ΄μ
κ΄λ ¨ μν. TableFooter μ props λ‘ νμ©λ©λλ€. |
setPagination | Dispatch<SetStateAction<PaginationState>> | νμ΄μ§λ€μ΄μ
κ΄λ ¨ μνκ΄λ¦¬ ν¨μ. TableFooter μ props λ‘ νμ©λ©λλ€. |
totalPageNum | number | μ 체 νμ΄μ§ κ°μλ₯Ό μλ―Έν©λλ€. TableFooter μ props λ‘ νμ©λ©λλ€. |
const data = [
{ id: 1, name: "kim", age: 28 },
{ id: 2, name: "lee", age: 22 },
];
const columns: ColumnDef<{ id: number; name: string; age: number }>[] = [
{ accessorKey: "id", header: "ID" },
{ accessorKey: "name", header: "μ΄λ¦" },
{
accessorKey: "age",
header: "λμ΄",
cell: ({ getValue }) => {
const age = getValue();
return <span>{age}μΈ</span>;
},
},
];
const { table, totalPageNum, pagination, setPagination } = useTable<{
id: number;
name: string;
age: number;
}>({
data,
columns,
isPagination: true,
});
return (
<TableProvider>
<TableHeader table={table} /> {/* ν
μ΄λΈμ ν€λλ₯Ό λ λλ§ */}
<TableBody table={table} /> {/* ν
μ΄λΈμ λ³Έλ¬Έμ λ λλ§ */}
<TableFooter
pagination={pagination} // νμ¬ νμ΄μ§λ€μ΄μ
μν μ λ¬
setPagination={setPagination} // νμ΄μ§λ€μ΄μ
μνλ₯Ό κ΄λ¦¬νλ ν¨μ μ λ¬
totalPageNum={totalPageNum} // μ 체 νμ΄μ§ μ μ λ¬
/>
</TableProvider>
);
3.6 useSubRowContents
TableProvider
μ subRowContents
μ μ λ¬ν μνμ μν κ΄λ¦¬ ν¨μλ₯Ό λ°ννλ 컀μ€ν
ν
μ
λλ€.
- ν
μ΄ λ°ννλ κ°μ μλμ κ°μ΅λλ€.
subRowContents | Array<object[]> | TableProvider μ subRowContents λ‘ νμ©λλ μν |
setSubRowContents | Dispatch<SetStateAction<Array<object[]>>> | subRowContents μνκ΄λ¦¬ ν¨μ |
const subRowData: object[][] = [
[
{ no: 1, name: "lee" },
{ no: 2, name: "kim" },
],
[
{ no: 1, name: "park" },
{ no: 2, name: "choi" },
],
];
const { subRowContents, setSubRowContents } = useSubRowContents(subRowData);
return (
<TableProvider useParentRowUi={true} subRowContents={subRowContents}>
<TableHeader table={table} />
<TableBody table={table} />
</TableProvider>
);
3.7 useSubRowExpand
TableBody
μ expandState
μ μ λ¬ν μνμ μν κ΄λ¦¬ ν¨μλ₯Ό λ°ννλ 컀μ€ν
ν
μ
λλ€.
- ν
μ΄ λ°ννλ κ°μ μλμ κ°μ΅λλ€.
expandState | Array<boolean> | TableBody μ expandState μν |
setExpandState | Dispatch<SetStateAction<boolean[]>> | expandState μνκ΄λ¦¬ ν¨μ |
changeSubRowExpandState | function | ν΄λ¦ν νμ expandState λ₯Ό λ³κ²½νλ ν¨μ |
const { expandState, changeSubRowExpandState } = useSubRowExpand();
const handleRowClick = ({ rowIndex }: { rowIndex: number }) => {
changeSubRowExpandState(rowIndex);
};
return (
<TableProvider
useParentRowUi={true} // λΆλͺ¨ νμ UIλ₯Ό μλΈ νμμ μμλ°λλ‘ μ€μ
subRowContents={subRowContents} // μλΈ νμμ μ¬μ©ν λ°μ΄ν°λ₯Ό μ λ¬
rowClickEvent={handleRowClick}
>
<TableBody
table={table}
subRowProps={{
expandState, // μλΈ νμ νμ₯ μν
}}
/>
</TableProvider>
);
3.8 getClickedRowContent, getClickedCellContent
- μ¬μ©μκ° ν΄λ¦ν ν λ° μ
μ μ½ν
μΈ λ₯Ό κ°μ Έμ€λ μ νΈλ¦¬ν° ν¨μμ
λλ€.
- μ¬μ© μμλ μλμ κ°μ΅λλ€. (ν΄λ¦ μ΄λ²€νΈμ μ λ¬νμ¬ μ¬μ©νλ μμ)
const handleClickRow = () => {
const rowContent = getClickedRowContent();
console.log("ν΄λ¦λ νμ λ°μ΄ν°:", rowContent);
};
const handleClickCell = () => {
const cellContent = getClickedCellContent();
console.log("ν΄λ¦λ μ
μ λ°μ΄ν°:", cellContent);
};
return (
<TableProvider
rowClickEvent={handleClickRow} // ν ν΄λ¦ μ΄λ²€νΈ νΈλ€λ¬λ₯Ό μ λ¬ν©λλ€.
cellClickEvent={handleClickCell} // μ
ν΄λ¦ μ΄λ²€νΈ νΈλ€λ¬λ₯Ό μ λ¬ν©λλ€.
>
<TableHeader table={table} />
<TableBody table={table} />
</TableProvider>
);
4. Issue
- νμ¬
sorting
κΈ°λ₯μ΄ λ―Έκ΅¬ν λ μνλ‘ μΆν κΈ°λ₯ 보μμ΄ νμν μν©μ
λλ€.