mirror of
https://github.com/tusen-ai/naive-ui.git
synced 2025-02-17 13:20:52 +08:00
feat(data-table): group header
This commit is contained in:
parent
2da3e06a34
commit
242238e37a
75
src/data-table/demos/enUS/group-header.demo.md
Normal file
75
src/data-table/demos/enUS/group-header.demo.md
Normal file
@ -0,0 +1,75 @@
|
||||
# Grouped Header
|
||||
|
||||
```html
|
||||
<n-data-table
|
||||
:data="data"
|
||||
:columns="columns"
|
||||
:single-line="false"
|
||||
:pagination="pagination"
|
||||
/>
|
||||
```
|
||||
|
||||
```js
|
||||
import { ref } from 'vue'
|
||||
|
||||
function createCols () {
|
||||
return [
|
||||
{
|
||||
title: 'Name',
|
||||
key: 'name'
|
||||
},
|
||||
{
|
||||
title: 'Attrs',
|
||||
key: 'attrs',
|
||||
children: [
|
||||
{
|
||||
title: 'Attack',
|
||||
key: 'attack',
|
||||
children: [
|
||||
{
|
||||
title: 'Physics Attack',
|
||||
key: 'physicsAttack'
|
||||
},
|
||||
{
|
||||
title: 'Magic Attack',
|
||||
key: 'magicAttack'
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
title: 'Defend',
|
||||
key: 'defend'
|
||||
},
|
||||
{
|
||||
title: 'Speed',
|
||||
key: 'speed'
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
function createData () {
|
||||
return Array.apply(null, { length: 50 }).map((_, i) => {
|
||||
return {
|
||||
name: `name_${i}`,
|
||||
physicsAttack: `physicsAttack_${i}`,
|
||||
magicAttack: `magicAttack_${i}`,
|
||||
defend: `defend_${i}`,
|
||||
speed: `speed_${i}`
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
export default {
|
||||
setup () {
|
||||
return {
|
||||
data: ref(createData()),
|
||||
columns: ref(createCols()),
|
||||
pagination: ref({
|
||||
pageSize: 10
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
@ -17,6 +17,7 @@ border
|
||||
size
|
||||
filter-and-sorter
|
||||
select
|
||||
group-header
|
||||
controlled-page
|
||||
controlled-filter
|
||||
controlled-sorter
|
||||
@ -75,6 +76,7 @@ These methods can help you control table in an uncontrolled manner. However, it'
|
||||
| Name | Type | Default | Description |
|
||||
| --- | --- | --- | --- |
|
||||
| align | `'left' \| 'right' \| 'center'` | `'left'` | Text align in column |
|
||||
| children | `Column[]` | `undefined` | Child nodes of a grouped column |
|
||||
| className | `string` | `undefined` | |
|
||||
| defaultFilterOptionValue | `string \| number \| null` | `null` | The default active filter option value in uncontrolled manner. (works when not using multiple filters) |
|
||||
| defaultFilterOptionValues | `Array<string \| number>` | `[]` | The default active filter option values in uncontrolled manner. (works when there are multiple filters) |
|
||||
|
75
src/data-table/demos/zhCN/group-header.demo.md
Normal file
75
src/data-table/demos/zhCN/group-header.demo.md
Normal file
@ -0,0 +1,75 @@
|
||||
# 表头分组
|
||||
|
||||
```html
|
||||
<n-data-table
|
||||
:data="data"
|
||||
:columns="columns"
|
||||
:single-line="false"
|
||||
:pagination="pagination"
|
||||
/>
|
||||
```
|
||||
|
||||
```js
|
||||
import { ref } from 'vue'
|
||||
|
||||
function createCols () {
|
||||
return [
|
||||
{
|
||||
title: 'Name',
|
||||
key: 'name'
|
||||
},
|
||||
{
|
||||
title: 'Attrs',
|
||||
key: 'attrs',
|
||||
children: [
|
||||
{
|
||||
title: 'Attack',
|
||||
key: 'attack',
|
||||
children: [
|
||||
{
|
||||
title: 'Physics Attack',
|
||||
key: 'physicsAttack'
|
||||
},
|
||||
{
|
||||
title: 'Magic Attack',
|
||||
key: 'magicAttack'
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
title: 'Defend',
|
||||
key: 'defend'
|
||||
},
|
||||
{
|
||||
title: 'Speed',
|
||||
key: 'speed'
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
function createData () {
|
||||
return Array.apply(null, { length: 50 }).map((_, i) => {
|
||||
return {
|
||||
name: `name_${i}`,
|
||||
physicsAttack: `physicsAttack_${i}`,
|
||||
magicAttack: `magicAttack_${i}`,
|
||||
defend: `defend_${i}`,
|
||||
speed: `speed_${i}`
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
export default {
|
||||
setup () {
|
||||
return {
|
||||
data: ref(createData()),
|
||||
columns: ref(createCols()),
|
||||
pagination: ref({
|
||||
pageSize: 10
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
@ -17,6 +17,7 @@ border
|
||||
size
|
||||
filter-and-sorter
|
||||
select
|
||||
group-header
|
||||
controlled-page
|
||||
controlled-filter
|
||||
controlled-sorter
|
||||
@ -75,6 +76,7 @@ custom-filter-menu
|
||||
| 名称 | 类型 | 默认值 | 说明 |
|
||||
| --- | --- | --- | --- |
|
||||
| align | `'left' \| 'right' \| 'center'` | `'left'` | 列内的文本排列 |
|
||||
| children | `Column[]` | `undefined` | 成组列头的子节点 |
|
||||
| className | `string` | `undefined` | |
|
||||
| defaultFilterOptionValue | `string \| number \| null` | `null` | 非受控状态下默认的过滤器选项值(过滤器单选时生效) |
|
||||
| defaultFilterOptionValues | `Array<string \| number>` | `[]` | 非受控状态下默认的过滤器选项值(过滤器多选时生效) |
|
||||
|
@ -28,15 +28,15 @@ import {
|
||||
OnUpdateCheckedRowKeys,
|
||||
OnUpdateSorter,
|
||||
RowKey,
|
||||
TableColumnInfo,
|
||||
TableColumns,
|
||||
TableNode,
|
||||
OnUpdateFilters,
|
||||
MainTableRef,
|
||||
DataTableInjection,
|
||||
DataTableRef,
|
||||
SelectionColInfo
|
||||
DataTableRef
|
||||
} from './interface'
|
||||
import style from './styles/index.cssr'
|
||||
import { useGroupHeader } from './use-group-header'
|
||||
|
||||
export const dataTableProps = {
|
||||
...(useTheme.props as ThemeProps<DataTableTheme>),
|
||||
@ -47,7 +47,7 @@ export const dataTableProps = {
|
||||
minHeight: Number,
|
||||
maxHeight: Number,
|
||||
columns: {
|
||||
type: Array as PropType<Array<TableColumnInfo | SelectionColInfo>>,
|
||||
type: Array as PropType<TableColumns>,
|
||||
default: () => []
|
||||
},
|
||||
data: {
|
||||
@ -201,6 +201,7 @@ export default defineComponent({
|
||||
props
|
||||
)
|
||||
const mainTableInstRef = ref<MainTableRef | null>(null)
|
||||
const { rows, cols, dataRelatedCols } = useGroupHeader(props)
|
||||
const {
|
||||
treeMate: treeMateRef,
|
||||
mergedCurrentPage: mergedCurrentPageRef,
|
||||
@ -217,7 +218,7 @@ export default defineComponent({
|
||||
clearFilters,
|
||||
page,
|
||||
sort
|
||||
} = useTableData(props)
|
||||
} = useTableData(props, { dataRelatedCols })
|
||||
const {
|
||||
doCheckAll,
|
||||
doUncheckAll,
|
||||
@ -251,7 +252,8 @@ export default defineComponent({
|
||||
treeMate: treeMateRef,
|
||||
mergedTheme: themeRef,
|
||||
scrollX: computed(() => props.scrollX),
|
||||
columns: toRef(props, 'columns'),
|
||||
rows,
|
||||
cols,
|
||||
paginatedData: paginatedDataRef,
|
||||
leftActiveFixedColKey,
|
||||
rightActiveFixedColKey,
|
||||
|
@ -4,7 +4,7 @@ import { NCheckbox } from '../../../checkbox'
|
||||
import { NScrollbar, ScrollbarRef } from '../../../scrollbar'
|
||||
import { formatLength } from '../../../_utils'
|
||||
import { DataTableInjection, TmNode } from '../interface'
|
||||
import { createCustomWidthStyle, createRowClassName, getColKey } from '../utils'
|
||||
import { createRowClassName } from '../utils'
|
||||
import Cell from './Cell'
|
||||
|
||||
export default defineComponent({
|
||||
@ -67,11 +67,8 @@ export default defineComponent({
|
||||
default: () => (
|
||||
<table ref="body" class="n-data-table-table">
|
||||
<colgroup>
|
||||
{NDataTable.columns.map((column, index) => (
|
||||
<col
|
||||
key={getColKey(column)}
|
||||
style={createCustomWidthStyle(column, index)}
|
||||
></col>
|
||||
{NDataTable.cols.map((col) => (
|
||||
<col key={col.key} style={col.style}></col>
|
||||
))}
|
||||
</colgroup>
|
||||
<tbody ref="tbody" class="n-data-table-tbody">
|
||||
@ -79,7 +76,7 @@ export default defineComponent({
|
||||
const { rawNode: row } = tmNode
|
||||
const { handleCheckboxUpdateChecked } = this
|
||||
const {
|
||||
columns,
|
||||
cols,
|
||||
fixedColumnLeftMap,
|
||||
fixedColumnRightMap,
|
||||
currentPage,
|
||||
@ -96,8 +93,8 @@ export default defineComponent({
|
||||
createRowClassName(row, index, rowClassName)
|
||||
]}
|
||||
>
|
||||
{columns.map((column) => {
|
||||
const key = getColKey(column)
|
||||
{cols.map((col) => {
|
||||
const { key, column } = col
|
||||
return (
|
||||
<td
|
||||
key={key}
|
||||
|
@ -7,7 +7,6 @@ import FilterButton from '../HeaderButton/FilterButton'
|
||||
import {
|
||||
isColumnSortable,
|
||||
isColumnFilterable,
|
||||
createCustomWidthStyle,
|
||||
createNextSorter,
|
||||
getColKey
|
||||
} from '../utils'
|
||||
@ -56,14 +55,15 @@ export default defineComponent({
|
||||
const {
|
||||
NDataTable: {
|
||||
scrollX,
|
||||
columns,
|
||||
fixedColumnLeftMap,
|
||||
fixedColumnRightMap,
|
||||
currentPage,
|
||||
allRowsChecked,
|
||||
someRowsChecked,
|
||||
leftActiveFixedColKey,
|
||||
rightActiveFixedColKey
|
||||
rightActiveFixedColKey,
|
||||
rows,
|
||||
cols
|
||||
},
|
||||
headerStyle,
|
||||
handleColHeaderClick,
|
||||
@ -81,81 +81,88 @@ export default defineComponent({
|
||||
style={{ width: formatLength(scrollX) }}
|
||||
>
|
||||
<colgroup>
|
||||
{columns.map((column, index) => (
|
||||
<col
|
||||
key={getColKey(column)}
|
||||
style={createCustomWidthStyle(column, index)}
|
||||
/>
|
||||
{cols.map((col) => (
|
||||
<col key={col.key} style={col.style} />
|
||||
))}
|
||||
</colgroup>
|
||||
<thead class="n-data-table-thead">
|
||||
<tr class="n-data-table-tr">
|
||||
{columns.map((column, index) => {
|
||||
const key = getColKey(column)
|
||||
return (
|
||||
<th
|
||||
key={key}
|
||||
style={{
|
||||
textAlign: column.align,
|
||||
left: pxfy(fixedColumnLeftMap[key]),
|
||||
right: pxfy(fixedColumnRightMap[key])
|
||||
}}
|
||||
class={[
|
||||
'n-data-table-th',
|
||||
column.fixed && `n-data-table-th--fixed-${column.fixed}`,
|
||||
{
|
||||
'n-data-table-th--filterable': isColumnFilterable(
|
||||
column
|
||||
),
|
||||
'n-data-table-th--sortable': isColumnSortable(column),
|
||||
'n-data-table-th--shadow-after':
|
||||
leftActiveFixedColKey === key,
|
||||
'n-data-table-th--shadow-before':
|
||||
rightActiveFixedColKey === key,
|
||||
'n-data-table-th--selection':
|
||||
column.type === 'selection'
|
||||
},
|
||||
column.className
|
||||
]}
|
||||
onClick={(e) => {
|
||||
column.type !== 'selection' &&
|
||||
handleColHeaderClick(e, column)
|
||||
}}
|
||||
>
|
||||
{column.type === 'selection' ? (
|
||||
<NCheckbox
|
||||
key={currentPage}
|
||||
tableHeader
|
||||
checked={allRowsChecked}
|
||||
indeterminate={someRowsChecked}
|
||||
onUpdateChecked={() =>
|
||||
handleCheckboxUpdateChecked(column)
|
||||
}
|
||||
/>
|
||||
) : column.ellipsis ? (
|
||||
<div class="n-data-table-th__ellipsis">
|
||||
{typeof column.title === 'function'
|
||||
? column.title(column, index)
|
||||
: column.title}
|
||||
</div>
|
||||
) : typeof column.title === 'function' ? (
|
||||
column.title(column, index)
|
||||
) : (
|
||||
column.title
|
||||
)}
|
||||
{isColumnSortable(column) ? (
|
||||
<SortButton column={column as TableColumnInfo} />
|
||||
) : null}
|
||||
{isColumnFilterable(column) ? (
|
||||
<FilterButton
|
||||
column={column as TableColumnInfo}
|
||||
options={column.filterOptions}
|
||||
/>
|
||||
) : null}
|
||||
</th>
|
||||
)
|
||||
})}
|
||||
</tr>
|
||||
{rows.map((row) => {
|
||||
return (
|
||||
<tr class="n-data-table-tr">
|
||||
{row.map(({ column, colSpan, rowSpan, isLast }) => {
|
||||
const key = getColKey(column)
|
||||
return (
|
||||
<th
|
||||
key={key}
|
||||
style={{
|
||||
textAlign: column.align,
|
||||
left: pxfy(fixedColumnLeftMap[key]),
|
||||
right: pxfy(fixedColumnRightMap[key])
|
||||
}}
|
||||
colspan={colSpan}
|
||||
rowspan={rowSpan}
|
||||
class={[
|
||||
'n-data-table-th',
|
||||
column.fixed &&
|
||||
`n-data-table-th--fixed-${column.fixed}`,
|
||||
{
|
||||
'n-data-table-th--filterable': isColumnFilterable(
|
||||
column
|
||||
),
|
||||
'n-data-table-th--sortable': isColumnSortable(
|
||||
column
|
||||
),
|
||||
'n-data-table-th--shadow-after':
|
||||
leftActiveFixedColKey === key,
|
||||
'n-data-table-th--shadow-before':
|
||||
rightActiveFixedColKey === key,
|
||||
'n-data-table-th--selection':
|
||||
column.type === 'selection',
|
||||
'n-data-table-th--last': isLast
|
||||
},
|
||||
column.className
|
||||
]}
|
||||
onClick={(e) => {
|
||||
column.type !== 'selection' &&
|
||||
handleColHeaderClick(e, column)
|
||||
}}
|
||||
>
|
||||
{column.type === 'selection' ? (
|
||||
<NCheckbox
|
||||
key={currentPage}
|
||||
tableHeader
|
||||
checked={allRowsChecked}
|
||||
indeterminate={someRowsChecked}
|
||||
onUpdateChecked={() =>
|
||||
handleCheckboxUpdateChecked(column)
|
||||
}
|
||||
/>
|
||||
) : column.ellipsis ? (
|
||||
<div class="n-data-table-th__ellipsis">
|
||||
{typeof column.title === 'function'
|
||||
? column.title(column)
|
||||
: column.title}
|
||||
</div>
|
||||
) : typeof column.title === 'function' ? (
|
||||
column.title(column)
|
||||
) : (
|
||||
column.title
|
||||
)}
|
||||
{isColumnSortable(column) ? (
|
||||
<SortButton column={column as TableColumnInfo} />
|
||||
) : null}
|
||||
{isColumnFilterable(column) ? (
|
||||
<FilterButton
|
||||
column={column as TableColumnInfo}
|
||||
options={column.filterOptions}
|
||||
/>
|
||||
) : null}
|
||||
</th>
|
||||
)
|
||||
})}
|
||||
</tr>
|
||||
)
|
||||
})}
|
||||
</thead>
|
||||
</table>
|
||||
</div>
|
||||
|
@ -3,6 +3,7 @@ import { CSSProperties, VNodeChild } from 'vue'
|
||||
import { NLocale } from '../../locales'
|
||||
import { MergedTheme } from '../../_mixins'
|
||||
import { DataTableTheme } from '../styles'
|
||||
import { RowItem, ColItem } from './use-group-header'
|
||||
|
||||
export type FilterOptionValue = string | number
|
||||
export type ColumnKey = string | number
|
||||
@ -42,8 +43,22 @@ export interface CommonColInfo {
|
||||
ellipsis?: boolean
|
||||
}
|
||||
|
||||
export type TableColumnTitle =
|
||||
| string
|
||||
| ((column: TableColumnInfo) => VNodeChild)
|
||||
|
||||
export type TableColumnGroup = {
|
||||
title?: TableColumnTitle
|
||||
type?: never
|
||||
key: ColumnKey
|
||||
children: TableColumnInfo[]
|
||||
|
||||
// to suppress type error in table header
|
||||
filterOptions?: never
|
||||
} & CommonColInfo
|
||||
|
||||
export type TableColumnInfo = {
|
||||
title?: string | ((column: TableColumnInfo, index: number) => VNodeChild)
|
||||
title?: TableColumnTitle
|
||||
// for compat maybe default
|
||||
type?: never
|
||||
key: ColumnKey
|
||||
@ -80,14 +95,18 @@ export type SelectionColInfo = {
|
||||
filterOptionValue?: never
|
||||
} & CommonColInfo
|
||||
|
||||
export type TableColumn = TableColumnGroup | TableColumnInfo | SelectionColInfo
|
||||
export type TableColumns = TableColumn[]
|
||||
|
||||
export interface DataTableInjection {
|
||||
mergedTheme: MergedTheme<DataTableTheme>
|
||||
scrollX?: string | number
|
||||
columns: Array<TableColumnInfo | SelectionColInfo>
|
||||
rows: RowItem[][]
|
||||
cols: ColItem[]
|
||||
treeMate: TreeMate<TableNode>
|
||||
paginatedData: TmNode[]
|
||||
leftFixedColumns: Array<TableColumnInfo | SelectionColInfo>
|
||||
rightFixedColumns: Array<TableColumnInfo | SelectionColInfo>
|
||||
leftFixedColumns: TableColumns
|
||||
rightFixedColumns: TableColumns
|
||||
leftActiveFixedColKey: ColumnKey | null
|
||||
rightActiveFixedColKey: ColumnKey | null
|
||||
fixedColumnLeftMap: Record<ColumnKey, number | undefined>
|
||||
|
@ -91,7 +91,7 @@ export default c([
|
||||
cB('data-table-th', {
|
||||
borderRight: '1px solid var(--border-color)'
|
||||
}, [
|
||||
c('&:last-child', {
|
||||
cM('last', {
|
||||
borderRight: '0 solid var(--border-color)'
|
||||
})
|
||||
]),
|
||||
@ -191,6 +191,7 @@ export default c([
|
||||
box-sizing: border-box;
|
||||
background-color: var(--th-color);
|
||||
border-color: var(--border-color);
|
||||
border-bottom: 1px solid var(--border-color);
|
||||
color: var(--th-text-color);
|
||||
transition:
|
||||
border-color .3s var(--bezier),
|
||||
@ -260,10 +261,6 @@ export default c([
|
||||
width: 0,
|
||||
height: 0
|
||||
}),
|
||||
cB('data-table-table', {
|
||||
borderBottom: '1px solid var(--border-color)',
|
||||
transition: 'border-color .3s var(--bezier)'
|
||||
}),
|
||||
cB('data-table-th', [
|
||||
cB('data-table-sorter', `
|
||||
height: 14px;
|
||||
|
110
src/data-table/src/use-group-header.ts
Normal file
110
src/data-table/src/use-group-header.ts
Normal file
@ -0,0 +1,110 @@
|
||||
import { CSSProperties, ComputedRef, computed } from 'vue'
|
||||
import { DataTableProps } from './DataTable'
|
||||
import type {
|
||||
SelectionColInfo,
|
||||
TableColumn,
|
||||
TableColumnInfo,
|
||||
TableColumns
|
||||
} from './interface'
|
||||
import { getColKey, createCustomWidthStyle } from './utils'
|
||||
|
||||
export interface RowItem {
|
||||
colSpan: number
|
||||
rowSpan: number
|
||||
column: TableColumn
|
||||
isLast: boolean
|
||||
}
|
||||
export interface ColItem {
|
||||
key: string | number
|
||||
style: CSSProperties
|
||||
column: SelectionColInfo | TableColumnInfo
|
||||
}
|
||||
|
||||
type RowItemMap = WeakMap<TableColumn, RowItem>
|
||||
function getRowsAndCols (
|
||||
columns: TableColumns
|
||||
): {
|
||||
rows: RowItem[][]
|
||||
cols: ColItem[]
|
||||
dataRelatedCols: Array<SelectionColInfo | TableColumnInfo>
|
||||
} {
|
||||
const rows: RowItem[][] = []
|
||||
const cols: ColItem[] = []
|
||||
const dataRelatedCols: Array<SelectionColInfo | TableColumnInfo> = []
|
||||
const rowItemMap: RowItemMap = new WeakMap()
|
||||
let maxDepth = -1
|
||||
function ensureMaxDepth (columns: TableColumns, currentDepth: number): void {
|
||||
if (currentDepth > maxDepth) {
|
||||
rows[currentDepth] = []
|
||||
maxDepth = currentDepth
|
||||
}
|
||||
for (const column of columns) {
|
||||
if ('children' in column) {
|
||||
ensureMaxDepth(column.children, currentDepth + 1)
|
||||
} else {
|
||||
cols.push({
|
||||
key: getColKey(column),
|
||||
style: createCustomWidthStyle(column),
|
||||
column
|
||||
})
|
||||
dataRelatedCols.push(column)
|
||||
}
|
||||
}
|
||||
}
|
||||
ensureMaxDepth(columns, 0)
|
||||
function ensureColLayout (
|
||||
columns: TableColumns,
|
||||
currentDepth: number,
|
||||
parentIsLast: boolean
|
||||
): void {
|
||||
const lastIndex = columns.length - 1
|
||||
columns.forEach((column, index) => {
|
||||
const isLast = parentIsLast && index === lastIndex
|
||||
if ('children' in column) {
|
||||
const rowItem: RowItem = {
|
||||
column,
|
||||
colSpan: 0,
|
||||
rowSpan: 1,
|
||||
isLast
|
||||
}
|
||||
ensureColLayout(column.children, currentDepth + 1, isLast)
|
||||
column.children.forEach((childColumn) => {
|
||||
rowItem.colSpan += rowItemMap.get(childColumn)?.colSpan ?? 0
|
||||
})
|
||||
rowItemMap.set(column, rowItem)
|
||||
rows[currentDepth].push(rowItem)
|
||||
} else {
|
||||
const rowItem: RowItem = {
|
||||
column,
|
||||
colSpan: 1,
|
||||
rowSpan: maxDepth - currentDepth + 1,
|
||||
isLast
|
||||
}
|
||||
rowItemMap.set(column, rowItem)
|
||||
rows[currentDepth].push(rowItem)
|
||||
}
|
||||
})
|
||||
}
|
||||
ensureColLayout(columns, 0, true)
|
||||
|
||||
return {
|
||||
rows,
|
||||
cols,
|
||||
dataRelatedCols
|
||||
}
|
||||
}
|
||||
|
||||
export function useGroupHeader (
|
||||
props: DataTableProps
|
||||
): {
|
||||
rows: ComputedRef<RowItem[][]>
|
||||
cols: ComputedRef<ColItem[]>
|
||||
dataRelatedCols: ComputedRef<Array<SelectionColInfo | TableColumnInfo>>
|
||||
} {
|
||||
const rowsAndCols = computed(() => getRowsAndCols(props.columns))
|
||||
return {
|
||||
rows: computed(() => rowsAndCols.value.rows),
|
||||
cols: computed(() => rowsAndCols.value.cols),
|
||||
dataRelatedCols: computed(() => rowsAndCols.value.dataRelatedCols)
|
||||
}
|
||||
}
|
@ -1,4 +1,4 @@
|
||||
import { computed, ref } from 'vue'
|
||||
import { computed, ref, ComputedRef } from 'vue'
|
||||
import { useMergedState } from 'vooks'
|
||||
import { createTreeMate } from 'treemate'
|
||||
import type { DataTableProps } from './DataTable'
|
||||
@ -10,6 +10,7 @@ import type {
|
||||
SortOrder,
|
||||
SortState,
|
||||
TableColumnInfo,
|
||||
SelectionColInfo,
|
||||
TableNode,
|
||||
TmNode
|
||||
} from './interface'
|
||||
@ -20,7 +21,14 @@ import { call, warn } from '../../_utils'
|
||||
// useTableData combines filter, sorter and pagination
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/explicit-function-return-type
|
||||
export function useTableData (props: DataTableProps) {
|
||||
export function useTableData (
|
||||
props: DataTableProps,
|
||||
{
|
||||
dataRelatedCols
|
||||
}: {
|
||||
dataRelatedCols: ComputedRef<Array<SelectionColInfo | TableColumnInfo>>
|
||||
}
|
||||
) {
|
||||
const treeMateRef = computed(() =>
|
||||
createTreeMate<TableNode>(props.data, {
|
||||
getKey: props.rowKey
|
||||
@ -32,7 +40,7 @@ export function useTableData (props: DataTableProps) {
|
||||
const uncontrolledCurrentPageRef = ref(1)
|
||||
const uncontrolledPageSizeRef = ref(10)
|
||||
|
||||
props.columns.forEach((column) => {
|
||||
dataRelatedCols.value.forEach((column) => {
|
||||
if (column.sorter !== undefined) {
|
||||
uncontrolledSortStateRef.value = {
|
||||
columnKey: column.key,
|
||||
@ -90,7 +98,7 @@ export function useTableData (props: DataTableProps) {
|
||||
const mergedSortStateRef = computed<SortState | null>(() => {
|
||||
// If one of the columns's sort order is false or 'ascend' or 'descend',
|
||||
// the table's controll functionality should work in controlled manner.
|
||||
const columnsWithControlledSortOrder = props.columns.filter(
|
||||
const columnsWithControlledSortOrder = dataRelatedCols.value.filter(
|
||||
(column) =>
|
||||
column.type !== 'selection' &&
|
||||
column.sorter !== undefined &&
|
||||
@ -120,12 +128,14 @@ export function useTableData (props: DataTableProps) {
|
||||
})
|
||||
|
||||
const mergedFilterStateRef = computed<FilterState>(() => {
|
||||
const columnsWithControlledFilter = props.columns.filter((column) => {
|
||||
return (
|
||||
column.filterOptionValues !== undefined ||
|
||||
column.filterOptionValue !== undefined
|
||||
)
|
||||
})
|
||||
const columnsWithControlledFilter = dataRelatedCols.value.filter(
|
||||
(column) => {
|
||||
return (
|
||||
column.filterOptionValues !== undefined ||
|
||||
column.filterOptionValue !== undefined
|
||||
)
|
||||
}
|
||||
)
|
||||
const controlledFilterState: FilterState = {}
|
||||
columnsWithControlledFilter.forEach((column) => {
|
||||
if (column.type === 'selection') return
|
||||
@ -315,7 +325,7 @@ export function useTableData (props: DataTableProps) {
|
||||
if (!columnKey) {
|
||||
clearSorter()
|
||||
} else {
|
||||
const columnToSort = props.columns.find(
|
||||
const columnToSort = dataRelatedCols.value.find(
|
||||
(column) => column.type !== 'selection' && column.key === columnKey
|
||||
)
|
||||
if (!columnToSort || !columnToSort.sorter) return
|
||||
|
@ -7,7 +7,9 @@ import type {
|
||||
SortOrderFlag,
|
||||
SortState,
|
||||
CreateRowClassName,
|
||||
SelectionColInfo
|
||||
SelectionColInfo,
|
||||
TableColumnGroup,
|
||||
TableColumn
|
||||
} from './interface'
|
||||
|
||||
export const selectionColWidth = 40
|
||||
@ -20,7 +22,7 @@ export function getColWidth (
|
||||
}
|
||||
|
||||
export function getColKey (
|
||||
col: TableColumnInfo | SelectionColInfo
|
||||
col: TableColumnInfo | SelectionColInfo | TableColumnGroup
|
||||
): string | number {
|
||||
if (col.type === 'selection') return '__n_selection__'
|
||||
return col.key
|
||||
@ -41,8 +43,7 @@ export function getFlagOfOrder (order: SortOrder): SortOrderFlag {
|
||||
}
|
||||
|
||||
export function createCustomWidthStyle (
|
||||
column: TableColumnInfo | SelectionColInfo,
|
||||
index: number
|
||||
column: TableColumnInfo | SelectionColInfo
|
||||
): CSSProperties {
|
||||
return {
|
||||
width: pxfy(getColWidth(column))
|
||||
@ -69,15 +70,13 @@ export function shouldUseArrayInSingleMode (column: TableColumnInfo): boolean {
|
||||
)
|
||||
}
|
||||
|
||||
export function isColumnSortable (
|
||||
column: TableColumnInfo | SelectionColInfo
|
||||
): boolean {
|
||||
export function isColumnSortable (column: TableColumn): boolean {
|
||||
if ('children' in column) return false
|
||||
return !!column.sorter
|
||||
}
|
||||
|
||||
export function isColumnFilterable (
|
||||
column: TableColumnInfo | SelectionColInfo
|
||||
): boolean {
|
||||
export function isColumnFilterable (column: TableColumn): boolean {
|
||||
if ('children' in column) return false
|
||||
return (
|
||||
!!column.filter && (!!column.filterOptions || !!column.renderFilterMenu)
|
||||
)
|
||||
|
Loading…
Reference in New Issue
Block a user