-
Notifications
You must be signed in to change notification settings - Fork 66
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
* feat(table): 新增 Table 组件 新增 Table 组件 * fix: fix(table): 修复comment问题 * fix: 修复comment问题 * docs(table): 更新Table md文档 * chore: update common --------- Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
- Loading branch information
1 parent
aa55b00
commit 7216ea9
Showing
19 changed files
with
999 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Submodule _common
updated
4 files
+41 −0 | docs/mobile/api/tab-bar.en-US.md | |
+22 −10 | docs/mobile/api/tab-bar.md | |
+1 −1 | style/mobile/components/_index.less | |
+2 −0 | style/web/components/empty/_index.less |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,210 @@ | ||
import React, { forwardRef, useRef } from 'react'; | ||
import isFunction from 'lodash/isFunction'; | ||
import get from 'lodash/get'; | ||
import cx from 'classnames'; | ||
|
||
import { StyledProps } from '../common'; | ||
import useClassName from './hooks/useClassName'; | ||
import useStyle, { formatCSSUnit } from './hooks/useStyle'; | ||
import useDefaultProps from '../hooks/useDefaultProps'; | ||
import defaultConfig from '../_common/js/global-config/mobile/locale/zh_CN'; | ||
import Loading from '../loading'; | ||
import { baseTableDefaultProps } from './defaultProps'; | ||
|
||
import type { TdBaseTableProps, BaseTableCol, TableRowData, BaseTableCellParams } from './type'; | ||
|
||
export type BaseTableProps = TdBaseTableProps & StyledProps; | ||
|
||
export const BaseTable = forwardRef((props: BaseTableProps, ref: React.Ref<HTMLTableElement>) => { | ||
const { | ||
data, | ||
empty, | ||
height, | ||
loading, | ||
loadingProps, | ||
columns, | ||
bordered, | ||
maxHeight, | ||
tableLayout, | ||
showHeader, | ||
cellEmptyContent, | ||
className, | ||
style, | ||
onRowClick, | ||
onCellClick, | ||
onScroll, | ||
} = useDefaultProps<BaseTableProps>(props, baseTableDefaultProps); | ||
|
||
const { tableLayoutClasses, tableHeaderClasses, tableBaseClass, tdAlignClasses, tdEllipsisClass, classPrefix } = | ||
useClassName(); | ||
|
||
const { tableClasses, tableContentStyles, tableElementStyles } = useStyle(props); | ||
|
||
const tableElmClasses = tableLayoutClasses[tableLayout || 'fixed']; | ||
|
||
const theadClasses = cx(tableHeaderClasses.header, { | ||
[tableHeaderClasses.fixed]: Boolean(maxHeight || height), | ||
[tableBaseClass.bordered]: bordered, | ||
}); | ||
|
||
const ellipsisClasses = cx([`${classPrefix}-table__ellipsis`, `${classPrefix}-text-ellipsis`]); | ||
|
||
const defaultColWidth = tableLayout === 'fixed' ? '80px' : undefined; | ||
|
||
const tableContentRef = useRef(); | ||
|
||
const tableElmRef = useRef(); | ||
|
||
const theadRef = useRef(); | ||
|
||
const colStyle = (colItem: BaseTableCol<TableRowData>) => ({ | ||
width: `${formatCSSUnit(colItem.width || defaultColWidth)}`, | ||
minWidth: `${ | ||
!formatCSSUnit(colItem.width || defaultColWidth) && !colItem.minWidth && tableLayout === 'fixed' | ||
? '80px' | ||
: formatCSSUnit(colItem.minWidth) | ||
}`, | ||
}); | ||
|
||
const thClassName = (thItem: BaseTableCol<TableRowData>) => { | ||
let className = ''; | ||
if (thItem.colKey) { | ||
className = `${classPrefix}-table__th-${thItem.colKey}`; | ||
} | ||
if (thItem.ellipsisTitle || thItem.ellipsis) { | ||
className = `${className} ${tdEllipsisClass}`; | ||
} | ||
if (thItem.align && thItem.align !== 'left') { | ||
className = `${className} ${tdAlignClasses[`${thItem.align}`]}`; | ||
} | ||
return className; | ||
}; | ||
|
||
const tdClassName = (tdItem: BaseTableCol<TableRowData>) => { | ||
let className = ''; | ||
if (tdItem.ellipsis) { | ||
className = tdEllipsisClass; | ||
} | ||
if (tdItem.align && tdItem.align !== 'left') { | ||
className = `${className} ${tdAlignClasses[`${tdItem.align}`]}`; | ||
} | ||
return className; | ||
}; | ||
|
||
const renderCell = ( | ||
params: BaseTableCellParams<TableRowData>, | ||
cellEmptyContent?: TdBaseTableProps['cellEmptyContent'], | ||
) => { | ||
const { col, row, rowIndex } = params; | ||
// support serial number column | ||
if (col.colKey === 'serial-number') { | ||
return rowIndex + 1; | ||
} | ||
|
||
if (isFunction(col.cell)) { | ||
return col.cell(params); | ||
} | ||
|
||
const r = get(row, col.colKey); | ||
// 0 和 false 属于正常可用值,不能使用兜底逻辑 cellEmptyContent | ||
if (![undefined, '', null].includes(r)) return r; | ||
|
||
// cellEmptyContent 作为空数据兜底显示,用户可自定义 | ||
if (cellEmptyContent) { | ||
return isFunction(cellEmptyContent) ? cellEmptyContent(params) : cellEmptyContent; | ||
} | ||
return r; | ||
}; | ||
|
||
const renderTitle = (thItem: BaseTableCol<TableRowData>, index: number) => { | ||
if (isFunction(thItem?.title)) { | ||
return thItem?.title({ col: thItem, colIndex: index }); | ||
} | ||
return thItem?.title; | ||
}; | ||
|
||
const handleRowClick = (row: TableRowData, rowIndex: number, e: React.MouseEvent) => { | ||
onRowClick?.({ row, index: rowIndex, e }); | ||
}; | ||
|
||
const handleCellClick = (row: TableRowData, col: any, rowIndex: number, colIndex: number, e: React.MouseEvent) => { | ||
if (col.stopPropagation) { | ||
e.stopPropagation(); | ||
} | ||
onCellClick?.({ row, col, rowIndex, colIndex, e }); | ||
}; | ||
|
||
const renderTableBody = () => { | ||
const renderContentEmpty = empty || defaultConfig?.table?.empty; | ||
|
||
if (!data?.length && renderContentEmpty) { | ||
return ( | ||
<tr className={tableBaseClass.emptyRow}> | ||
<td colSpan={columns?.length}> | ||
<div className={tableBaseClass.empty}>{renderContentEmpty}</div> | ||
</td> | ||
</tr> | ||
); | ||
} | ||
if (data?.length) { | ||
return data?.map((trItem, trIdx) => ( | ||
<tr | ||
key={trIdx} | ||
onClick={(ev) => { | ||
handleRowClick(trItem, trIdx, ev); | ||
}} | ||
> | ||
{columns?.map((tdItem, tdIdx) => ( | ||
<td | ||
key={tdIdx} | ||
className={tdClassName(tdItem)} | ||
onClick={($event) => { | ||
handleCellClick(trItem, tdItem, trIdx, tdIdx, $event); | ||
}} | ||
> | ||
<div className={tdItem.ellipsis && ellipsisClasses}> | ||
{renderCell({ row: trItem, col: tdItem, rowIndex: trIdx, colIndex: tdIdx }, cellEmptyContent)} | ||
</div> | ||
</td> | ||
))} | ||
</tr> | ||
)); | ||
} | ||
}; | ||
|
||
return ( | ||
<div ref={ref} className={cx(tableClasses, className)} style={{ position: 'relative', ...style }}> | ||
<div | ||
ref={tableContentRef} | ||
className={tableBaseClass.content} | ||
style={tableContentStyles} | ||
onScroll={(e) => { | ||
onScroll?.({ e }); | ||
}} | ||
> | ||
<table ref={tableElmRef} className={tableElmClasses} style={tableElementStyles}> | ||
<colgroup>{columns?.map((col) => <col key={col.colKey} style={colStyle(col)} />)}</colgroup> | ||
{showHeader && ( | ||
<thead ref={theadRef} className={theadClasses}> | ||
<tr> | ||
{columns?.map((thItem, idx) => ( | ||
<th key={idx} className={thClassName(thItem)}> | ||
<div className={(thItem.ellipsisTitle || thItem.ellipsis) && ellipsisClasses}> | ||
{renderTitle(thItem, idx)} | ||
</div> | ||
</th> | ||
))} | ||
</tr> | ||
</thead> | ||
)} | ||
<tbody className={tableBaseClass.body}>{renderTableBody()}</tbody> | ||
</table> | ||
{loading && ( | ||
<div className={`${classPrefix}-table__loading--full`}> | ||
<Loading {...loadingProps} /> | ||
</div> | ||
)} | ||
</div> | ||
</div> | ||
); | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,61 @@ | ||
import React from 'react'; | ||
import { Table } from 'tdesign-mobile-react'; | ||
|
||
const data = []; | ||
const total = 10; | ||
for (let i = 0; i < total; i++) { | ||
data.push({ | ||
index: i + 1, | ||
applicant: ['内容', '内容', '内容'][i % 3], | ||
status: ['内容', '内容', '内容'][i % 3], | ||
channel: ['内容', '内容', '内容'][i % 3], | ||
detail: { | ||
email: ['内容', '内容', '内容内容内容'][i % 3], | ||
}, | ||
}); | ||
} | ||
|
||
const columns = [ | ||
{ colKey: 'applicant', title: '标题', ellipsis: true, cell: 'type-slot-name' }, | ||
{ | ||
colKey: 'status', | ||
title: '标题', | ||
ellipsis: true, | ||
}, | ||
{ | ||
colKey: 'channel', | ||
title: '标题', | ||
cell: ({ col, row }) => row[col.colKey], | ||
ellipsis: true, | ||
}, | ||
{ | ||
colKey: 'detail.email', | ||
title: '标题', | ||
cell: () => '内容', | ||
ellipsis: true, | ||
}, | ||
]; | ||
|
||
export function BaseExample() { | ||
const handleRowClick = (e) => { | ||
console.log('row-cliek=====', e); | ||
}; | ||
|
||
const handleCellClick = (e) => { | ||
console.log('cell-cliek=====', e); | ||
}; | ||
|
||
return ( | ||
<div style={{ margin: '16px 16px 0' }}> | ||
<Table | ||
columns={columns} | ||
data={data} | ||
cellEmptyContent={'vvv'} | ||
rowKey="index" | ||
showHeader | ||
onCellClick={handleCellClick} | ||
onRowClick={handleRowClick} | ||
></Table> | ||
</div> | ||
); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,56 @@ | ||
import React from 'react'; | ||
import { Table } from 'tdesign-mobile-react'; | ||
|
||
const data = []; | ||
const total = 10; | ||
for (let i = 0; i < total; i++) { | ||
data.push({ | ||
index: i + 1, | ||
applicant: ['内容', '内容', '内容'][i % 3], | ||
status: ['内容', '内容', '内容'][i % 3], | ||
channel: ['内容', '内容', '内容'][i % 3], | ||
detail: { | ||
email: ['内容', '内容', '内容内容内容'][i % 3], | ||
}, | ||
}); | ||
} | ||
|
||
const columns = [ | ||
{ colKey: 'applicant', title: '标题', ellipsis: true }, | ||
{ | ||
colKey: 'status', | ||
title: '标题', | ||
ellipsis: true, | ||
}, | ||
{ colKey: 'channel', title: '标题', ellipsis: true }, | ||
{ colKey: 'detail.email', title: '标题', ellipsis: true }, | ||
]; | ||
|
||
export function BorderedExample() { | ||
const handleRowClick = (e) => { | ||
console.log('row-cliek=====', e); | ||
}; | ||
|
||
const handleCellClick = (e) => { | ||
console.log('cell-cliek=====', e); | ||
}; | ||
|
||
const handleScroll = (e) => { | ||
console.log('scroll=====', e); | ||
}; | ||
|
||
return ( | ||
<div style={{ margin: '16px 16px 0' }}> | ||
<Table | ||
columns={columns} | ||
data={data} | ||
rowKey="index" | ||
showHeader | ||
onCellClick={handleCellClick} | ||
onRowClick={handleRowClick} | ||
onScroll={handleScroll} | ||
bordered | ||
></Table> | ||
</div> | ||
); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,30 @@ | ||
import React from 'react'; | ||
import TDemoBlock from '../../../site/mobile/components/DemoBlock'; | ||
import TDemoHeader from '../../../site/mobile/components/DemoHeader'; | ||
import { BaseExample } from './base'; | ||
import { ScrollExample } from './scroll'; | ||
import { StripeExample } from './stripe'; | ||
import { BorderedExample } from './bordered'; | ||
|
||
export default function Base() { | ||
return ( | ||
<div className="tdesign-mobile-demo"> | ||
<TDemoHeader | ||
title="Table 表格" | ||
summary=" 表格常用于展示同类结构下的多种数据,易于组织、对比和分析等,并可对数据进行搜索、筛选、排序等操作。一般包括表头、数据行和表尾三部分。 " | ||
/> | ||
<TDemoBlock title="01 组件类型" summary="基础表格"> | ||
<BaseExample /> | ||
|
||
<TDemoHeader title="" summary="横向平铺可滚动表格" /> | ||
<ScrollExample /> | ||
|
||
<TDemoHeader title="" summary="带斑马纹表格样式" /> | ||
<StripeExample /> | ||
|
||
<TDemoHeader title="" summary="带边框表格样式" /> | ||
<BorderedExample /> | ||
</TDemoBlock> | ||
</div> | ||
); | ||
} |
Oops, something went wrong.