가상화 테이블
가상화 테이블 (Virtualized Table)
출처: 문서
본문
웹 개발이 진화하면서 테이블 컴포넌트는 특히 대시보드, 데이터 분석에서 가장 인기 있는 컴포넌트가 되었어요. Table V1의 경우 데이터가 1000개만 되어도 성능이 떨어져서 사용하기 불편할 때가 많았죠. Virtualized Table을 사용하면 방대한 양의 데이터도 순식간에 렌더링할 수 있어요.
TIP 이 컴포넌트는 아직 테스트 중이라 사용에 주의가 필요해요. 버그나 이슈를 발견하면 GitHub에 보고해주세요. 또한 이 문서에 언급되지 않은 API도 있는데, 그중 일부는 아직 완전히 개발되지 않아서 여기에 언급하지 않았어요. Virtualized Table이 효율적이긴 하지만 데이터 로드가 너무 크면 네트워크와 메모리 크기가 앱의 병목이 될 수 있어요. Virtualized Table이 모든 문제의 궁극적인 해결책은 아니라는 점을 기억하세요. 데이터를 페이지네이션하고 필터를 추가하는 걸 고려해보세요.
기본 사용법 (Basic Usage)
10개의 열과 1000개의 행이 있는 기본 예시를 렌더링해서 Virtualized Table의 성능을 보여드릴게요.
자동 크기 조절 (Auto Resizer)
테이블에 width와 height 속성을 수동으로 전달하고 싶지 않다면 테이블 컴포넌트를 AutoResizer로 감싸면 돼요. 그러면 width와 height를 자동으로 업데이트해줘요. 브라우저 크기를 조절해서 어떻게 동작하는지 확인해보세요.
TIP AutoResizer의 기본 높이 값이 100%이므로 AutoResizer의 부모 노드가 고정 높이를 가져야 해요. 또는 AutoResizer에 style 속성을 전달해서 높이를 정의할 수도 있어요.
const generateColumns = (length = 10, prefix = 'column-', props?: any) =>
Array.from({ length }).map((_, columnIndex) => ({
...props,
key: `${prefix}${columnIndex}`,
dataKey: `${prefix}${columnIndex}`,
title: `Column ${columnIndex}`,
width: 150,
}))
const generateData = (
columns: ReturnType,
length = 200,
prefix = 'row-'
) =>
Array.from({ length }).map((_, rowIndex) => {
return columns.reduce(
(rowData, column, columnIndex) => {
rowData[column.dataKey] = `Row ${rowIndex} - Col ${columnIndex}`
return rowData
},
{
id: `${prefix}${rowIndex}`,
parentId: null,
}
)
})
const columns = generateColumns(10)
const data = generateData(columns, 200)
셀 렌더러 커스터마이즈 (Customize Cell Renderer)
물론 필요에 따라 테이블 셀을 직접 렌더링할 수도 있어요. 셀을 커스터마이즈하는 간단한 예시를 보여드릴게요.
선택이 있는 테이블 (Table With Selections)
커스텀 셀 렌더러를 사용해서 테이블에 선택 기능을 추가할 수 있어요.
import { ref, unref } from 'vue'
import { ElCheckbox, useLocale } from 'element-plus'
import type { FunctionalComponent } from 'vue'
import type { CheckboxValueType, Column } from 'element-plus'
type SelectionCellProps = {
value: boolean
intermediate?: boolean
ariaLabel?: string
onChange: (value: CheckboxValueType) => void
}
const { t } = useLocale()
const SelectionCell: FunctionalComponent = ({
value,
intermediate = false,
ariaLabel,
onChange,
}) => {
return (
)
}
const generateColumns = (length = 10, prefix = 'column-', props?: any) =>
Array.from({ length }).map((_, columnIndex) => ({
...props,
key: `${prefix}${columnIndex}`,
dataKey: `${prefix}${columnIndex}`,
title: `Column ${columnIndex}`,
width: 150,
}))
const generateData = (
columns: ReturnType,
length = 200,
prefix = 'row-'
) =>
Array.from({ length }).map((_, rowIndex) => {
return columns.reduce(
(rowData, column, columnIndex) => {
rowData[column.dataKey] = `Row ${rowIndex} - Col ${columnIndex}`
return rowData
},
{
id: `${prefix}${rowIndex}`,
checked: false,
parentId: null,
}
)
})
const columns: Column[] = generateColumns(10)
columns.unshift({
key: 'selection',
width: 50,
cellRenderer: ({ rowData }) => {
const onChange = (value: CheckboxValueType) => (rowData.checked = value)
return (
)
},
headerCellRenderer: () => {
const _data = unref(data)
const onChange = (value: CheckboxValueType) =>
(data.value = _data.map((row) => {
row.checked = value
return row
}))
const allSelected = _data.every((row) => row.checked)
const containsChecked = _data.some((row) => row.checked)
return (
)
},
})
const data = ref(generateData(columns, 200))
인라인 편집 (Inline Editing)
위에서 선택 기능으로 보여드린 것처럼, 동일한 방법으로 인라인 편집도 활성화할 수 있어요.
import { ref, withKeys } from 'vue'
import { ElInput } from 'element-plus'
import type { FunctionalComponent } from 'vue'
import type { Column, InputInstance } from 'element-plus'
type SelectionCellProps = {
value: string
intermediate?: boolean
onChange: (value: string) => void
onBlur: () => void
onKeydownEnter: () => void
forwardRef: (el: InputInstance) => void
}
const InputCell: FunctionalComponent = ({
value,
onChange,
onBlur,
onKeydownEnter,
forwardRef,
}) => {
return (
)
}
const generateColumns = (length = 10, prefix = 'column-', props?: any) =>
Array.from({ length }).map((_, columnIndex) => ({
...props,
key: `${prefix}${columnIndex}`,
dataKey: `${prefix}${columnIndex}`,
title: `Column ${columnIndex}`,
width: 150,
}))
const generateData = (
columns: ReturnType,
length = 200,
prefix = 'row-'
) =>
Array.from({ length }).map((_, rowIndex) => {
return columns.reduce(
(rowData, column, columnIndex) => {
rowData[column.dataKey] = `Row ${rowIndex} - Col ${columnIndex}`
return rowData
},
{
id: `${prefix}${rowIndex}`,
editing: false,
parentId: null,
}
)
})
const columns: Column[] = generateColumns(10)
columns[0] = {
...columns[0],
title: 'Editable Column',
cellRenderer: ({ rowData, column }) => {
const onChange = (value: string) => {
rowData[column.dataKey!] = value
}
const onEnterEditMode = () => {
rowData.editing = true
}
const onExitEditMode = () => (rowData.editing = false)
const input = ref()
const setRef = (el) => {
input.value = el
if (el) {
el.focus?.()
}
}
return rowData.editing ? (
) : (
{rowData[column.dataKey!]}
)
},
}
const data = ref(generateData(columns, 200))
.table-v2-inline-editing-trigger {
border: 1px transparent dotted;
padding: 4px;
}
.table-v2-inline-editing-trigger:hover {
border-color: var(--el-color-primary);
}
상태가 있는 테이블 (Table With Status)
테이블 콘텐츠를 강조해서 "success, information, warning, danger" 등의 상태를 구분할 수 있어요.
행의 모양을 커스터마이즈하려면 row-class-name 속성을 사용해요. 예를 들어 10번째마다 행은 bg-blue-200 클래스로, 5번째마다 행은 bg-red-100 클래스로 강조돼요.
고정 행이 있는 테이블 (Table With Sticky Rows)
일부 행을 테이블 상단에 고정할 수 있는데, fixed-data 속성만 사용하면 아주 쉽게 구현할 수 있어요.
아래 예시처럼 스크롤 이벤트를 기반으로 고정 행을 동적으로 설정할 수도 있어요.
고정 열이 있는 테이블 (Table With Fixed Columns)
어떤 이유로 열을 왼쪽이나 오른쪽에 고정하고 싶다면 테이블에 특별한 속성을 추가해서 구현할 수 있어요.
열의 fixed 속성을 true(고정 방향 FixedDir.LEFT를 의미) 또는 FixedDir.LEFT, FixedDir.RIGHT로 설정할 수 있어요.
그룹 헤더 (Grouping Header)
헤더 렌더러를 커스터마이즈하면 아래 예시처럼 헤더를 그룹화할 수 있어요.
TIP 이 예시에서는 플레이그라운드에서 지원하지 않는 JSX 기능을 사용했어요. 로컬 환경이나 codesandbox 같은 온라인 IDE에서 시도해볼 수 있어요. VNode 조작이 포함되어 있으므로 테이블 컴포넌트는 JSX로 작성하는 것을 권장해요.
.el-el-table-v2__header-row .custom-header-cell {
border-right: 1px solid var(--el-border-color);
}
.el-el-table-v2__header-row .custom-header-cell:last-child {
border-right: none;
}
.el-primary-color {
background-color: var(--el-color-primary);
color: var(--el-color-white);
font-size: 14px;
font-weight: bold;
}
.el-primary-color .custom-header-cell {
padding: 0 4px;
}
필터 (Filter)
Virtualized Table은 커스텀 헤더를 만들기 위한 커스텀 헤더 렌더러를 제공해요. 이를 활용해서 필터를 렌더링할 수 있어요.
.el-table-v2__demo-filter {
border-top: var(--el-border);
margin: 12px -12px -12px;
padding: 0 12px;
display: flex;
justify-content: space-between;
}
.el-table-v2__demo-filter-btn {
display: flex;
cursor: pointer;
padding: 0;
margin: 0;
background-color: transparent;
appearance: none;
border: none;
}
정렬 가능 (Sortable)
정렬 상태로 테이블을 정렬할 수 있어요.
제어 정렬 (Controlled Sort)
필요에 따라 여러 정렬 가능한 열을 정의할 수 있어요. 정렬 가능한 열이 여러 개면 어떤 열이 현재 정렬 중인지 명확하지 않아 사용자에게 UI가 혼란스러워 보일 수 있다는 점에 주의하세요.
교차 호버링 (Cross Hovering)
큰 리스트를 다룰 때 현재 방문 중인 행과 열을 놓치기 쉬워요. 이런 경우 이 기능이 매우 유용할 수 있어요.
import { ref } from 'vue'
const generateColumns = (length = 10, prefix = 'column-', props?: any) =>
Array.from({ length }).map((_, columnIndex) => ({
...props,
key: `${prefix}${columnIndex}`,
dataKey: `${prefix}${columnIndex}`,
title: `Column ${columnIndex}`,
width: 150,
}))
const generateData = (
columns: ReturnType,
length = 200,
prefix = 'row-'
) =>
Array.from({ length }).map((_, rowIndex) => {
return columns.reduce(
(rowData, column, columnIndex) => {
rowData[column.dataKey] = `Row ${rowIndex} - Col ${columnIndex}`
return rowData
},
{
id: `${prefix}${rowIndex}`,
parentId: null,
}
)
})
const columns = generateColumns(10)
columns.unshift({
key: 'column-n-1',
width: 50,
title: 'Row No.',
cellRenderer: ({ rowIndex }) => `${rowIndex + 1}`,
align: 'center',
})
const data = generateData(columns, 200)
const cellProps = ({ columnIndex }) => {
const key = `hovering-col-${columnIndex}`
return {
['data-key']: key,
onMouseenter: () => {
kls.value = key
},
onMouseleave: () => {
kls.value = ''
},
}
}
const kls = ref('')
.hovering-col-0 [data-key='hovering-col-0'],
.hovering-col-1 [data-key='hovering-col-1'],
.hovering-col-2 [data-key='hovering-col-2'],
.hovering-col-3 [data-key='hovering-col-3'],
.hovering-col-4 [data-key='hovering-col-4'],
.hovering-col-5 [data-key='hovering-col-5'],
.hovering-col-6 [data-key='hovering-col-6'],
.hovering-col-7 [data-key='hovering-col-7'],
.hovering-col-8 [data-key='hovering-col-8'],
.hovering-col-9 [data-key='hovering-col-9'],
.hovering-col-10 [data-key='hovering-col-10'] {
background: var(--el-table-row-hover-bg-color);
}
[data-key='hovering-col-0'] {
font-weight: bold;
user-select: none;
pointer-events: none;
}
Colspan
가상화 테이블은 내장된 table 요소를 사용하지 않으므로 colspan과 rowspan이 TableV1과 조금 다르게 동작해요. 하지만 커스텀 행 렌더러를 사용하면 이러한 기능들을 여전히 구현할 수 있어요. 이 섹션에서 그 방법을 보여드릴게요.
Rowspan
Colspan을 다뤘으니, row span도 있다는 점을 언급할게요. colspan과는 조금 다르지만 기본 컨셉은 동일해요.
Rowspan과 Colspan 함께 사용하기 (Rowspan and Colspan Together)
rowspan과 colspan을 함께 조합해서 비즈니스 목표를 달성할 수 있어요!
트리 데이터 (Tree Data)
가상 테이블도 트리 구조 형태로 데이터를 렌더링할 수 있어요. 화살표 아이콘을 클릭하면 트리 노드를 펼치거나 접을 수 있어요.
동적 높이 행 (Dynamic Height Rows)
가상 테이블은 동적 높이를 가진 행을 렌더링할 수 있어요. 데이터를 다루는데 콘텐츠의 크기가 확실하지 않다면, 콘텐츠 높이에 맞춰 조절되는 행을 렌더링하는 데 이 기능이 이상적이에요. 활성화하려면 estimated-row-height 속성을 전달하세요. 추정 높이가 실제 콘텐츠와 가까울수록 렌더링 경험이 더 부드러워져요.
TIP 각 행의 높이는 행을 렌더링하는 동안 동적으로 측정돼요. 따라서 많은 양의 데이터를 표시하려고 하면 UI가 튕길 수 있어요.
상세 보기 (Detail View)
동적 높이 렌더링을 사용해서 테이블 안에 상세 보기를 표시할 수도 있어요.
커스텀 푸터 (Customized Footer)
결론 메시지나 정보를 표시하려고 할 때 커스텀 푸터를 렌더링할 수 있어요.
const generateColumns = (length = 10, prefix = 'column-', props?: any) =>
Array.from({ length }).map((_, columnIndex) => ({
...props,
key: `${prefix}${columnIndex}`,
dataKey: `${prefix}${columnIndex}`,
title: `Column ${columnIndex}`,
width: 150,
}))
const generateData = (
columns: ReturnType,
length = 200,
prefix = 'row-'
) =>
Array.from({ length }).map((_, rowIndex) => {
return columns.reduce(
(rowData, column, columnIndex) => {
rowData[column.dataKey] = `Row ${rowIndex} - Col ${columnIndex}`
return rowData
},
{
id: `${prefix}${rowIndex}`,
parentId: null,
}
)
})
const columns = generateColumns(10)
const data = generateData(columns, 200)
커스텀 빈 렌더러 (Customized Empty Renderer)
커스텀 빈 요소를 렌더링할 수 있어요.
const generateColumns = (length = 10, prefix = 'column-', props?: any) =>
Array.from({ length }).map((_, columnIndex) => ({
...props,
key: `${prefix}${columnIndex}`,
dataKey: `${prefix}${columnIndex}`,
title: `Column ${columnIndex}`,
width: 150,
}))
const columns = generateColumns(10)
오버레이 (Overlay)
로딩 인디케이터 같은 것을 표시하고 싶을 때 테이블 위에 오버레이를 렌더링할 수 있어요.
import { Loading as LoadingIcon } from '@element-plus/icons-vue'
const generateColumns = (length = 10, prefix = 'column-', props?: any) =>
Array.from({ length }).map((_, columnIndex) => ({
...props,
key: `${prefix}${columnIndex}`,
dataKey: `${prefix}${columnIndex}`,
title: `Column ${columnIndex}`,
width: 150,
}))
const generateData = (
columns: ReturnType,
length = 200,
prefix = 'row-'
) =>
Array.from({ length }).map((_, rowIndex) => {
return columns.reduce(
(rowData, column, columnIndex) => {
rowData[column.dataKey] = `Row ${rowIndex} - Col ${columnIndex}`
return rowData
},
{
id: `${prefix}${rowIndex}`,
parentId: null,
}
)
})
const columns = generateColumns(10)
const data = generateData(columns, 200)
.example-showcase .el-table-v2__overlay {
z-index: 9;
}
수동 스크롤 (Manual Scrolling)
Table V2가 제공하는 메서드를 사용해서 원하는 오프셋/행으로 수동/프로그래매틱하게 스크롤할 수 있어요.
TIP scrollToRow의 두 번째 파라미터는 스크롤 전략이며 기본값은 auto예요. auto는 스크롤할 위치를 스스로 계산해요. 특정 위치로 스크롤하고 싶다면 직접 전략을 정의할 수 있어요. 사용 가능한 옵션은 "auto" | "center" | "end" | "start" | "smart"예요. smart와 auto의 차이는 auto가 smart 스크롤 전략의 하위 집합이라는 점이에요.
import { ref } from 'vue'
import type { TableV2Instance } from 'element-plus'
const generateColumns = (length = 10, prefix = 'column-', props?: any) =>
Array.from({ length }).map((_, columnIndex) => ({
...props,
key: `${prefix}${columnIndex}`,
dataKey: `${prefix}${columnIndex}`,
title: `Column ${columnIndex}`,
width: 150,
}))
const generateData = (
columns: ReturnType,
length = 200,
prefix = 'row-'
) =>
Array.from({ length }).map((_, rowIndex) => {
return columns.reduce(
(rowData, column, columnIndex) => {
rowData[column.dataKey] = `Row ${rowIndex} - Col ${columnIndex}`
return rowData
},
{
id: `${prefix}${rowIndex}`,
parentId: null,
}
)
})
const columns = generateColumns(10)
const data = generateData(columns, 200)
const tableRef = ref()
const scrollDelta = ref(200)
const scrollRows = ref(10)
function scrollByPixels() {
tableRef.value?.scrollToTop(scrollDelta.value)
}
function scrollByRows() {
tableRef.value?.scrollToRow(scrollRows.value)
}
TableV2 API
TableV2 속성 (Attributes)
| Name | Description | Type | Default |
|---|---|---|---|
| cache | 성능 향상을 위해 미리 렌더링하는 행의 수 | number | 2 |
| estimated-row-height | 동적 높이 행 렌더링을 위한 추정 행 높이 | number | — |
| header-class | 헤더 래퍼에 전달되는 커스텀 클래스 이름 | string / Function |
— |
| header-props | 헤더 컴포넌트에 전달되는 커스텀 props 이름 | object / Function |
— |
| header-cell-props | 헤더 셀 컴포넌트에 전달되는 커스텀 props 이름 | object / Function |
— |
| header-height | 헤더의 높이는 height로 설정돼요. 배열이 주어지면 그 길이만큼 헤더 행을 렌더링해요 | number/ number[] | 50 |
| footer-height | 푸터 요소의 높이. 제공되면 테이블 높이 계산에 포함돼요 | number | 0 |
| row-class | 행 래퍼에 전달되는 커스텀 클래스 이름 | string / Function |
— |
| row-key | 각 행의 키. 제공하지 않으면 행의 인덱스가 돼요 | string / Symbol / number | id |
| row-props | 행 컴포넌트에 전달되는 커스텀 props 이름 | object / Function |
— |
| row-height | 각 행의 높이. 테이블 전체 높이 계산에 사용돼요 | number | 50 |
| row-event-handlers | 각 행에 연결되는 핸들러 모음 | object |
— |
| cell-props | 각 셀(헤더 셀 제외)에 전달되는 추가 props | object / Function |
— |
| columns | 열 정의의 배열 | Column[] | — |
| data | 테이블에서 렌더링할 데이터의 배열 | Data[] | [] |
| data-getter | 데이터 소스에서 데이터를 가져오는 방식을 커스터마이즈하는 메서드 | Function<DataGetter |
— |
| fixed-data | 메인 콘텐츠 위, 헤더 아래에 렌더링되는 행의 데이터 | object | — |
| expand-column-key | 어떤 행이 확장 가능한지 나타내는 열 키 | string | — |
| expanded-row-keys | 확장된 행의 키 배열. v-model과 함께 사용할 수 있어요 | KeyType[] | — |
| default-expanded-row-keys | 기본 확장 행의 키 배열. 반응형이 아니예요 | KeyType[] | — |
| class | 가상 테이블의 클래스 이름. 세 개의 테이블(왼쪽, 오른쪽, 메인) 모두에 적용돼요 | string / array / object | — |
| fixed | 테이블 열의 너비를 고정(fixed)할지 유연(flexible)하게 할지를 나타내는 플래그 | boolean | false |
| width required | 테이블의 너비 | number | — |
| height required | 테이블의 높이 | number | — |
| max-height | 테이블의 최대 높이 | number | — |
| indent-size | 트리 테이블의 가로 들여쓰기 | number | 12 |
| h-scrollbar-size | 테이블의 가로 스크롤바 크기. 가로·세로 스크롤바가 겹치는 것을 방지하는 데 사용돼요 | number | 6 |
| v-scrollbar-size | 테이블의 세로 스크롤바 크기. 가로·세로 스크롤바가 겹치는 것을 방지하는 데 사용돼요 | number | 6 |
| scrollbar-always-on | true면 마우스를 테이블 위에 올렸을 때만이 아니라 항상 스크롤바를 표시해요 | boolean | false |
| sort-by | 정렬 표시기 | object |
{} |
| sort-state | 다중 정렬 표시기 | object |
undefined |
TableV2 슬롯 (Slots)
| Name | Type |
|---|---|
| cell | object |
| header | object |
| header-cell | object |
| row | object |
| footer | — |
| empty | — |
| overlay | — |
TableV2 이벤트 (Events)
| Name | Description | Parameters |
|---|---|---|
| column-sort | 열이 정렬될 때 호출돼요 | object |
| expanded-rows-change | 확장된 행이 변경될 때 호출돼요 | KeyType[] |
| end-reached | 테이블의 끝에 도달할 때 호출돼요. 콜백에는 남은 거리가 포함되는데, 보통 스크롤바 높이예요 | Function |
| scroll | 스크롤 후 호출돼요 | object |
| rows-rendered | 행이 렌더링될 때 호출돼요 | object |
| row-expand | 화살표 아이콘을 클릭해서 트리 노드를 펼치거나 접을 때 호출돼요 | object |
TableV2 Exposes
| Method | Description | Parameters |
|---|---|---|
| scrollTo | 주어진 위치로 스크롤 | Function |
| scrollToLeft | 주어진 가로 위치로 스크롤 | Function |
| scrollToTop | 주어진 세로 위치로 스크롤 | Function |
| scrollToRow | 지정된 스크롤 전략으로 주어진 행으로 스크롤 | Function |
TIP 이것들은 JavaScript Object이므로 이 속성들에는 kebab-case를 사용할 수 없어요.
Column 속성 (Attributes)
| Name | Description | Type | Default |
|---|---|---|---|
| align | 테이블 셀 콘텐츠의 정렬 | Alignment | left |
| class | 열의 클래스 이름 | string | — |
| key | 고유 식별자 | KeyType | — |
| dataKey | 데이터의 고유 식별자 | KeyType | — |
| fixed | 열의 고정 방향 | boolean / FixedDir | false |
| flexGrow | CSSProperties의 flex grow. 고정 테이블이 아닐 때만 유용해요 | number | 0 |
| flexShrink | CSSProperties의 flex shrink. 고정 테이블이 아닐 때만 유용해요 | number | 1 |
| headerClass | 헤더 열 클래스 커스터마이즈에 사용 | string | — |
| hidden | 열이 보이지 않는지 여부 | boolean | — |
| style | 열 셀의 커스텀 스타일. 그리드 셀과 병합돼요 | object | — |
| sortable | 열이 정렬 가능한지 여부 | boolean | — |
| title | 헤더 셀에 렌더링되는 기본 텍스트 | string | — |
| maxWidth | 열의 최대 너비 | number | — |
| minWidth | 열의 최소 너비 | number | — |
| width required | 열의 너비 | number | — |
| cellRenderer | 커스텀 셀 렌더러 | VueComponent / (props: CellRenderProps) => VNode | — |
| headerCellRenderer | 커스텀 헤더 렌더러 | VueComponent / (props: HeaderRenderProps) => VNode | — |
Typings
type HeaderClassGetter = (param: {
columns: Column[]
headerIndex: number
}) => string
type HeaderPropsGetter = (param: {
columns: Column[]
headerIndex: number
}) => Record
type HeaderCellPropsGetter = (param: {
columns: Column[]
column: Column
columnIndex: number
headerIndex: number
style: CSSProperties
}) => Record
type RowClassGetter = (param: {
columns: Column[]
rowData: any
rowIndex: number
}) => string
type RowPropsGetter = (param: {
columns: Column[]
rowData: any
rowIndex: number
}) => Record
type CellPropsGetter = (param: {
column: Column
columns: Column[]
columnIndex: number
cellData: any
rowData: any
rowIndex: number
}) => void
type DataGetterParams = {
columns: Column[]
column: Column
columnIndex: number
} & RowCommonParams
type DataGetter = (params: DataGetterParams) => T
type CellRenderProps = {
cellData: T
column: Column
columns: Column[]
columnIndex: number
rowData: any
rowIndex: number
}
type HeaderRenderProps = {
column: Column
columns: Column[]
columnIndex: number
headerIndex: number
}
type ScrollParams = {
xAxisScrollDir: 'forward' | 'backward'
scrollLeft: number
yAxisScrollDir: 'forward' | 'backward'
scrollTop: number
}
type CellSlotProps = {
column: Column
columns: Column[]
columnIndex: number
depth: number
style: CSSProperties
rowData: any
rowIndex: number
isScrolling: boolean
expandIconProps?:
| {
rowData: any
rowIndex: number
onExpand: (expand: boolean) => void
}
| undefined
}
type HeaderSlotProps = {
cells: VNode[]
columns: Column[]
headerIndex: number
}
type HeaderCellSlotProps = {
class: string
columns: Column[]
column: Column
columnIndex: number
headerIndex: number
style: CSSProperties
headerCellProps?: any
sortBy: SortBy
sortState?: SortState | undefined
onColumnSorted: (e: MouseEvent) => void
}
type RowCommonParams = {
rowData: any
rowIndex: number
}
type RowEventHandlerParams = {
rowKey: KeyType
event: Event
} & RowCommonParams
type RowEventHandler = (params: RowEventHandlerParams) => void
type RowEventHandlers = {
onClick?: RowEventHandler
onContextmenu?: RowEventHandler
onDblclick?: RowEventHandler
onMouseenter?: RowEventHandler
onMouseleave?: RowEventHandler
}
type RowsRenderedParams = {
rowCacheStart: number
rowCacheEnd: number
rowVisibleStart: number
rowVisibleEnd: number
}
type RowSlotProps = {
columns: Column[]
rowData: any
columnIndex: number
rowIndex: number
data: any
key: number | string
isScrolling?: boolean
style: CSSProperties
}
type RowExpandParams = {
expanded: boolean
rowKey: KeyType
} & RowCommonParams
type Data = {
[key: KeyType]: any
children?: Array
}
type FixedData = Data
type KeyType = string | number | symbol
type ColumnSortParam = { column: Column; key: KeyType; order: SortOrder }
enum SortOrder {
ASC = 'asc',
DESC = 'desc',
}
enum Alignment {
LEFT = 'left',
CENTER = 'center',
RIGHT = 'right',
}
type SortBy = { key: KeyType; Order: SortOrder }
type SortState = Record
FAQs
첫 번째 열에 체크박스가 있는 리스트를 어떻게 렌더링하나요?
자신만의 셀 렌더러를 정의할 수 있으므로, Customize Cell Renderer 예시가 한 것처럼 체크박스를 직접 렌더링하고 상태도 직접 유지하면 돼요.
가상화 테이블이 TableV1보다 기능이 적은 이유는 무엇인가요?
가상화 테이블은 기능을 적게 제공하고 사용자가 필요에 따라 자신만의 기능을 구현하도록 하려고 해요. 너무 많은 기능을 통합하면 코드를 유지보수하기 어려워지고, 대부분의 사용자에게는 기본 기능으로 충분해요. 몇 가지 핵심 기능은 아직 개발되지 않았어요. 여러분의 의견을 듣고 싶어요. Discord에 참여해서 소식을 받아보세요.