feat(data-table): selection.options

This commit is contained in:
07akioni 2021-04-20 22:37:06 +08:00
parent 8d8b4c764f
commit 3581c3570f
10 changed files with 190 additions and 35 deletions

View File

@ -19,9 +19,12 @@ Rows can be selectable by making first column's type as `selection`.
```
```js
import { DataTableCheckOption } from 'naive-ui'
const columns = [
{
type: 'selection',
options: [DataTableCheckOption.CHECK_ALL, DataTableCheckOption.UNCHECK_ALL],
disabled (row, index) {
return row.name === 'Edward King 3'
}

View File

@ -16,9 +16,12 @@
```
```js
import { DataTableCheckOption } from 'naive-ui'
const columns = [
{
type: 'selection',
options: [DataTableCheckOption.CHECK_ALL, DataTableCheckOption.UNCHECK_ALL],
disabled (row, index) {
return row.name === 'Edward King 3'
}

View File

@ -1,4 +1,5 @@
export { default as NDataTable } from './src/DataTable'
export { DataTableCheckOption } from './src/TableParts/CheckMenu'
export type { DataTableProps } from './src/DataTable'
export type {
RenderFilter as DataTableRenderFilter,
@ -6,7 +7,7 @@ export type {
ColumnKey as DataTableColumnKey,
TableColumn as DataTableColumn,
TableBaseColumn as DataTableBaseColumn,
TableSelectionColumn as DataTableSelectionColumn,
TableSelectionColumn as DataTableCheckOptionColumn,
TableExpandColumn as DataTableExpandColumn,
DataTableInst
} from './src/interface'

View File

@ -225,6 +225,7 @@ export default defineComponent({
treeMate: treeMateRef,
mergedCurrentPage: mergedCurrentPageRef,
paginatedData: paginatedDataRef,
selectionColumn: selectionColumnRef,
hoverKey,
currentPage,
mergedPagination,
@ -247,8 +248,9 @@ export default defineComponent({
allRowsChecked,
mergedCheckedRowKeys
} = useCheck(props, {
paginatedDataRef,
treeMateRef
selectionColumnRef,
treeMateRef,
paginatedDataRef
})
const {
mergedExpandedRowKeys,
@ -299,6 +301,10 @@ export default defineComponent({
mergedExpandedRowKeys,
locale,
rowKey: toRef(props, 'rowKey'),
checkOptions: computed(() => {
const { value: selectionColumn } = selectionColumnRef
return selectionColumn?.options
}),
filterMenuCssVars: computed(() => {
const {
self: { actionDividerColor, actionPadding, actionButtonMargin }

View File

@ -0,0 +1,99 @@
import { h, defineComponent, inject, computed } from 'vue'
import { NDropdown } from '../../../dropdown'
import { NBaseIcon } from '../../../_internal'
import { ChevronDownIcon } from '../../../_internal/icons'
import { dataTableInjectionKey } from '../interface'
export enum DataTableCheckOption {
CHECK_ALL,
UNCHECK_ALL
}
function createSelectHandler (
options:
| Array<
| DataTableCheckOption
| { label: string, key: string | number, onSelect: () => void }
>
| undefined,
doCheckAll: (checkWholeTable?: boolean) => void,
doUncheckAll: (checkWholeTable?: boolean) => void
): (key: string | number) => void {
if (!options) return () => {}
return (key: string | number) => {
for (const option of options) {
switch (key) {
case DataTableCheckOption.CHECK_ALL:
doCheckAll(true)
return
case DataTableCheckOption.UNCHECK_ALL:
doUncheckAll(true)
return
default:
if (typeof option === 'object' && option.key === key) {
option.onSelect()
return
}
}
}
}
}
function createDropdownOptions (
options:
| Array<
| DataTableCheckOption
| { label: string, key: string | number, onSelect: () => void }
>
| undefined
): Array<{ label: string, key: string | number }> {
if (!options) return []
return options.map((option) => {
switch (option) {
case DataTableCheckOption.CHECK_ALL:
return {
label: '选择表格全部数据',
key: DataTableCheckOption.CHECK_ALL
}
case DataTableCheckOption.UNCHECK_ALL:
return {
label: '选择表格全部数据取消',
key: DataTableCheckOption.UNCHECK_ALL
}
default:
return option
}
})
}
export default defineComponent({
name: 'DataTableCheckMenu',
setup () {
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
const NDataTable = inject(dataTableInjectionKey)!
const { doCheckAll, doUncheckAll } = NDataTable
return {
handleSelect: createSelectHandler(
NDataTable.checkOptions,
doCheckAll,
doUncheckAll
),
options: computed(() => createDropdownOptions(NDataTable.checkOptions))
}
},
render () {
return (
<NDropdown options={this.options} onSelect={this.handleSelect}>
{{
default: () => (
<NBaseIcon clsPrefix="n" class="n-data-table-check-extra">
{{
default: () => <ChevronDownIcon />
}}
</NBaseIcon>
)
}}
</NDropdown>
)
}
})

View File

@ -1,4 +1,4 @@
import { h, defineComponent, inject, PropType, VNodeChild } from 'vue'
import { h, defineComponent, inject, PropType, VNodeChild, Fragment } from 'vue'
import { happensIn, pxfy } from 'seemly'
import { formatLength } from '../../../_utils'
import { NCheckbox } from '../../../checkbox'
@ -13,11 +13,11 @@ import {
} from '../utils'
import {
TableExpandColumn,
TableSelectionColumn,
TableColumnGroup,
TableBaseColumn,
dataTableInjectionKey
} from '../interface'
import CheckMenu from './CheckMenu'
function renderTitle (
column: TableExpandColumn | TableBaseColumn | TableColumnGroup
@ -35,11 +35,11 @@ export default defineComponent({
setup () {
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
const NDataTable = inject(dataTableInjectionKey)!
function handleCheckboxUpdateChecked (column: TableSelectionColumn): void {
function handleCheckboxUpdateChecked (): void {
if (NDataTable.someRowsChecked || NDataTable.allRowsChecked) {
NDataTable.doUncheckAll(column)
NDataTable.doUncheckAll()
} else {
NDataTable.doCheckAll(column)
NDataTable.doCheckAll()
}
}
function handleColHeaderClick (
@ -75,7 +75,8 @@ export default defineComponent({
rightActiveFixedColKey,
rows,
cols,
mergedTheme
mergedTheme,
checkOptions
},
headerStyle,
handleColHeaderClick,
@ -145,15 +146,16 @@ export default defineComponent({
}
>
{column.type === 'selection' ? (
<NCheckbox
key={currentPage}
tableHeader
checked={allRowsChecked}
indeterminate={someRowsChecked}
onUpdateChecked={() =>
handleCheckboxUpdateChecked(column)
}
/>
<>
<NCheckbox
key={currentPage}
tableHeader
checked={allRowsChecked}
indeterminate={someRowsChecked}
onUpdateChecked={handleCheckboxUpdateChecked}
/>
{checkOptions ? <CheckMenu /> : null}
</>
) : column.ellipsis === true ||
(column.ellipsis && !column.ellipsis.tooltip) ? (
<div

View File

@ -5,6 +5,7 @@ import { NLocale } from '../../locales'
import { MergedTheme } from '../../_mixins'
import { DataTableTheme } from '../styles'
import { RowItem, ColItem } from './use-group-header'
import { DataTableCheckOption } from './TableParts/CheckMenu'
export type FilterOptionValue = string | number
export type ColumnKey = string | number
@ -100,6 +101,7 @@ export type TableBaseColumn = {
export type TableSelectionColumn = {
type: 'selection'
disabled?: (row: RowData) => boolean
options?: DataTableCheckOptions
// to suppress type error in utils
sorter?: never
@ -127,11 +129,16 @@ export type TableColumn =
| TableExpandColumn
export type TableColumns = TableColumn[]
export type DataTableCheckOptions = Array<
| DataTableCheckOption
| { label: string, key: string | number, onSelect: () => void }
>
export interface DataTableInjection {
checkOptions: DataTableCheckOptions | undefined
hoverKey: RowKey | null
mergedClsPrefix: string
mergedTheme: MergedTheme<DataTableTheme>
scrollX?: string | number
scrollX: string | number | undefined
rows: RowItem[][]
cols: ColItem[]
treeMate: TreeMate<RowData>
@ -148,7 +155,7 @@ export interface DataTableInjection {
mergedSortState: SortState | null
mergedFilterState: FilterState
loading: boolean
rowClassName?: string | CreateRowClassName
rowClassName: string | CreateRowClassName | undefined
mergedCheckedRowKeys: RowKey[]
locale: NLocale['DataTable']
filterMenuCssVars: CSSProperties
@ -162,8 +169,8 @@ export interface DataTableInjection {
) => void
doUpdateSorter: (sorter: SortState | null) => void
doUpdateCheckedRowKeys: (keys: RowKey[]) => void
doUncheckAll: (column: TableSelectionColumn) => void
doCheckAll: (column: TableSelectionColumn) => void
doUncheckAll: (checkWholeTable?: boolean) => void
doCheckAll: (checkWholeTable?: boolean) => void
handleTableHeaderScroll: (e: Event) => void
handleTableBodyScroll: (e: Event) => void
deriveActiveRightFixedColumn: (

View File

@ -214,10 +214,12 @@ export default c([
cM('filterable', {
paddingRight: '36px'
}),
fixedColumnStyle,
cM('selection', `
padding: 0;
text-align: center;
line-height: 0;
z-index: 3;
`),
cE('ellipsis', `
display: inline-block;
@ -236,8 +238,7 @@ export default c([
c('&:hover', {
backgroundColor: 'var(--merged-th-color-hover)'
})
]),
fixedColumnStyle
])
]),
cB('data-table-td', `
text-align: start;
@ -329,7 +330,17 @@ export default c([
`)
])
])
])
]),
cB('data-table-check-extra', `
transition: color .3s var(--bezier);
color: var(--th-icon-color);
position: absolute;
font-size: 14px;
right: -4px;
top: 50%;
transform: translateY(-50%);
z-index: 1;
`)
]),
cB('data-table-filter-menu', [
cB('scrollbar', {
@ -387,11 +398,11 @@ export default c([
function createFixedColumnStyle (): CNode[] {
return [
cM('fixed-left', {
left: 0,
position: 'sticky',
zIndex: 2
}, [
cM('fixed-left', `
left: 0;
position: sticky;
z-index: 2;
`, [
c('&::after', `
pointer-events: none;
content: "";

View File

@ -9,11 +9,12 @@ import { TreeMate } from 'treemate'
export function useCheck (
props: DataTableSetupProps,
data: {
selectionColumnRef: ComputedRef<TableSelectionColumn | null>
paginatedDataRef: ComputedRef<TmNode[]>
treeMateRef: ComputedRef<TreeMate<RowData>>
}
) {
const { paginatedDataRef, treeMateRef } = data
const { paginatedDataRef, treeMateRef, selectionColumnRef } = data
const uncontrolledCheckedRowKeysRef = ref(props.defaultCheckedRowKeys)
const controlledCheckedRowKeysRef = toRef(props, 'checkedRowKeys')
const mergedCheckedRowKeysRef = useMergedState(
@ -47,9 +48,14 @@ export function useCheck (
if (onCheckedRowKeysChange) call(onCheckedRowKeysChange, keys)
uncontrolledCheckedRowKeysRef.value = keys
}
function doCheckAll (column: TableSelectionColumn): void {
function doCheckAll (checkWholeTable: boolean = false): void {
const { value: column } = selectionColumnRef
if (!column) return
const rowKeysToCheck: RowKey[] = []
paginatedDataRef.value.forEach((tmNode) => {
;(checkWholeTable
? treeMateRef.value.treeNodes
: paginatedDataRef.value
).forEach((tmNode) => {
if (column.disabled?.(tmNode.rawNode)) {
return
}
@ -62,9 +68,14 @@ export function useCheck (
.checkedKeys
)
}
function doUncheckAll (column: TableSelectionColumn): void {
function doUncheckAll (checkWholeTable: boolean = false): void {
const { value: column } = selectionColumnRef
if (!column) return
const rowKeysToUncheck: RowKey[] = []
paginatedDataRef.value.forEach((tmNode) => {
;(checkWholeTable
? treeMateRef.value.treeNodes
: paginatedDataRef.value
).forEach((tmNode) => {
const { rawNode: row } = tmNode
if (column.disabled?.(row)) {
return

View File

@ -301,6 +301,17 @@ export function useTableData (
}
})
const selectionColumnRef = computed<TableSelectionColumn | null>(() => {
return (
(props.columns.find((col) => {
if (col.type === 'selection') {
return true
}
return false
}) as TableSelectionColumn | undefined) || null
)
})
function doUpdatePage (page: number): void {
const { 'onUpdate:page': onUpdatePage, onPageChange } = props
if (onUpdatePage) call(onUpdatePage, page)
@ -380,6 +391,7 @@ export function useTableData (
mergedFilterState: mergedFilterStateRef,
mergedSortState: mergedSortStateRef,
hoverKey: ref<RowKey | null>(null),
selectionColumn: selectionColumnRef,
doUpdateFilters,
doUpdateSorter,
doUpdatePageSize,