docs(components): [virtualized-table] finishing leftovers (#7496)

- Finishing the documentation examples
- Add APIs to the documentation
- Fix some issue while updating the documentations
This commit is contained in:
JeremyWuuuuu
2022-05-05 00:54:49 +08:00
committed by GitHub
parent 49d48b8f3b
commit 14cfb0500f
37 changed files with 1101 additions and 132 deletions

View File

@@ -2,7 +2,6 @@ import fs from 'fs'
import path from 'path'
import { vpRoot } from '@element-plus/build-utils'
import { languages } from '../utils/lang'
import { features } from './features'
import type { HeadConfig } from 'vitepress'
@@ -59,13 +58,6 @@ export const head: HeadConfig[] = [
content: '/browserconfig.xml',
},
],
[
'link',
{
rel: 'stylesheet',
href: '//fonts.loli.net/css?family=Inter:300,400,500,600|Open+Sans:400,600;display=swap',
},
],
[
'script',
{},
@@ -84,7 +76,9 @@ export const head: HeadConfig[] = [
],
[
'script',
{},
{
async: 'true',
},
`window.dataLayer = window.dataLayer || [];
function gtag(){dataLayer.push(arguments);}
gtag('js', new Date());
@@ -92,7 +86,9 @@ gtag('config', 'UA-175337989-1');`,
],
[
'script',
{},
{
async: 'true',
},
`(function(h,o,t,j,a,r){
h.hj=h.hj||function(){(h.hj.q=h.hj.q||[]).push(arguments)};
h._hjSettings={hjid:2894908,hjsv:6};
@@ -102,11 +98,24 @@ gtag('config', 'UA-175337989-1');`,
a.appendChild(r);
})(window,document,'https://static.hotjar.com/c/hotjar-','.js?sv=');`,
],
]
if (features.theme) {
head.push([
[
'script',
{},
fs.readFileSync(path.resolve(vpRoot, 'dark-mode.js'), 'utf-8'),
])
}
{
async: 'true',
},
`
var resource = document.createElement('link');
resource.setAttribute("rel", "stylesheet");
resource.setAttribute("href","//fonts.loli.net/css?family=Inter:300,400,500,600|Open+Sans:400,600;display=swap");
resource.setAttribute("type","text/css");
var head = document.querySelector('head');
head.appendChild(resource);
`,
],
]
head.push([
'script',
{},
fs.readFileSync(path.resolve(vpRoot, 'dark-mode.js'), 'utf-8'),
])

View File

@@ -194,6 +194,11 @@
"link": "/table",
"text": "Table"
},
{
"link": "/table-v2",
"text": "Virtualized Table",
"promotion": "2.2.0"
},
{
"link": "/tag",
"text": "Tag"

View File

@@ -237,6 +237,14 @@ form {
margin: 0;
}
details {
summary {
margin: 16px 0;
font-size: 18px;
font-weight: 600;
}
}
.el-popover.is-light.translation-popup {
background-color: var(--bg-color) !important;
border-color: var(--border-color);

View File

@@ -3,7 +3,7 @@ title: Virtualized Table
lang: en-US
---
# Virtualized Table
# Virtualized Table <el-tag round effect="plain">Beta</el-tag>
Along with the evolutionary web development, table component has always been the most popular component in our web apps especially for dashboards, data analysis. For [Table V1](./table.md), with even just 1000 records of data, it can be very annoying when using it, because the poor performance.
@@ -11,6 +11,9 @@ With Virtualized Table, you can render massive chunks of data in a blink of an e
:::tip
This component is **still under testing**, use at your own risk. if you found any bug or issue please report it at [GitHub](https://github.com/element-plus/element-plus/issues) for us to fix. Also there were some APIs which was not mentioned in this documentation, some of them were not
fully developed yet, so that we are not mentioning them here.
**Even though** Virtualized Table is efficient, but when the data load is too big, your **network**, **memory size** can be the bottle neck of your app. So keep in mind that Virtualized Table is never the ultimate solution for everything, consider paginate your data, add filters etc.
:::
@@ -99,6 +102,27 @@ table-v2/filter
:::
## Sortable
You can sort the table with sort state.
:::demo
table-v2/sort
:::
## Controlled Sort
You can define multiple sortable column when you need it. Keep in mind that if you define multiple sortable columns, the UI
might seem strange to your users since the it is unclear which column is being sorted.
:::demo
table-v2/controlled-sort
:::
## Colspan
Virtualized table did not use built-in `table` element, so that `colspan` and `rowspan` is a little bit different than [TableV1](./table.md). With customized row renderer, we can still do that. In this case, you'll learn how to do that.
@@ -129,3 +153,281 @@ We can combine rowspan and colspan together to meet your business goal!
table-v2/spans
:::
## Tree data
Virtual Table can also render data like tree, you can expand/collapse the tree node by clicking the arrow icon.
:::demo
table-v2/tree-data
:::
## Dynamic height rows
Virtual Table can also render rows with dynamic height, when you have data without knowing how big the content it would be, you
might want to use this feature for rendering a dynamic height row. You must pass down `estimated-row-height` to enable this
feature, and the closer the estimated height is, the smoother the rendering will be.
:::tip
The height of each row is dynamically measured while rendering the rows, so that the UI **might be** bouncing if you are trying
to render a large amount of data.
:::
:::demo
table-v2/dynamic-height
:::
## Detail view
With dynamic height rendering, we can use that to render detail view in the table.
:::demo
table-v2/detailed-view
:::
## Customized Footer
Rendering a customized footer when you want to show a concluding message or information.
:::demo
table-v2/footer
:::
## Customized Empty Renderer
Render customized empty element
:::demo
table-v2/empty
:::
## Overlay
Render an overlay above the table when you want to show a loading indicator or something else.
:::demo
table-v2/overlay
:::
## TableV2 Attributes
| Attribute | Description | Type | Default |
| ------------------------- | -------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------- | --------- |
| cache | Number of rows rendered in advance for boosting the performance | Number | 2 |
| estimated-row-height | The estimated row height for rendering dynamic height rows | Number | - |
| header-class | Customized class name passed to header wrapper | String/Function\<[HeaderClassGetter](#typings)\> | - |
| header-props | Customized props name passed to header component | Object/Function\<[HeaderPropsGetter](#typings)\> | - |
| header-cell-props | Customized props name passed to header cell component | Object/Function\<[HeaderCellPropsGetter](#typings)\> | - |
| header-height | The height of entire header, when it's array, it will render as many header rows as the given array's length | Number/Array\<Number\> | 50 |
| footer-height | The height of the footer element, when presented, it will be part of the calculation of the table's height. | Number | 0 |
| row-class | Customized class name passed to row wrapper | String/Function\<[RowClassGetter](#typings)\> | - |
| row-key | The key of each row, if not provided, it will be the index of the row | String/Symbol/Number | id |
| row-props | Customized props name passed to row component | Object/Function\<[RowPropsGetter](#typings)\> | - |
| row-height | The height of each row, used for calculating the total height of the table | Number | 50 |
| columns | An array of column definitions. | Array\<[Column](#column-attribute)\> | - |
| data | An array of data to be rendered in the table. | Array\<[Data](#typings)\> | [] |
| data-getter | An method which helps customizing the how to fetch the data from the data source. | Function | - |
| fixed-data | Data for rendering rows above the main content and below the header | Array\<[Data](#typings)\> | - |
| expand-column-key | The column key indicates which row is expandable | String | - |
| expanded-row-keys | An array of keys for expanded rows, can be used with `v-model` | Array\<[KeyType](#typings)\> | - |
| default-expanded-row-keys | An array of keys for default expanded rows, **NON REACTIVE** | Array\<[KeyType](#typings)\> | - |
| class | Class name for the the virtual table, will be applied to all three tables (left, right, main) | String/Array/Object | - |
| fixed | Flag indicates the table column's width is a fixed or flexible. | Boolean | false |
| width \* | Width for the table, required | Number | - |
| height \* | Height for the table, required | Number | - |
| max-height | Maximum height for the table | Number | - |
| h-scrollbar-size | Indicates the horizontal scrollbar's size for the table, used to prevent the horizontal and vertical scrollbar to collapse | Number | 6 |
| v-scrollbar-size | Indicates the horizontal scrollbar's size for the table, used to prevent the horizontal and vertical scrollbar to collapse | Number | 6 |
| scrollbar-always-on | If true, the scrollbar will always be shown instead of when mouse is placed above the table | Boolean | false |
| sort-by | Sort indicator | Object\<[SortBy](#typings)\> | {} |
| sort-state | Multiple sort indicator | Object\<[SortState](#typings)\> | undefined |
## TableV2 Slots
| Name | Params |
| ----------- | ------------------------------- |
| cell | [CellSlotProps](#typings) |
| header | [HeaderSlotProps](#typings) |
| header-cell | [HeaderCellSlotProps](#typings) |
| row | [RowSlotProps](#typings) |
| footer | - |
| empty | - |
| overlay | - |
## Table Methods
| Event Name | Description | Parameters |
| -------------------- | --------------------------------------------- | ---------------------------------------- |
| column-sort | Invoked when column sorted | Object\<ColumnSortParam\> |
| expanded-rows-change | Invoked when expanded rows changed | `Array<KeyType>` |
| end-reached | Invoked when the end of the table is reached | - |
| scroll | Invoked after scrolled | Object\<[ScrollParams](#typings)\> |
| rows-rendered | Invoked when rows are rendered | Object\<[RowsRenderedParams](#typings)\> |
| row-event-handlers | A collection of handlers attached to each row | Object\<[RowEventHandlers](#typings)\> |
:::tip
Note that these are `JavaScript` Objects, so that you **CANNOT USE** kebab-case for these attributes
:::
## Column Attribute
| Attribute | Description | Type | Default |
| -------------- | ------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------- |
| align | Alignment of the table cell content | [Alignment](https://github.com/element-plus/element-plus/blob/b92b22932758f0ddea98810ae248f6ca62f77e25/packages/components/table-v2/src/constants.ts#L6) | left |
| class | Class name for the column | String | - |
| fixed | Fixed direction of the column | Boolean/[FixedDir](https://github.com/element-plus/element-plus/blob/b92b22932758f0ddea98810ae248f6ca62f77e25/packages/components/table-v2/src/constants.ts#L11) | false |
| flexGrow | CSSProperties flex grow, Only useful when not this is not a fixed table | Number | 0 |
| flexShrink | CSSProperties flex shrink, Only useful when not this is not a fixed table | Number | 1 |
| headerClass | Used for customizing header column class | String | - |
| hidden | Whether the column is invisible | Boolean | - |
| style | Customized style for column cell, will be merged with grid cell | CSSProperties | - |
| sortable | Indicates whether the column is sortable | Boolean | - |
| title | The default text rendered in header cell | String | - |
| maxWidth | Maximum width for the column | String | - |
| minWidth | Minimum width for the column | String | - |
| width \* | Width for the column **Required** | Number | - |
| cellRenderer | Customized Cell renderer | VueComponent/(props: [CellRenderProps](#renderer-typings)) => VNode | - |
| headerRenderer | Customized Header renderer | VueComponent/(props: [HeaderRenderProps](#renderer-typings)) => VNode | - |
## Typings
<details>
<summary>Show Type Declarations</summary>
```ts
type HeaderClassGetter = (param: {
columns: Column<any>[]
headerIndex: number
}) => string
type HeaderPropsGetter = (param: {
columns: Column<any>[]
headerIndex: number
}) => Record<string, any>
type HeaderCellPropsGetter = (param: {
columns: Column<any>[]
column: Column<any>
columnIndex: number
headerIndex: number
style: CSSProperties
}) => Record<string, any>
type RowClassGetter = (param: {
columns: Column<any>[]
rowData: any
rowIndex: number
}) => string
type RowPropsGetter = (param: {
columns: Column<any>[]
rowData: any
rowIndex: number
}) => Record<string, any>
type CellRenderProps<T> = {
cellData: T
column: Column<T>
columns: Column<T>[]
columnIndex: number
rowData: any
rowIndex: number
}
type HeaderRenderProps<T> = {
column: Column<T>
columns: Column<T>[]
columnIndex: number
headerIndex: number
}
type ScrollParams = {
xAxisScrollDir: 'forward' | 'backward'
scrollLeft: number
yAxisScrollDir: 'forward' | 'backward'
scrollTop: number
}
type CellSlotProps<T> = {
column: Column<T>
columns: Column<T>[]
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<any>[]
headerIndex: number
}
type HeaderCellSlotProps = {
class: string
columns: Column<any>[]
column: Column<any>
columnIndex: number
headerIndex: number
style: CSSProperties
headerCellProps?: any
sortBy: SortBy
sortState?: SortState | undefined
onColumnSorted: (e: MouseEvent) => void
}
type RowSlotProps = {
columnIndex: number
rowIndex: number
data: any
key: number | string
isScrolling?: boolean | undefined
style: CSSProperties
}
type Data = {
[key: KeyType]: any
children?: Array<any>
}
type FixedData = Data
type KeyType = string | number | symbol
type ColumnSortParam<T> = { column: Column<T>; key: KeyType; order: SortOrder }
enum SortOrder {
ASC = 'asc',
DESC = 'desc',
}
type SortBy = { key: KeyType; Order: SortOrder }
type SortState = Record<KeyType, SortOrder>
```
</details>

View File

@@ -1,11 +1,41 @@
<template>
<table-v2 :columns="columns" :data="data" :width="700" :height="400" fixed />
<el-table-v2
:columns="columns"
:data="data"
:width="700"
:height="400"
fixed
/>
</template>
<script lang="ts" setup>
import TableV2 from '@element-plus/components/table-v2/src/table-v2'
import { generateColumns, generateData } from './utils'
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<typeof generateColumns>,
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, 1000)
const data = generateData(columns, 200)
</script>

View File

@@ -1,15 +1,26 @@
<template>
<table-v2 :columns="columns" :data="data" :width="700" :height="400" fixed />
<el-table-v2
:columns="columns"
:data="data"
:width="700"
:height="400"
fixed
/>
</template>
<script lang="tsx" setup>
import { ref } from 'vue'
import dayjs from 'dayjs'
import {
ElButton,
ElIcon,
ElTag,
ElTooltip,
TableV2FixedDir,
} from 'element-plus'
import { Timer } from '@element-plus/icons-vue'
import { ElButton, ElIcon, ElTag, ElTooltip } from '@element-plus/components'
import { FixedDir, TableV2 } from '@element-plus/components/table-v2'
import type { Column } from '@element-plus/components/table-v2'
import type { Column } from 'element-plus'
let id = 0
@@ -25,7 +36,7 @@ const columns: Column<any>[] = [
title: 'Date',
dataKey: 'date',
width: 150,
fixed: FixedDir.LEFT,
fixed: TableV2FixedDir.LEFT,
cellRenderer: ({ cellData: date }) => (
<ElTooltip content={dayjs(date).format('YYYY/MM/DD')}>
{
@@ -63,5 +74,5 @@ const columns: Column<any>[] = [
},
]
const data = ref(Array.from({ length: 5000 }).map(dataGenerator))
const data = ref(Array.from({ length: 200 }).map(dataGenerator))
</script>

View File

@@ -1,14 +1,13 @@
<template>
<table-v2 fixed :columns="columns" :data="data" :width="700" :height="400">
<el-table-v2 fixed :columns="columns" :data="data" :width="700" :height="400">
<template #row="props">
<Row v-bind="props" />
</template>
</table-v2>
</el-table-v2>
</template>
<script lang="ts" setup>
import { cloneVNode } from 'vue'
import TableV2 from '@element-plus/components/table-v2/src/table-v2'
const generateColumns = (length = 10, prefix = 'column-', props?: any) =>
Array.from({ length }).map((_, columnIndex) => ({

View File

@@ -0,0 +1,60 @@
<template>
<el-table-v2
v-model:sort-state="sortState"
:columns="columns"
:data="data"
:width="700"
:height="400"
fixed
@column-sort="onSort"
/>
</template>
<script lang="ts" setup>
import { ref } from 'vue'
import { TableV2SortOrder } from 'element-plus'
import type { SortBy, SortState } 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<typeof generateColumns>,
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 = ref(generateData(columns, 200))
columns[0].sortable = true
columns[1].sortable = true
const sortState = ref<SortState>({
'column-0': TableV2SortOrder.DESC,
'column-1': TableV2SortOrder.ASC,
})
const onSort = ({ key, order }: SortBy) => {
sortState.value[key] = order
data.value = data.value.reverse()
}
</script>

View File

@@ -0,0 +1,88 @@
<template>
<el-table-v2
:columns="columns"
:data="data"
:estimated-row-height="50"
:expand-column-key="columns[0].key"
:width="700"
:height="400"
>
<template #row="props">
<Row v-bind="props" />
</template>
</el-table-v2>
</template>
<script lang="tsx" setup>
import { ref } from 'vue'
const detailedText = `Velit sed aspernatur tempora. Natus consequatur officiis dicta vel assumenda.
Itaque est temporibus minus quis. Ipsum commodiab porro vel voluptas illum.
Qui quam nulla et dolore autem itaque est.
Id consequatur ipsum ea fuga et odit eligendi impedit.
Maiores officiis occaecati et magnam et sapiente est velit sunt.
Non et tempore temporibus. Excepturi et quos. Minus distinctio aut.
Voluptatem ea excepturi omnis vel. Non aperiam sit sed laboriosam eaque omnis deleniti.
Est molestiae omnis non et nulla repudiandae fuga sit.`
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<typeof generateColumns>,
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 = ref(
generateData(columns, 200).map((data) => {
data.children = [
{
id: `${data.id}-detail-content`,
detail: detailedText,
},
]
return data
})
)
const Row = ({ cells, rowData }) => {
if (rowData.detail) return <div class="p-6">{rowData.detail}</div>
return cells
}
Row.inheritAttrs = false
console.log(data)
</script>
<style>
.el-table-v2__row-depth-0 {
height: 50px;
}
.el-table-v2__cell-text {
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
</style>

View File

@@ -0,0 +1,99 @@
<template>
<el-table-v2
:columns="columns"
:data="data"
:sort-by="sort"
:estimated-row-height="40"
:width="700"
:height="400"
fixed
@column-sort="onColumnSort"
/>
</template>
<script lang="tsx" setup>
import { ref } from 'vue'
import {
ElButton,
ElTag,
TableV2FixedDir,
TableV2SortOrder,
} from 'element-plus'
import type { Column, SortBy } from '@element-plus/components/table-v2'
const longText =
'Quaerat ipsam necessitatibus eum quibusdam est id voluptatem cumque mollitia.'
const midText = 'Corrupti doloremque a quos vero delectus consequatur.'
const shortText = 'Eius optio fugiat.'
const textList = [shortText, midText, longText]
// generate random number in range 0 to 2
let id = 0
const dataGenerator = () => ({
id: `random:${++id}`,
name: 'Tom',
date: '2016-05-03',
description: textList[Math.floor(Math.random() * 3)],
})
const columns: Column<any>[] = [
{
key: 'id',
title: 'Id',
dataKey: 'id',
width: 150,
sortable: true,
fixed: TableV2FixedDir.LEFT,
},
{
key: 'name',
title: 'Name',
dataKey: 'name',
width: 150,
align: 'center',
cellRenderer: ({ cellData: name }) => <ElTag>{name}</ElTag>,
},
{
key: 'description',
title: 'Description',
dataKey: 'description',
width: 150,
cellRenderer: ({ cellData: description }) => (
<div style="padding: 10px 0;">{description}</div>
),
},
{
key: 'operations',
title: 'Operations',
cellRenderer: () => (
<>
<ElButton size="small">Edit</ElButton>
<ElButton size="small" type="danger">
Delete
</ElButton>
</>
),
width: 150,
align: 'center',
},
]
const data = ref(
Array.from({ length: 200 })
.map(dataGenerator)
.sort((a, b) => (a.name > b.name ? 1 : -1))
)
const sort = ref<SortBy>({ key: 'name', order: TableV2SortOrder.ASC })
const onColumnSort = (sortBy: SortBy) => {
const order = sortBy.order === 'asc' ? 1 : -1
const dataClone = [...data.value]
dataClone.sort((a, b) => (a[sortBy.key] > b[sortBy.key] ? order : -order))
sort.value = sortBy
data.value = dataClone
}
</script>

View File

@@ -0,0 +1,29 @@
<template>
<el-table-v2
:columns="columns"
:data="[]"
:row-height="40"
:width="700"
:height="400"
:footer-height="50"
>
<template #empty>
<div class="flex items-center justify-center h-100%">
<el-empty />
</div>
</template>
</el-table-v2>
</template>
<script lang="tsx" setup>
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)
</script>

View File

@@ -1,5 +1,5 @@
<template>
<table-v2
<el-table-v2
fixed
:columns="fixedColumns"
:data="data"
@@ -9,11 +9,16 @@
</template>
<script lang="tsx" setup>
import { ref } from 'vue'
import { ElButton, ElCheckbox, ElIcon, ElPopover } from 'element-plus'
import { FixedDir, TableV2 } from '@element-plus/components/table-v2'
import {
ElButton,
ElCheckbox,
ElIcon,
ElPopover,
TableV2FixedDir,
} from 'element-plus'
import { Filter } from '@element-plus/icons-vue'
import type { HeaderCellSlotProps } from '@element-plus/components/table-v2'
import type { HeaderCellSlotProps } from 'element-plus'
const generateColumns = (length = 10, prefix = 'column-', props?: any) =>
Array.from({ length }).map((_, columnIndex) => ({
@@ -58,7 +63,7 @@ const onFilter = () => {
const onReset = () => {
shouldFilter.value = false
popoverRef.value.hide()
onFilter()
}
columns[0].headerCellRenderer = (props: HeaderCellSlotProps) => {
@@ -74,7 +79,7 @@ columns[0].headerCellRenderer = (props: HeaderCellSlotProps) => {
Filter Text
</ElCheckbox>
</div>
<div class="table-v2__demo-filter">
<div class="el-table-v2__demo-filter">
<ElButton type="text" onClick={onFilter}>
Confirm
</ElButton>
@@ -96,15 +101,15 @@ columns[0].headerCellRenderer = (props: HeaderCellSlotProps) => {
}
const fixedColumns = columns.map((column, columnIndex) => {
let fixed: FixedDir | undefined = undefined
if (columnIndex < 2) fixed = FixedDir.LEFT
if (columnIndex > 9) fixed = FixedDir.RIGHT
let fixed: TableV2FixedDir | undefined = undefined
if (columnIndex < 2) fixed = TableV2FixedDir.LEFT
if (columnIndex > 9) fixed = TableV2FixedDir.RIGHT
return { ...column, fixed, width: 100 }
})
</script>
<style>
.table-v2__demo-filter {
.el-table-v2__demo-filter {
border-top: var(--el-border);
margin: 12px -12px -12px;
padding: 0 12px;

View File

@@ -1,5 +1,5 @@
<template>
<table-v2
<el-table-v2
:columns="columns"
:data="data"
:sort-by="sortBy"
@@ -12,9 +12,9 @@
<script lang="ts" setup>
import { ref } from 'vue'
import { FixedDir, SortOrder, TableV2 } from '@element-plus/components/table-v2'
import { TableV2FixedDir, TableV2SortOrder } from 'element-plus'
import type { SortBy } from '@element-plus/components/table-v2'
import type { SortBy } from 'element-plus'
const generateColumns = (length = 10, prefix = 'column-', props?: any) =>
Array.from({ length }).map((_, columnIndex) => ({
@@ -47,14 +47,14 @@ const columns = generateColumns(10)
let data = generateData(columns, 200)
columns[0].fixed = true
columns[1].fixed = FixedDir.LEFT
columns[9].fixed = FixedDir.RIGHT
columns[1].fixed = TableV2FixedDir.LEFT
columns[9].fixed = TableV2FixedDir.RIGHT
for (let i = 0; i < 3; i++) columns[i].sortable = true
const sortBy = ref<SortBy>({
key: 'column-0',
order: SortOrder.ASC,
order: TableV2SortOrder.ASC,
})
const onSort = (_sortBy: SortBy) => {

View File

@@ -0,0 +1,56 @@
<template>
<el-table-v2
:columns="columns"
:data="data"
:row-height="40"
:width="700"
:height="400"
:footer-height="50"
fixed
>
<template #footer
><div
class="flex items-center"
style="
justify-content: center;
height: 100%;
background-color: var(--el-color-primary-light-7);
"
>
Display a message in the footer
</div>
</template>
</el-table-v2>
</template>
<script lang="ts" setup>
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<typeof generateColumns>,
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)
</script>

View File

@@ -1,5 +1,5 @@
<template>
<table-v2
<el-table-v2
fixed
:columns="fixedColumns"
:data="data"
@@ -11,20 +11,16 @@
<template #header="props">
<customized-header v-bind="props" />
</template>
</table-v2>
</el-table-v2>
</template>
<script lang="tsx" setup>
import {
FixedDir,
TableV2,
TableV2Placeholder,
} from '@element-plus/components/table-v2'
import { TableV2FixedDir, TableV2Placeholder } from 'element-plus'
import type { FunctionalComponent } from 'vue'
import type {
HeaderClassNameGetter,
TableV2CustomizedHeaderSlotParam,
} from '@element-plus/components/table-v2'
} from 'element-plus'
const generateColumns = (length = 10, prefix = 'column-', props?: any) =>
Array.from({ length }).map((_, columnIndex) => ({
@@ -56,9 +52,9 @@ const columns = generateColumns(15)
const data = generateData(columns, 200)
const fixedColumns = columns.map((column, columnIndex) => {
let fixed: FixedDir | undefined = undefined
if (columnIndex < 3) fixed = FixedDir.LEFT
if (columnIndex > 12) fixed = FixedDir.RIGHT
let fixed: TableV2FixedDir | undefined = undefined
if (columnIndex < 3) fixed = TableV2FixedDir.LEFT
if (columnIndex > 12) fixed = TableV2FixedDir.RIGHT
return { ...column, fixed, width: 100 }
})
@@ -112,11 +108,11 @@ const headerClass = ({
</script>
<style>
.el-table-v2__header-row .custom-header-cell {
.el-el-table-v2__header-row .custom-header-cell {
border-right: 1px solid var(--el-border-color);
}
.el-table-v2__header-row .custom-header-cell:last-child {
.el-el-table-v2__header-row .custom-header-cell:last-child {
border-right: none;
}

View File

@@ -0,0 +1,54 @@
<template>
<el-table-v2
:columns="columns"
:data="data"
:row-height="40"
:width="700"
:height="400"
>
<template #overlay>
<div
class="el-loading-mask"
style="display: flex; align-items: center; justify-content: center"
>
<el-icon class="is-loading" color="var(--el-color-primary)" :size="26">
<loading-icon />
</el-icon>
</div>
</template>
</el-table-v2>
</template>
<script lang="ts" setup>
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<typeof generateColumns>,
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)
</script>

View File

@@ -1,5 +1,5 @@
<template>
<table-v2
<el-table-v2
:columns="columns"
:data="data"
:row-class="rowClass"
@@ -12,14 +12,16 @@
<script lang="tsx" setup>
import { ref } from 'vue'
import dayjs from 'dayjs'
import {
ElButton,
ElIcon,
ElTag,
ElTooltip,
TableV2FixedDir,
} from 'element-plus'
import { Timer } from '@element-plus/icons-vue'
import { ElButton, ElIcon, ElTag, ElTooltip } from '@element-plus/components'
import { FixedDir, TableV2 } from '@element-plus/components/table-v2'
import type {
Column,
RowClassNameGetter,
} from '@element-plus/components/table-v2'
import type { Column, RowClassNameGetter } from 'element-plus'
let id = 0
@@ -35,7 +37,7 @@ const columns: Column<any>[] = [
title: 'Date',
dataKey: 'date',
width: 150,
fixed: FixedDir.LEFT,
fixed: TableV2FixedDir.LEFT,
cellRenderer: ({ cellData: date }) => (
<ElTooltip content={dayjs(date).format('YYYY/MM/DD')}>
{
@@ -73,7 +75,7 @@ const columns: Column<any>[] = [
},
]
const data = ref(Array.from({ length: 5000 }).map(dataGenerator))
const data = ref(Array.from({ length: 200 }).map(dataGenerator))
const rowClass = ({ rowIndex }: Parameters<RowClassNameGetter<any>>[0]) => {
if (rowIndex % 10 === 5) {

View File

@@ -1,14 +1,13 @@
<template>
<table-v2 fixed :columns="columns" :data="data" :width="700" :height="400">
<el-table-v2 fixed :columns="columns" :data="data" :width="700" :height="400">
<template #row="props">
<Row v-bind="props" />
</template>
</table-v2>
</el-table-v2>
</template>
<script lang="ts" setup>
import { cloneVNode } from 'vue'
import TableV2 from '@element-plus/components/table-v2/src/table-v2'
const generateColumns = (length = 10, prefix = 'column-', props?: any) =>
Array.from({ length }).map((_, columnIndex) => ({

View File

@@ -0,0 +1,60 @@
<template>
<el-table-v2
:columns="columns"
:data="data"
:sort-by="sortState"
:width="700"
:height="400"
fixed
@column-sort="onSort"
/>
</template>
<script lang="ts" setup>
import { ref } from 'vue'
import { TableV2SortOrder } from 'element-plus'
import type { SortBy } 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<typeof generateColumns>,
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)
let data = generateData(columns, 200)
columns[0].sortable = true
const sortState = ref<SortBy>({
key: 'column-0',
order: TableV2SortOrder.ASC,
})
const onSort = (sortBy: SortBy) => {
console.log(sortBy)
data = data.reverse()
sortState.value = sortBy
}
</script>

View File

@@ -1,14 +1,13 @@
<template>
<table-v2 fixed :columns="columns" :data="data" :width="700" :height="400">
<el-table-v2 fixed :columns="columns" :data="data" :width="700" :height="400">
<template #row="props">
<Row v-bind="props" />
</template>
</table-v2>
</el-table-v2>
</template>
<script lang="tsx" setup>
import { cloneVNode } from 'vue'
import { TableV2 } from '@element-plus/components/table-v2'
const generateColumns = (length = 10, prefix = 'column-', props?: any) =>
Array.from({ length }).map((_, columnIndex) => ({

View File

@@ -1,5 +1,5 @@
<template>
<table-v2
<el-table-v2
:columns="columns"
:data="tableData"
:fixed-data="fixedData"
@@ -13,8 +13,33 @@
<script lang="ts" setup>
import { computed, ref } from 'vue'
import { TableV2 } from '@element-plus/components/table-v2'
import { generateColumns, generateData } from './utils'
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<typeof generateColumns>,
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)
@@ -39,7 +64,7 @@ const onScroll = ({ scrollTop }) => {
</script>
<style>
.el-table-v2__fixed-header-row {
.el-el-table-v2__fixed-header-row {
background-color: var(--el-color-primary-light-5);
font-weight: bold;
}

View File

@@ -0,0 +1,128 @@
<template>
<el-table-v2
v-model:expanded-row-keys="expandedRowKeys"
:columns="columns"
:data="treeData"
:width="700"
:expand-column-key="expandColumnKey"
:height="400"
fixed
@row-expand="onRowExpanded"
@expanded-rows-change="onExpandedRowsChange"
/>
</template>
<script lang="ts" setup>
import { computed, ref } from 'vue'
import { TableV2FixedDir } from 'element-plus'
import type { ExpandedRowsChangeHandler, RowExpandHandler } 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<typeof generateColumns>,
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).map((column, columnIndex) => {
let fixed!: TableV2FixedDir
if (columnIndex < 2) fixed = TableV2FixedDir.LEFT
if (columnIndex > 8) fixed = TableV2FixedDir.RIGHT
return { ...column, fixed }
})
const data = generateData(columns, 200)
const expandColumnKey = 'column-0'
// add some sub items
for (let i = 0; i < 50; i++) {
data.push(
{
...data[0],
id: `${data[0].id}-sub-${i}`,
parentId: data[0].id,
[expandColumnKey]: `Sub ${i}`,
},
{
...data[2],
id: `${data[2].id}-sub-${i}`,
parentId: data[2].id,
[expandColumnKey]: `Sub ${i}`,
},
{
...data[2],
id: `${data[2].id}-sub-sub-${i}`,
parentId: `${data[2].id}-sub-${i}`,
[expandColumnKey]: `Sub-Sub ${i}`,
}
)
}
function unflatten(
data: ReturnType<typeof generateData>,
rootId = null,
dataKey = 'id',
parentKey = 'parentId'
) {
const tree: any[] = []
const childrenMap = {}
for (const datum of data) {
const item = { ...datum }
const id = item[dataKey]
const parentId = item[parentKey]
if (Array.isArray(item.children)) {
childrenMap[id] = item.children.concat(childrenMap[id] || [])
} else if (!childrenMap[id]) {
childrenMap[id] = []
}
item.children = childrenMap[id]
if (parentId !== undefined && parentId !== rootId) {
if (!childrenMap[parentId]) childrenMap[parentId] = []
childrenMap[parentId].push(item)
} else {
tree.push(item)
}
}
return tree
}
const treeData = computed(() => unflatten(data))
const expandedRowKeys = ref<string[]>([])
const onRowExpanded = ({ expanded }: Parameters<RowExpandHandler<any>>[0]) => {
console.log('Expanded:', expanded)
}
const onExpandedRowsChange = (
expandedKeys: Parameters<ExpandedRowsChangeHandler>[0]
) => {
console.log(expandedKeys)
}
</script>

View File

@@ -1,26 +0,0 @@
export 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,
}))
export const generateData = (
columns: ReturnType<typeof generateColumns>,
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,
}
)
})

View File

@@ -53,6 +53,7 @@ export * from './space'
export * from './steps'
export * from './switch'
export * from './table'
export * from './table-v2'
export * from './tabs'
export * from './tag'
export * from './time-picker'

View File

@@ -1,13 +1,24 @@
export { Alignment, FixedDir, SortOrder } from './src/constants'
import { withInstall } from '@element-plus/utils'
import TableV2 from './src/table-v2'
export {
Alignment as TableV2Alignment,
FixedDir as TableV2FixedDir,
SortOrder as TableV2SortOrder,
} from './src/constants'
export { default as TableV2 } from './src/table-v2'
export { placeholderSign as TableV2Placeholder } from './src/private'
export const ElTableV2 = withInstall(TableV2)
export type {
Column,
Columns,
SortBy,
SortState,
TableV2CustomizedHeaderSlotParam,
} from './src/types'
export * from './src/table'
export * from './src/row'
export type { HeaderCellSlotProps } from './src/renderers/header-cell'

View File

@@ -11,5 +11,6 @@ const HeaderCell: FunctionalComponent<TableV2HeaderCell> = (props, { slots }) =>
)
HeaderCell.displayName = 'ElTableV2HeaderCell'
HeaderCell.inheritAttrs = false
export default HeaderCell

View File

@@ -64,7 +64,7 @@ function useColumns(
return _columns.reduce<Record<Column<any>['key'], CSSProperties>>(
(style, column) => {
style[column.key] = calcColumnStyle(column, unref(fixed))
style[column.key] = calcColumnStyle(column, unref(fixed), props.fixed)
return style
},
{}

View File

@@ -51,7 +51,7 @@ export const useRow = (
const isDynamic = computed(() => isNumber(props.estimatedRowHeight))
function onRowsRendered(params: onRowRenderedParams) {
props.onRowRendered?.(params)
props.onRowsRendered?.(params)
if (params.rowCacheEnd > unref(lastRenderedRowIndex)) {
lastRenderedRowIndex.value = params.rowCacheEnd

View File

@@ -3,14 +3,21 @@ import type { AnyColumns } from '../types'
export const calcColumnStyle = (
column: AnyColumns[number],
fixedColumn: boolean
fixedColumn: boolean,
fixed: boolean
): CSSProperties => {
const flex = {
flexGrow: 0,
flexShrink: 0,
...(fixed
? {}
: {
flexGrow: column.flexGrow || 0,
flexShrink: column.flexShrink || 1,
}),
}
if (column.fixed) {
if (!fixed) {
flex.flexShrink = 1
}

View File

@@ -13,10 +13,7 @@ import type { UseTableReturn } from '../use-table'
import type { TableV2Props } from '../table'
type CellRendererProps = TableV2RowCellRenderParam &
Pick<
TableV2Props,
'cellProps' | 'expandColumnKey' | 'indentSize' | 'iconSize' | 'rowKey'
> &
Pick<TableV2Props, 'expandColumnKey' | 'indentSize' | 'iconSize' | 'rowKey'> &
UnwrapNestedRefs<Pick<UseTableReturn, 'expandedRowKeys'>> & {
ns: UseNamespaceReturn
}

View File

@@ -76,7 +76,7 @@ const HeaderCellRenderer: FunctionalComponent<HeaderCellRendererProps> = (
...tryCall(headerCellProps, props),
onClick: column.sortable ? onColumnSorted : undefined,
class: cellKls,
cellStyle,
style: cellStyle,
['data-key']: column.key,
}

View File

@@ -25,6 +25,7 @@ import type {
KeyType,
RowCommonParams,
SortBy,
SortState,
} from './types'
/**
@@ -71,17 +72,12 @@ export type RowClassNameGetter<T> = (
*/
export type ColumnSortHandler<T> = (params: ColumnSortParams<T>) => void
export type ColumnResizeHandler<T> = (column: Column<T>, width: number) => void
export type ExpandedRowsChangeHandler = (expandedRowKeys: KeyType[]) => void
export const tableV2Props = buildProps({
cache: tableV2GridProps.cache,
estimatedRowHeight: tableV2RowProps.estimatedRowHeight,
rowKey,
/**
* extra props deriver
*/
cellProps: {
type: definePropType<any | ExtraCellPropGetter<any>>([Object, Function]),
},
// Header attributes
headerClass: {
type: definePropType<string | HeaderClassNameGetter<any>>([
@@ -130,10 +126,6 @@ export const tableV2Props = buildProps({
dataGetter: {
type: definePropType<DataGetter<any>>(Function),
},
dataKey: {
type: String,
default: 'id',
},
fixedData: fixedDataType,
/**
* Expanded keys
@@ -146,7 +138,7 @@ export const tableV2Props = buildProps({
* Attributes
*/
class: classType,
disabled: Boolean,
// disabled: Boolean,
fixed: Boolean,
style: {
type: definePropType<CSSProperties>(Object),
@@ -176,7 +168,7 @@ export const tableV2Props = buildProps({
},
sortState: {
type: definePropType<Record<KeyType, SortOrder>>(Object),
type: definePropType<SortState>(Object),
default: undefined,
},
@@ -186,13 +178,15 @@ export const tableV2Props = buildProps({
onColumnSort: {
type: definePropType<ColumnSortHandler<any>>(Function),
},
onExpandedRowsChange: Function,
onExpandedRowsChange: {
type: definePropType<ExpandedRowsChangeHandler>(Function),
},
onEndReached: {
type: definePropType<(distance: number) => void>(Function),
},
onRowExpand: tableV2RowProps.onRowExpand,
onScroll: tableV2GridProps.onScroll,
onRowRendered: tableV2GridProps.onRowsRendered,
onRowsRendered: tableV2GridProps.onRowsRendered,
rowEventHandlers: tableV2RowProps.rowEventHandlers,
} as const)

View File

@@ -61,19 +61,20 @@ export type HeaderCellRenderer<T> = (
) => VNode
export type Column<T = any> = {
key: KeyType
/**
* Attributes
*/
align?: Alignment
class?: string | ClassNameGetter<T>
dataKey: KeyType
fixed?: true | FixedDirection
flexGrow?: CSSProperties['flexGrow']
flexShrink?: CSSProperties['flexShrink']
title?: string
hidden?: boolean
headerClass?: HeaderClassGetter<T> | string
maxWidth?: number
minWidth?: number
resizable?: boolean
style?: CSSProperties
sortable?: boolean
width: number
@@ -96,6 +97,10 @@ export type SortBy = {
order: SortOrder
}
export type SortState = {
[key: KeyType]: SortOrder
}
export type CustomizedCellsType = VNode<
RendererNode,
RendererElement,

View File

@@ -0,0 +1,2 @@
import '@element-plus/components/base/style/css'
import '@element-plus/theme-chalk/el-table-v2.css'

View File

@@ -0,0 +1,2 @@
import '@element-plus/components/base/style'
import '@element-plus/theme-chalk/src/table-v2.scss'

View File

@@ -85,6 +85,7 @@ import { ElSpace } from '@element-plus/components/space'
import { ElStep, ElSteps } from '@element-plus/components/steps'
import { ElSwitch } from '@element-plus/components/switch'
import { ElTable, ElTableColumn } from '@element-plus/components/table'
import { ElTableV2 } from '@element-plus/components/table-v2'
import { ElTabPane, ElTabs } from '@element-plus/components/tabs'
import { ElTag } from '@element-plus/components/tag'
import { ElTimePicker } from '@element-plus/components/time-picker'
@@ -178,6 +179,7 @@ export default [
ElSwitch,
ElTable,
ElTableColumn,
ElTableV2,
ElTabs,
ElTabPane,
ElTag,

View File

@@ -142,6 +142,7 @@
@include cell-alignment;
height: 100%;
user-select: none;
overflow: hidden;
background-color: getCssVar('table-header', 'bg-color');
color: getCssVar('table-header', 'text-color');
font-weight: bold;
@@ -170,6 +171,8 @@
}
@include e('row') {
border-bottom: getCssVar('table', 'border');
@include center-flex;
transition: background-color getCssVar('transition-duration', '');
@@ -180,7 +183,6 @@
@include e('row-cell') {
height: 100%;
overflow: hidden;
border-bottom: getCssVar('table', 'border');
@include center-flex;
@include cell-padding;
@include cell-alignment;
@@ -210,4 +212,11 @@
white-space: nowrap;
}
}
@include when(dynamic) {
@include e('row') {
overflow: hidden;
align-items: stretch;
}
}
}