From 8472c25633e7b99c2283e80cbc76540298c0089a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=9E=9C=E5=86=BB=E6=A9=99?= Date: Thu, 16 Feb 2023 07:44:48 +0800 Subject: [PATCH] refactor:table (#6267) * refactor:table * docs:update & refactor: table type --------- Co-authored-by: tangjinzhou <415800467@qq.com> --- components/style.ts | 3 +- components/table/Table.tsx | 186 ++--- .../table/hooks/useFilter/FilterSearch.tsx | 12 +- components/table/index.en-US.md | 2 +- components/table/index.zh-CN.md | 2 +- components/table/style/bordered.less | 136 ---- components/table/style/bordered.ts | 159 ++++ components/table/style/ellipsis.ts | 37 + components/table/style/empty.ts | 22 + components/table/style/expand.ts | 152 ++++ components/table/style/filter.ts | 172 ++++ components/table/style/fixed.ts | 132 +++ components/table/style/index.less | 754 ------------------ components/table/style/index.ts | 409 ++++++++++ components/table/style/index.tsx | 16 - components/table/style/pagination.ts | 39 + components/table/style/radius.less | 45 -- components/table/style/radius.ts | 57 ++ components/table/style/resize.less | 41 - components/table/style/rtl.less | 168 ---- components/table/style/rtl.ts | 39 + components/table/style/selection.ts | 76 ++ components/table/style/size.less | 51 -- components/table/style/size.ts | 68 ++ components/table/style/sorter.ts | 101 +++ components/table/style/sticky.ts | 59 ++ components/table/style/summary.ts | 29 + components/theme/interface/components.ts | 4 +- 28 files changed, 1634 insertions(+), 1337 deletions(-) delete mode 100644 components/table/style/bordered.less create mode 100644 components/table/style/bordered.ts create mode 100644 components/table/style/ellipsis.ts create mode 100644 components/table/style/empty.ts create mode 100644 components/table/style/expand.ts create mode 100644 components/table/style/filter.ts create mode 100644 components/table/style/fixed.ts delete mode 100644 components/table/style/index.less create mode 100644 components/table/style/index.ts delete mode 100644 components/table/style/index.tsx create mode 100644 components/table/style/pagination.ts delete mode 100644 components/table/style/radius.less create mode 100644 components/table/style/radius.ts delete mode 100644 components/table/style/resize.less delete mode 100644 components/table/style/rtl.less create mode 100644 components/table/style/rtl.ts create mode 100644 components/table/style/selection.ts delete mode 100644 components/table/style/size.less create mode 100644 components/table/style/size.ts create mode 100644 components/table/style/sorter.ts create mode 100644 components/table/style/sticky.ts create mode 100644 components/table/style/summary.ts diff --git a/components/style.ts b/components/style.ts index 0f7071e77..ab6240993 100644 --- a/components/style.ts +++ b/components/style.ts @@ -37,7 +37,8 @@ import './time-picker/style'; import './calendar/style'; // import './date-picker/style'; // import './slider/style'; -import './table/style'; +// import './table/style'; + // import './progress/style'; // import './timeline/style'; // import './input-number/style'; diff --git a/components/table/Table.tsx b/components/table/Table.tsx index 498fa2796..a52e829e4 100644 --- a/components/table/Table.tsx +++ b/components/table/Table.tsx @@ -34,7 +34,7 @@ import scrollTo from '../_util/scrollTo'; import defaultLocale from '../locale/en_US'; import type { SizeType } from '../config-provider'; import devWarning from '../vc-util/devWarning'; -import type { CSSProperties, PropType } from 'vue'; +import type { CSSProperties } from 'vue'; import { nextTick, reactive, ref, computed, defineComponent, toRef, watchEffect, watch } from 'vue'; import type { DefaultRecordType } from '../vc-table/interface'; import useBreakpoint from '../_util/hooks/useBreakpoint'; @@ -47,6 +47,17 @@ import { useProvideSlots, useProvideTableContext } from './context'; import type { ContextSlots } from './context'; import useColumns from './hooks/useColumns'; import { convertChildrenToColumns } from './util'; +import { + stringType, + booleanType, + arrayType, + someType, + functionType, + objectType, +} from '../_util/type'; + +// CSSINJS +import useStyle from './style'; export type { ColumnsType, TablePaginationConfig }; @@ -107,128 +118,68 @@ export interface TableProps export const tableProps = () => { return { - prefixCls: { type: String as PropType, default: undefined }, - columns: { type: Array as PropType, default: undefined }, - rowKey: { type: [String, Function] as PropType, default: undefined }, - tableLayout: { type: String as PropType, default: undefined }, - rowClassName: { - type: [String, Function] as PropType, - default: undefined, - }, - title: { type: Function as PropType, default: undefined }, - footer: { type: Function as PropType, default: undefined }, - id: { type: String as PropType, default: undefined }, - showHeader: { type: Boolean as PropType, default: undefined }, - components: { type: Object as PropType, default: undefined }, - customRow: { type: Function as PropType, default: undefined }, - customHeaderRow: { - type: Function as PropType, - default: undefined, - }, - direction: { type: String as PropType, default: undefined }, - expandFixed: { - type: [Boolean, String] as PropType, - default: undefined, - }, - expandColumnWidth: { - type: Number as PropType, - default: undefined, - }, - expandedRowKeys: { - type: Array as PropType, - default: undefined as TableProps['expandedRowKeys'], - }, - defaultExpandedRowKeys: { - type: Array as PropType, - default: undefined as TableProps['defaultExpandedRowKeys'], - }, - expandedRowRender: { - type: Function as PropType, - default: undefined, - }, - expandRowByClick: { - type: Boolean as PropType, - default: undefined, - }, - expandIcon: { type: Function as PropType, default: undefined }, - onExpand: { type: Function as PropType, default: undefined }, - onExpandedRowsChange: { - type: Function as PropType, - default: undefined, - }, - 'onUpdate:expandedRowKeys': { - type: Function as PropType, - default: undefined, - }, - defaultExpandAllRows: { - type: Boolean as PropType, - default: undefined, - }, - indentSize: { type: Number as PropType, default: undefined }, + prefixCls: stringType(), + columns: arrayType(), + rowKey: someType([String, Function]), + tableLayout: stringType(), + rowClassName: someType([String, Function]), + title: functionType(), + footer: functionType(), + id: stringType(), + showHeader: booleanType(), + components: objectType(), + customRow: functionType(), + customHeaderRow: functionType(), + direction: stringType(), + expandFixed: someType([Boolean, String]), + expandColumnWidth: Number, + expandedRowKeys: arrayType(), + defaultExpandedRowKeys: arrayType(), + expandedRowRender: functionType(), + expandRowByClick: booleanType(), + expandIcon: functionType(), + onExpand: functionType(), + onExpandedRowsChange: functionType(), + 'onUpdate:expandedRowKeys': functionType(), + defaultExpandAllRows: booleanType(), + indentSize: Number, /** @deprecated Please use `EXPAND_COLUMN` in `columns` directly */ - expandIconColumnIndex: { - type: Number as PropType, - default: undefined, - }, - showExpandColumn: { type: Boolean, default: undefined }, - expandedRowClassName: { - type: Function as PropType, - default: undefined, - }, - childrenColumnName: { - type: String as PropType, - default: undefined, - }, - rowExpandable: { type: Function as PropType, default: undefined }, - sticky: { type: [Boolean, Object] as PropType, default: undefined }, + expandIconColumnIndex: Number, + showExpandColumn: booleanType(), + expandedRowClassName: functionType(), + childrenColumnName: stringType(), + rowExpandable: functionType(), + sticky: someType([Boolean, Object]), dropdownPrefixCls: String, - dataSource: { type: Array as PropType, default: undefined }, - pagination: { - type: [Boolean, Object] as PropType, - default: undefined, - }, - loading: { type: [Boolean, Object] as PropType, default: undefined }, - size: { type: String as PropType, default: undefined }, - bordered: Boolean, - locale: { type: Object as PropType, default: undefined }, + dataSource: arrayType(), + pagination: someType([Boolean, Object]), + loading: someType([Boolean, Object]), + size: stringType(), + bordered: booleanType(), + locale: objectType(), - onChange: { - type: Function as PropType< + onChange: + functionType< ( pagination: TablePaginationConfig, filters: Record, sorter: SorterResult | SorterResult[], extra: TableCurrentDataSource, ) => void - >, - default: undefined, - }, - onResizeColumn: { - type: Function as PropType<(w: number, col: ColumnType) => void>, - default: undefined, - }, - rowSelection: { type: Object as PropType, default: undefined }, - getPopupContainer: { type: Function as PropType, default: undefined }, - scroll: { - type: Object as PropType< - RcTableProps['scroll'] & { - scrollToFirstRowOnChange?: boolean; - } - >, - default: undefined, - }, - sortDirections: { type: Array as PropType, default: undefined }, - showSorterTooltip: { - type: [Boolean, Object] as PropType, - default: true, - }, - contextSlots: { - type: Object as PropType, - }, - transformCellText: { - type: Function as PropType, - }, + >(), + onResizeColumn: functionType<(w: number, col: ColumnType) => void>(), + rowSelection: objectType(), + getPopupContainer: functionType(), + scroll: objectType< + RcTableProps['scroll'] & { + scrollToFirstRowOnChange?: boolean; + } + >(), + sortDirections: arrayType(), + showSorterTooltip: someType([Boolean, Object], true), + contextSlots: objectType(), + transformCellText: functionType(), }; }; @@ -287,6 +238,10 @@ const InteralTable = defineComponent< prefixCls, configProvider, } = useConfigInject('table', props); + + // Style + const [wrapSSR, hashId] = useStyle(prefixCls); + const transformCellText = computed( () => props.transformCellText || configProvider.transformCellText?.value, ); @@ -637,9 +592,10 @@ const InteralTable = defineComponent< [`${prefixCls.value}-wrapper-rtl`]: direction.value === 'rtl', }, attrs.class, + hashId.value, ); const tableProps = omit(props, ['columns']); - return ( + return wrapSSR(
{topPaginationNode} @@ -677,7 +633,7 @@ const InteralTable = defineComponent< /> {bottomPaginationNode} -
+ , ); }; }, diff --git a/components/table/hooks/useFilter/FilterSearch.tsx b/components/table/hooks/useFilter/FilterSearch.tsx index 79c8b2c45..f4be80af9 100644 --- a/components/table/hooks/useFilter/FilterSearch.tsx +++ b/components/table/hooks/useFilter/FilterSearch.tsx @@ -1,19 +1,19 @@ -import type { PropType } from 'vue'; import { defineComponent } from 'vue'; import SearchOutlined from '@ant-design/icons-vue/SearchOutlined'; import type { FilterSearchType, TableLocale } from '../../interface'; import Input from '../../../input'; +import { stringType, someType, functionType, objectType } from '../../../_util/type'; export default defineComponent({ compatConfig: { MODE: 3 }, name: 'FilterSearch', inheritAttrs: false, props: { - value: String, - onChange: Function as PropType<(e: InputEvent) => void>, - filterSearch: [Boolean, Function] as PropType, - tablePrefixCls: String, - locale: { type: Object as PropType, default: undefined as TableLocale }, + value: stringType(), + onChange: functionType<(e: InputEvent) => void>(), + filterSearch: someType([Boolean, Function]), + tablePrefixCls: stringType(), + locale: objectType(), }, setup(props) { return () => { diff --git a/components/table/index.en-US.md b/components/table/index.en-US.md index 122ef313e..3e0aebbe7 100644 --- a/components/table/index.en-US.md +++ b/components/table/index.en-US.md @@ -3,7 +3,7 @@ category: Components cols: 1 type: Data Display title: Table -cover: https://gw.alipayobjects.com/zos/alicdn/f-SbcX2Lx/Table.svg +cover: https://mdn.alipayobjects.com/huamei_7uahnr/afts/img/A*3yz3QqMlShYAAAAAAAAAAAAADrJ8AQ/original --- A table displays rows of data. diff --git a/components/table/index.zh-CN.md b/components/table/index.zh-CN.md index 452f0760d..3eda9f16f 100644 --- a/components/table/index.zh-CN.md +++ b/components/table/index.zh-CN.md @@ -4,7 +4,7 @@ cols: 1 type: 数据展示 title: Table subtitle: 表格 -cover: https://gw.alipayobjects.com/zos/alicdn/f-SbcX2Lx/Table.svg +cover: https://mdn.alipayobjects.com/huamei_7uahnr/afts/img/A*3yz3QqMlShYAAAAAAAAAAAAADrJ8AQ/original --- 展示行列数据。 diff --git a/components/table/style/bordered.less b/components/table/style/bordered.less deleted file mode 100644 index 30c09a0de..000000000 --- a/components/table/style/bordered.less +++ /dev/null @@ -1,136 +0,0 @@ -@import './size'; -@import (reference) '../../style/themes/index'; -@table-prefix-cls: ~'@{ant-prefix}-table'; - -@table-border: @border-width-base @border-style-base @table-border-color; - -.@{table-prefix-cls}.@{table-prefix-cls}-bordered { - // ============================ Title ============================= - > .@{table-prefix-cls}-title { - border: @table-border; - border-bottom: 0; - } - - > .@{table-prefix-cls}-container { - // ============================ Content ============================ - border-left: @table-border; - - > .@{table-prefix-cls}-content, - > .@{table-prefix-cls}-header, - > .@{table-prefix-cls}-body, - > .@{table-prefix-cls}-summary { - > table { - // ============================= Cell ============================= - > thead > tr > th, - > tbody > tr > td, - > tfoot > tr > th, - > tfoot > tr > td { - border-right: @table-border; - } - // ============================ Header ============================ - > thead { - > tr:not(:last-child) > th { - border-bottom: @border-width-base @border-style-base @table-border-color; - } - - > tr > th { - &::before { - background-color: transparent !important; - } - } - } - - // Fixed right should provides additional border - > thead > tr, - > tbody > tr, - > tfoot > tr { - > .@{table-prefix-cls}-cell-fix-right-first::after { - border-right: @table-border; - } - } - } - - // ========================== Expandable ========================== - > table > tbody > tr > td { - > .@{table-prefix-cls}-expanded-row-fixed { - margin: -@table-padding-vertical (-@table-padding-horizontal - @border-width-base); - - &::after { - position: absolute; - top: 0; - right: @border-width-base; - bottom: 0; - border-right: @table-border; - content: ''; - } - } - } - } - - > .@{table-prefix-cls}-content, - > .@{table-prefix-cls}-header { - > table { - border-top: @table-border; - } - } - } - - &.@{table-prefix-cls}-scroll-horizontal { - > .@{table-prefix-cls}-container > .@{table-prefix-cls}-body { - > table > tbody { - > tr.@{table-prefix-cls}-expanded-row, - > tr.@{table-prefix-cls}-placeholder { - > td { - border-right: 0; - } - } - } - } - } - - // Size related - &.@{table-prefix-cls}-middle { - > .@{table-prefix-cls}-container { - > .@{table-prefix-cls}-content, - > .@{table-prefix-cls}-body { - > table > tbody > tr > td { - > .@{table-prefix-cls}-expanded-row-fixed { - margin: -@table-padding-vertical-md (-@table-padding-horizontal-md - @border-width-base); - } - } - } - } - } - - &.@{table-prefix-cls}-small { - > .@{table-prefix-cls}-container { - > .@{table-prefix-cls}-content, - > .@{table-prefix-cls}-body { - > table > tbody > tr > td { - > .@{table-prefix-cls}-expanded-row-fixed { - margin: -@table-padding-vertical-sm (-@table-padding-horizontal-sm - @border-width-base); - } - } - } - } - } - - // ============================ Footer ============================ - > .@{table-prefix-cls}-footer { - border: @table-border; - border-top: 0; - } -} - -.@{table-prefix-cls}-cell { - // ============================ Nested ============================ - .@{table-prefix-cls}-container:first-child { - // :first-child to avoid the case when bordered and title is set - border-top: 0; - } - - // https://github.com/ant-design/ant-design/issues/35577 - &-scrollbar:not([rowspan]) { - box-shadow: 0 @border-width-base 0 @border-width-base @table-header-bg; - } -} diff --git a/components/table/style/bordered.ts b/components/table/style/bordered.ts new file mode 100644 index 000000000..076e291ca --- /dev/null +++ b/components/table/style/bordered.ts @@ -0,0 +1,159 @@ +import type { CSSObject } from '../../_util/cssinjs'; +import type { GenerateStyle } from '../../theme/internal'; +import type { TableToken } from './index'; + +const genBorderedStyle: GenerateStyle = token => { + const { componentCls } = token; + const tableBorder = `${token.lineWidth}px ${token.lineType} ${token.tableBorderColor}`; + + const getSizeBorderStyle = ( + size: 'small' | 'middle', + paddingVertical: number, + paddingHorizontal: number, + ) => ({ + [`&${componentCls}-${size}`]: { + [`> ${componentCls}-container`]: { + [`> ${componentCls}-content, > ${componentCls}-body`]: { + '> table > tbody > tr > td': { + [`> ${componentCls}-expanded-row-fixed`]: { + margin: `-${paddingVertical}px -${paddingHorizontal + token.lineWidth}px`, + }, + }, + }, + }, + }, + }); + + return { + [`${componentCls}-wrapper`]: { + [`${componentCls}${componentCls}-bordered`]: { + // ============================ Title ============================= + [`> ${componentCls}-title`]: { + border: tableBorder, + borderBottom: 0, + }, + + // ============================ Content ============================ + [`> ${componentCls}-container`]: { + borderInlineStart: tableBorder, + + [` + > ${componentCls}-content, + > ${componentCls}-header, + > ${componentCls}-body, + > ${componentCls}-summary + `]: { + '> table': { + // ============================= Cell ============================= + [` + > thead > tr > th, + > tbody > tr > td, + > tfoot > tr > th, + > tfoot > tr > td + `]: { + borderInlineEnd: tableBorder, + }, + + // ============================ Header ============================ + '> thead': { + '> tr:not(:last-child) > th': { + borderBottom: tableBorder, + }, + + '> tr > th::before': { + backgroundColor: 'transparent !important', + }, + }, + + // Fixed right should provides additional border + [` + > thead > tr, + > tbody > tr, + > tfoot > tr + `]: { + [`> ${componentCls}-cell-fix-right-first::after`]: { + borderInlineEnd: tableBorder, + }, + }, + + // ========================== Expandable ========================== + '> tbody > tr > td': { + [`> ${componentCls}-expanded-row-fixed`]: { + margin: `-${token.tablePaddingVertical}px -${ + token.tablePaddingHorizontal + token.lineWidth + }px`, + + '&::after': { + position: 'absolute', + top: 0, + insetInlineEnd: token.lineWidth, + bottom: 0, + borderInlineEnd: tableBorder, + content: '""', + }, + }, + }, + }, + }, + + [` + > ${componentCls}-content, + > ${componentCls}-header + `]: { + '> table': { + borderTop: tableBorder, + }, + }, + }, + + // ============================ Scroll ============================ + [`&${componentCls}-scroll-horizontal`]: { + [`> ${componentCls}-container > ${componentCls}-body`]: { + '> table > tbody': { + [` + > tr${componentCls}-expanded-row, + > tr${componentCls}-placeholder + `]: { + '> td': { + borderInlineEnd: 0, + }, + }, + }, + }, + }, + + // ============================ Size ============================ + ...getSizeBorderStyle( + 'middle', + token.tablePaddingVerticalMiddle, + token.tablePaddingHorizontalMiddle, + ), + ...getSizeBorderStyle( + 'small', + token.tablePaddingVerticalSmall, + token.tablePaddingHorizontalSmall, + ), + + // ============================ Footer ============================ + [`> ${componentCls}-footer`]: { + border: tableBorder, + borderTop: 0, + }, + }, + + // ============================ Nested ============================ + [`${componentCls}-cell`]: { + [`${componentCls}-container:first-child`]: { + // :first-child to avoid the case when bordered and title is set + borderTop: 0, + }, + // https://github.com/ant-design/ant-design/issues/35577 + '&-scrollbar:not([rowspan])': { + boxShadow: `0 ${token.lineWidth}px 0 ${token.lineWidth}px ${token.tableHeaderBg}`, + }, + }, + }, + }; +}; + +export default genBorderedStyle; diff --git a/components/table/style/ellipsis.ts b/components/table/style/ellipsis.ts new file mode 100644 index 000000000..4097aef57 --- /dev/null +++ b/components/table/style/ellipsis.ts @@ -0,0 +1,37 @@ +import type { CSSObject } from '../../_util/cssinjs'; +import { textEllipsis } from '../../_style'; +import type { GenerateStyle } from '../../theme/internal'; +import type { TableToken } from './index'; + +const genEllipsisStyle: GenerateStyle = token => { + const { componentCls } = token; + return { + [`${componentCls}-wrapper`]: { + [`${componentCls}-cell-ellipsis`]: { + ...textEllipsis, + wordBreak: 'keep-all', + + // Fixed first or last should special process + [` + &${componentCls}-cell-fix-left-last, + &${componentCls}-cell-fix-right-first + `]: { + overflow: 'visible', + [`${componentCls}-cell-content`]: { + display: 'block', + overflow: 'hidden', + textOverflow: 'ellipsis', + }, + }, + + [`${componentCls}-column-title`]: { + overflow: 'hidden', + textOverflow: 'ellipsis', + wordBreak: 'keep-all', + }, + }, + }, + }; +}; + +export default genEllipsisStyle; diff --git a/components/table/style/empty.ts b/components/table/style/empty.ts new file mode 100644 index 000000000..d478eed04 --- /dev/null +++ b/components/table/style/empty.ts @@ -0,0 +1,22 @@ +import type { CSSObject } from '../../_util/cssinjs'; +import type { GenerateStyle } from '../../theme/internal'; +import type { TableToken } from './index'; + +// ========================= Placeholder ========================== +const genEmptyStyle: GenerateStyle = token => { + const { componentCls } = token; + return { + [`${componentCls}-wrapper`]: { + [`${componentCls}-tbody > tr${componentCls}-placeholder`]: { + textAlign: 'center', + color: token.colorTextDisabled, + + '&:hover > td': { + background: token.colorBgContainer, + }, + }, + }, + }; +}; + +export default genEmptyStyle; diff --git a/components/table/style/expand.ts b/components/table/style/expand.ts new file mode 100644 index 000000000..f9946c4d9 --- /dev/null +++ b/components/table/style/expand.ts @@ -0,0 +1,152 @@ +import type { CSSObject } from '../../_util/cssinjs'; +import type { GenerateStyle } from '../../theme/internal'; +import type { TableToken } from './index'; +import { operationUnit } from '../../_style'; + +const genExpandStyle: GenerateStyle = token => { + const { + componentCls, + antCls, + controlInteractiveSize: checkboxSize, + motionDurationSlow, + lineWidth, + paddingXS, + lineType, + tableBorderColor, + tableExpandIconBg, + tableExpandColumnWidth, + borderRadius, + fontSize, + fontSizeSM, + lineHeight, + tablePaddingVertical, + tablePaddingHorizontal, + tableExpandedRowBg, + paddingXXS, + } = token; + const halfInnerSize = checkboxSize / 2 - lineWidth; + // must be odd number, unless it cannot align center + const expandIconSize = halfInnerSize * 2 + lineWidth * 3; + const tableBorder = `${lineWidth}px ${lineType} ${tableBorderColor}`; + const expandIconLineOffset = paddingXXS - lineWidth; + + return { + [`${componentCls}-wrapper`]: { + [`${componentCls}-expand-icon-col`]: { + width: tableExpandColumnWidth, + }, + + [`${componentCls}-row-expand-icon-cell`]: { + textAlign: 'center', + + [`${componentCls}-row-expand-icon`]: { + display: 'inline-flex', + float: 'none', + verticalAlign: 'sub', + }, + }, + + [`${componentCls}-row-indent`]: { + height: 1, + float: 'left', + }, + + [`${componentCls}-row-expand-icon`]: { + ...operationUnit(token), + position: 'relative', + float: 'left', + boxSizing: 'border-box', + width: expandIconSize, + height: expandIconSize, + padding: 0, + color: 'inherit', + lineHeight: `${expandIconSize}px`, + background: tableExpandIconBg, + border: tableBorder, + borderRadius, + transform: `scale(${checkboxSize / expandIconSize})`, + transition: `all ${motionDurationSlow}`, + userSelect: 'none', + + [`&:focus, &:hover, &:active`]: { + borderColor: 'currentcolor', + }, + + [`&::before, &::after`]: { + position: 'absolute', + background: 'currentcolor', + transition: `transform ${motionDurationSlow} ease-out`, + content: '""', + }, + + '&::before': { + top: halfInnerSize, + insetInlineEnd: expandIconLineOffset, + insetInlineStart: expandIconLineOffset, + height: lineWidth, + }, + + '&::after': { + top: expandIconLineOffset, + bottom: expandIconLineOffset, + insetInlineStart: halfInnerSize, + width: lineWidth, + transform: 'rotate(90deg)', + }, + + // Motion effect + '&-collapsed::before': { + transform: 'rotate(-180deg)', + }, + + '&-collapsed::after': { + transform: 'rotate(0deg)', + }, + + '&-spaced': { + '&::before, &::after': { + display: 'none', + content: 'none', + }, + background: 'transparent', + border: 0, + visibility: 'hidden', + }, + }, + + [`${componentCls}-row-indent + ${componentCls}-row-expand-icon`]: { + marginTop: + (fontSize * lineHeight - lineWidth * 3) / 2 - + Math.ceil((fontSizeSM * 1.4 - lineWidth * 3) / 2), + marginInlineEnd: paddingXS, + }, + + [`tr${componentCls}-expanded-row`]: { + '&, &:hover': { + '> td': { + background: tableExpandedRowBg, + }, + }, + + // https://github.com/ant-design/ant-design/issues/25573 + [`${antCls}-descriptions-view`]: { + display: 'flex', + + table: { + flex: 'auto', + width: 'auto', + }, + }, + }, + + // With fixed + [`${componentCls}-expanded-row-fixed`]: { + position: 'relative', + margin: `-${tablePaddingVertical}px -${tablePaddingHorizontal}px`, + padding: `${tablePaddingVertical}px ${tablePaddingHorizontal}px`, + }, + }, + }; +}; + +export default genExpandStyle; diff --git a/components/table/style/filter.ts b/components/table/style/filter.ts new file mode 100644 index 000000000..bdb17f82f --- /dev/null +++ b/components/table/style/filter.ts @@ -0,0 +1,172 @@ +import type { GenerateStyle } from '../../theme/internal'; +import type { TableToken } from './index'; +import { resetComponent } from '../../_style'; + +const genFilterStyle: GenerateStyle = token => { + const { + componentCls, + antCls, + iconCls, + tableFilterDropdownWidth, + tableFilterDropdownSearchWidth, + paddingXXS, + paddingXS, + colorText, + lineWidth, + lineType, + tableBorderColor, + tableHeaderIconColor, + fontSizeSM, + tablePaddingHorizontal, + borderRadius, + motionDurationSlow, + colorTextDescription, + colorPrimary, + tableHeaderFilterActiveBg, + colorTextDisabled, + tableFilterDropdownBg, + tableFilterDropdownHeight, + controlItemBgHover, + controlItemBgActive, + boxShadowSecondary, + } = token; + const dropdownPrefixCls = `${antCls}-dropdown`; + const tableFilterDropdownPrefixCls = `${componentCls}-filter-dropdown`; + const treePrefixCls = `${antCls}-tree`; + const tableBorder = `${lineWidth}px ${lineType} ${tableBorderColor}`; + + return [ + { + [`${componentCls}-wrapper`]: { + [`${componentCls}-filter-column`]: { + display: 'flex', + justifyContent: 'space-between', + }, + + [`${componentCls}-filter-trigger`]: { + position: 'relative', + display: 'flex', + alignItems: 'center', + marginBlock: -paddingXXS, + marginInline: `${paddingXXS}px ${-tablePaddingHorizontal / 2}px`, + padding: `0 ${paddingXXS}px`, + color: tableHeaderIconColor, + fontSize: fontSizeSM, + borderRadius, + cursor: 'pointer', + transition: `all ${motionDurationSlow}`, + + '&:hover': { + color: colorTextDescription, + background: tableHeaderFilterActiveBg, + }, + + '&.active': { + color: colorPrimary, + }, + }, + }, + }, + { + // Dropdown + [`${antCls}-dropdown`]: { + [tableFilterDropdownPrefixCls]: { + ...resetComponent(token), + + minWidth: tableFilterDropdownWidth, + backgroundColor: tableFilterDropdownBg, + borderRadius, + boxShadow: boxShadowSecondary, + + // Reset menu + [`${dropdownPrefixCls}-menu`]: { + // https://github.com/ant-design/ant-design/issues/4916 + // https://github.com/ant-design/ant-design/issues/19542 + maxHeight: tableFilterDropdownHeight, + overflowX: 'hidden', + border: 0, + boxShadow: 'none', + + '&:empty::after': { + display: 'block', + padding: `${paddingXS}px 0`, + color: colorTextDisabled, + fontSize: fontSizeSM, + textAlign: 'center', + content: '"Not Found"', + }, + }, + + [`${tableFilterDropdownPrefixCls}-tree`]: { + paddingBlock: `${paddingXS}px 0`, + paddingInline: paddingXS, + + [treePrefixCls]: { + padding: 0, + }, + + [`${treePrefixCls}-treenode ${treePrefixCls}-node-content-wrapper:hover`]: { + backgroundColor: controlItemBgHover, + }, + + [`${treePrefixCls}-treenode-checkbox-checked ${treePrefixCls}-node-content-wrapper`]: { + '&, &:hover': { + backgroundColor: controlItemBgActive, + }, + }, + }, + + [`${tableFilterDropdownPrefixCls}-search`]: { + padding: paddingXS, + borderBottom: tableBorder, + + '&-input': { + input: { + minWidth: tableFilterDropdownSearchWidth, + }, + [iconCls]: { + color: colorTextDisabled, + }, + }, + }, + + [`${tableFilterDropdownPrefixCls}-checkall`]: { + width: '100%', + marginBottom: paddingXXS, + marginInlineStart: paddingXXS, + }, + + // Operation + [`${tableFilterDropdownPrefixCls}-btns`]: { + display: 'flex', + justifyContent: 'space-between', + padding: `${paddingXS - lineWidth}px ${paddingXS}px`, + overflow: 'hidden', + backgroundColor: 'inherit', + borderTop: tableBorder, + }, + }, + }, + }, + // Dropdown Menu & SubMenu + { + // submenu of table filter dropdown + [`${antCls}-dropdown ${tableFilterDropdownPrefixCls}, ${tableFilterDropdownPrefixCls}-submenu`]: + { + // Checkbox + [`${antCls}-checkbox-wrapper + span`]: { + paddingInlineStart: paddingXS, + color: colorText, + }, + + [`> ul`]: { + maxHeight: 'calc(100vh - 130px)', + overflowX: 'hidden', + overflowY: 'auto', + }, + }, + }, + ]; +}; + +export default genFilterStyle; diff --git a/components/table/style/fixed.ts b/components/table/style/fixed.ts new file mode 100644 index 000000000..f92db444c --- /dev/null +++ b/components/table/style/fixed.ts @@ -0,0 +1,132 @@ +import type { CSSObject } from '../../_util/cssinjs'; +import type { GenerateStyle } from '../../theme/internal'; +import type { TableToken } from './index'; + +const genFixedStyle: GenerateStyle = token => { + const { + componentCls, + lineWidth, + colorSplit, + motionDurationSlow, + zIndexTableFixed, + tableBg, + zIndexTableSticky, + } = token; + + const shadowColor = colorSplit; + + // Follow style is magic of shadow which should not follow token: + return { + [`${componentCls}-wrapper`]: { + [` + ${componentCls}-cell-fix-left, + ${componentCls}-cell-fix-right + `]: { + position: 'sticky !important' as 'sticky', + zIndex: zIndexTableFixed, + background: tableBg, + }, + + [` + ${componentCls}-cell-fix-left-first::after, + ${componentCls}-cell-fix-left-last::after + `]: { + position: 'absolute', + top: 0, + right: { + _skip_check_: true, + value: 0, + }, + bottom: -lineWidth, + width: 30, + transform: 'translateX(100%)', + transition: `box-shadow ${motionDurationSlow}`, + content: '""', + pointerEvents: 'none', + }, + + [`${componentCls}-cell-fix-left-all::after`]: { + display: 'none', + }, + + [` + ${componentCls}-cell-fix-right-first::after, + ${componentCls}-cell-fix-right-last::after + `]: { + position: 'absolute', + top: 0, + bottom: -lineWidth, + left: { + _skip_check_: true, + value: 0, + }, + width: 30, + transform: 'translateX(-100%)', + transition: `box-shadow ${motionDurationSlow}`, + content: '""', + pointerEvents: 'none', + }, + + [`${componentCls}-container`]: { + '&::before, &::after': { + position: 'absolute', + top: 0, + bottom: 0, + zIndex: zIndexTableSticky + 1, + width: 30, + transition: `box-shadow ${motionDurationSlow}`, + content: '""', + pointerEvents: 'none', + }, + + '&::before': { + insetInlineStart: 0, + }, + + '&::after': { + insetInlineEnd: 0, + }, + }, + + [`${componentCls}-ping-left`]: { + [`&:not(${componentCls}-has-fix-left) ${componentCls}-container`]: { + position: 'relative', + + '&::before': { + boxShadow: `inset 10px 0 8px -8px ${shadowColor}`, + }, + }, + + [` + ${componentCls}-cell-fix-left-first::after, + ${componentCls}-cell-fix-left-last::after + `]: { + boxShadow: `inset 10px 0 8px -8px ${shadowColor}`, + }, + + [`${componentCls}-cell-fix-left-last::before`]: { + backgroundColor: 'transparent !important', + }, + }, + + [`${componentCls}-ping-right`]: { + [`&:not(${componentCls}-has-fix-right) ${componentCls}-container`]: { + position: 'relative', + + '&::after': { + boxShadow: `inset -10px 0 8px -8px ${shadowColor}`, + }, + }, + + [` + ${componentCls}-cell-fix-right-first::after, + ${componentCls}-cell-fix-right-last::after + `]: { + boxShadow: `inset -10px 0 8px -8px ${shadowColor}`, + }, + }, + }, + }; +}; + +export default genFixedStyle; diff --git a/components/table/style/index.less b/components/table/style/index.less deleted file mode 100644 index 46a5af8a5..000000000 --- a/components/table/style/index.less +++ /dev/null @@ -1,754 +0,0 @@ -@import '../../style/themes/index'; -@import '../../style/mixins/index'; -@import './size'; -@import './bordered'; - -@table-prefix-cls: ~'@{ant-prefix}-table'; -@tree-prefix-cls: ~'@{ant-prefix}-tree'; -@dropdown-prefix-cls: ~'@{ant-prefix}-dropdown'; -@descriptions-prefix-cls: ~'@{ant-prefix}-descriptions'; -@table-header-icon-color: #bfbfbf; -@table-header-icon-color-hover: darken(@table-header-icon-color, 10%); -@table-sticky-zindex: calc(@zindex-table-fixed + 1); -@table-sticky-scroll-bar-active-bg: fade(@table-sticky-scroll-bar-bg, 80%); -@table-filter-dropdown-max-height: 264px; - -.@{table-prefix-cls}-wrapper { - clear: both; - max-width: 100%; - .clearfix(); -} - -.@{table-prefix-cls} { - .reset-component(); - position: relative; - font-size: @table-font-size; - background: @table-bg; - border-radius: @table-border-radius-base; - - // https://github.com/ant-design/ant-design/issues/17611 - table { - width: 100%; - text-align: left; - border-radius: @table-border-radius-base @table-border-radius-base 0 0; - border-collapse: separate; - border-spacing: 0; - } - - // ============================= Cell ============================= - &-thead > tr > th, - &-tbody > tr > td, - tfoot > tr > th, - tfoot > tr > td { - position: relative; - padding: @table-padding-vertical @table-padding-horizontal; - overflow-wrap: break-word; - } - - &-cell-ellipsis { - overflow: hidden; - white-space: nowrap; - text-overflow: ellipsis; - word-break: keep-all; - - // Fixed first or last should special process - &.@{table-prefix-cls}-cell-fix-left-last, - &.@{table-prefix-cls}-cell-fix-right-first { - overflow: visible; - - .@{table-prefix-cls}-cell-content { - display: block; - overflow: hidden; - text-overflow: ellipsis; - } - } - - .@{table-prefix-cls}-column-title { - overflow: hidden; - text-overflow: ellipsis; - word-break: keep-all; - } - } - - // ============================ Title ============================= - &-title { - padding: @table-padding-vertical @table-padding-horizontal; - } - - // ============================ Footer ============================ - &-footer { - padding: @table-padding-vertical @table-padding-horizontal; - color: @table-footer-color; - background: @table-footer-bg; - } - - // ============================ Header ============================ - &-thead { - > tr { - > th { - position: relative; - color: @table-header-color; - font-weight: 500; - text-align: left; - background: @table-header-bg; - border-bottom: @border-width-base @border-style-base @table-border-color; - transition: background 0.3s ease; - - &[colspan]:not([colspan='1']) { - text-align: center; - } - - &:not(:last-child):not(.@{table-prefix-cls}-selection-column):not(.@{table-prefix-cls}-row-expand-icon-cell):not([colspan])::before { - position: absolute; - top: 50%; - right: 0; - width: 1px; - height: 1.6em; - background-color: @table-header-cell-split-color; - transform: translateY(-50%); - transition: background-color 0.3s; - content: ''; - } - } - } - - > tr:not(:last-child) > th { - &[colspan] { - border-bottom: 0; - } - } - } - - // ============================= Body ============================= - &-tbody { - > tr { - > td { - border-bottom: @border-width-base @border-style-base @table-border-color; - transition: background 0.3s; - - // ========================= Nest Table =========================== - > .@{table-prefix-cls}-wrapper:only-child, - > .@{table-prefix-cls}-expanded-row-fixed > .@{table-prefix-cls}-wrapper:only-child { - .@{table-prefix-cls} { - margin: -@table-padding-vertical -@table-padding-horizontal -@table-padding-vertical (@table-padding-horizontal + - ceil(@font-size-sm * 1.4)); - - &-tbody > tr:last-child > td { - border-bottom: 0; - - &:first-child, - &:last-child { - border-radius: 0; - } - } - } - } - } - - &.@{table-prefix-cls}-row:hover > td, - > td.@{table-prefix-cls}-cell-row-hover { - background: @table-row-hover-bg; - } - - &.@{table-prefix-cls}-row-selected { - > td { - background: @table-selected-row-bg; - border-color: rgba(0, 0, 0, 0.03); - } - - &:hover { - > td { - background: @table-selected-row-hover-bg; - } - } - } - } - } - - // =========================== Summary ============================ - &-summary { - position: relative; - z-index: @zindex-table-fixed; - background: @table-bg; - - div& { - box-shadow: 0 -@border-width-base 0 @table-border-color; - } - - > tr { - > th, - > td { - border-bottom: @border-width-base @border-style-base @table-border-color; - } - } - } - - // ========================== Pagination ========================== - &-pagination.@{ant-prefix}-pagination { - margin: 16px 0; - } - - &-pagination { - display: flex; - flex-wrap: wrap; - row-gap: @padding-xs; - - > * { - flex: none; - } - - &-left { - justify-content: flex-start; - } - - &-center { - justify-content: center; - } - - &-right { - justify-content: flex-end; - } - } - - // ================================================================ - // = Function = - // ================================================================ - - // ============================ Sorter ============================ - &-thead th.@{table-prefix-cls}-column-has-sorters { - outline: none; - cursor: pointer; - transition: all 0.3s; - - &:hover { - background: @table-header-sort-active-bg; - - &::before { - background-color: transparent !important; - } - } - - &:focus-visible { - color: @primary-color; - } - - // https://github.com/ant-design/ant-design/issues/30969 - &.@{table-prefix-cls}-cell-fix-left:hover, - &.@{table-prefix-cls}-cell-fix-right:hover { - background: @table-fixed-header-sort-active-bg; - } - } - - &-thead th.@{table-prefix-cls}-column-sort { - background: @table-header-sort-bg; - - &::before { - background-color: transparent !important; - } - } - - td&-column-sort { - background: @table-body-sort-bg; - } - - &-column-title { - position: relative; - z-index: 1; - flex: 1; - } - - &-column-sorters { - display: flex; - flex: auto; - align-items: center; - justify-content: space-between; - - &::after { - position: absolute; - top: 0; - right: 0; - bottom: 0; - left: 0; - width: 100%; - height: 100%; - content: ''; - } - } - - &-column-sorter { - margin-left: 4px; - color: @table-header-icon-color; - font-size: 0; - transition: color 0.3s; - - &-inner { - display: inline-flex; - flex-direction: column; - align-items: center; - } - - &-up, - &-down { - font-size: 11px; - - &.active { - color: @primary-color; - } - } - - &-up + &-down { - margin-top: -0.3em; - } - } - - &-column-sorters:hover &-column-sorter { - color: darken(@table-header-icon-color, 10%); - } - - // ============================ Filter ============================ - &-filter-column { - display: flex; - justify-content: space-between; - } - - &-filter-trigger { - position: relative; - display: flex; - align-items: center; - margin: -4px (-@table-padding-horizontal / 2) -4px 4px; - padding: 0 4px; - color: @table-header-icon-color; - font-size: @font-size-sm; - border-radius: @border-radius-base; - cursor: pointer; - transition: all 0.3s; - - &:hover { - color: @text-color-secondary; - background: @table-header-filter-active-bg; - } - - &.active { - color: @primary-color; - } - } - - // Dropdown - &-filter-dropdown { - .reset-component(); - - min-width: 120px; - background-color: @table-filter-dropdown-bg; - border-radius: @border-radius-base; - box-shadow: @box-shadow-base; - - // Reset menu - .@{dropdown-prefix-cls}-menu { - // https://github.com/ant-design/ant-design/issues/4916 - // https://github.com/ant-design/ant-design/issues/19542 - max-height: @table-filter-dropdown-max-height; - overflow-x: hidden; - border: 0; - box-shadow: none; - - &:empty::after { - display: block; - padding: 8px 0; - color: @disabled-color; - font-size: @font-size-sm; - text-align: center; - content: 'Not Found'; - } - } - - &-tree { - padding: 8px 8px 0; - - .@{tree-prefix-cls}-treenode .@{tree-prefix-cls}-node-content-wrapper:hover { - background-color: @tree-node-hover-bg; - } - - .@{tree-prefix-cls}-treenode-checkbox-checked .@{tree-prefix-cls}-node-content-wrapper { - &, - &:hover { - background-color: @tree-node-selected-bg; - } - } - } - - &-search { - padding: 8px; - border-bottom: @border-width-base @border-color-split @border-style-base; - - &-input { - input { - min-width: 140px; - } - .@{iconfont-css-prefix} { - color: @disabled-color; - } - } - } - - &-checkall { - width: 100%; - margin-bottom: 4px; - margin-left: 4px; - } - - &-submenu > ul { - max-height: calc(100vh - 130px); - overflow-x: hidden; - overflow-y: auto; - } - - // Checkbox - &, - &-submenu { - .@{ant-prefix}-checkbox-wrapper + span { - padding-left: 8px; - } - } - - // Operation - &-btns { - display: flex; - justify-content: space-between; - padding: 7px 8px; - overflow: hidden; - background-color: @table-filter-btns-bg; - border-top: @border-width-base @border-style-base @table-border-color; - } - } - - // ========================== Selections ========================== - &-selection-col { - width: @table-selection-column-width; - } - - &-bordered &-selection-col { - width: @table-selection-column-width + 18px; - } - - table tr th&-selection-column, - table tr td&-selection-column { - padding-right: @padding-xs; - padding-left: @padding-xs; - text-align: center; - - .@{ant-prefix}-radio-wrapper { - margin-right: 0; - } - } - - table tr th&-selection-column&-cell-fix-left { - z-index: 3; - } - - table tr th&-selection-column::after { - background-color: transparent !important; - } - - &-selection { - position: relative; - display: inline-flex; - flex-direction: column; - - &-extra { - position: absolute; - top: 0; - z-index: 1; - cursor: pointer; - transition: all 0.3s; - margin-inline-start: 100%; - padding-inline-start: (@table-padding-horizontal / 4); - - .@{iconfont-css-prefix} { - color: @table-header-icon-color; - font-size: 10px; - - &:hover { - color: @table-header-icon-color-hover; - } - } - } - } - - // ========================== Expandable ========================== - &-expand-icon-col { - width: 48px; - } - - &-row-expand-icon-cell { - text-align: center; - } - - &-row-indent { - float: left; - height: 1px; - } - - &-row-expand-icon { - .operation-unit(); - position: relative; - display: inline-flex; - float: left; - box-sizing: border-box; - width: @expand-icon-size; - height: @expand-icon-size; - padding: 0; - color: inherit; - line-height: ceil(((@font-size-sm * 1.4 - @border-width-base * 3) / 2)) * 2 + @border-width-base * - 3; - background: @table-expand-icon-bg; - border: @border-width-base @border-style-base @table-border-color; - border-radius: @border-radius-base; - outline: none; - transform: scale((unit(@checkbox-size) / unit(@expand-icon-size))); - transition: all 0.3s; - user-select: none; - @expand-icon-size: ceil(((@font-size-sm * 1.4 - @border-width-base * 3) / 2)) * 2 + - @border-width-base * 3; - - &:focus, - &:hover, - &:active { - border-color: currentcolor; - } - - &::before, - &::after { - position: absolute; - background: currentcolor; - transition: transform 0.3s ease-out; - content: ''; - } - - &::before { - top: ceil(((@font-size-sm * 1.4 - @border-width-base * 3) / 2)); - right: 3px; - left: 3px; - height: @border-width-base; - } - - &::after { - top: 3px; - bottom: 3px; - left: ceil(((@font-size-sm * 1.4 - @border-width-base * 3) / 2)); - width: @border-width-base; - transform: rotate(90deg); - } - - // Motion effect - &-collapsed::before { - transform: rotate(-180deg); - } - - &-collapsed::after { - transform: rotate(0deg); - } - - &-spaced { - &::before, - &::after { - display: none; - content: none; - } - background: transparent; - border: 0; - visibility: hidden; - } - - .@{table-prefix-cls}-row-indent + & { - margin-top: ((@font-size-base * @line-height-base - @border-width-base * 3) / 2) - - ceil(((@font-size-sm * 1.4 - @border-width-base * 3) / 2)); - margin-right: @padding-xs; - } - } - - tr&-expanded-row { - &, - &:hover { - > td { - background: @table-expanded-row-bg; - } - } - - // https://github.com/ant-design/ant-design/issues/25573 - .@{descriptions-prefix-cls}-view { - display: flex; - - table { - flex: auto; - width: auto; - } - } - } - - // With fixed - .@{table-prefix-cls}-expanded-row-fixed { - position: relative; - margin: -@table-padding-vertical -@table-padding-horizontal; - padding: @table-padding-vertical @table-padding-horizontal; - } - - // ========================= Placeholder ========================== - &-tbody > tr&-placeholder { - text-align: center; - .@{table-prefix-cls}-empty & { - color: @disabled-color; - } - - &:hover { - > td { - background: @component-background; - } - } - } - - // ============================ Fixed ============================= - &-cell-fix-left, - &-cell-fix-right { - position: sticky !important; - z-index: @zindex-table-fixed; - background: @table-bg; - } - - &-cell-fix-left-first::after, - &-cell-fix-left-last::after { - position: absolute; - top: 0; - right: 0; - bottom: -1px; - width: 30px; - transform: translateX(100%); - transition: box-shadow 0.3s; - content: ''; - pointer-events: none; - } - - &-cell-fix-right-first::after, - &-cell-fix-right-last::after { - position: absolute; - top: 0; - bottom: -1px; - left: 0; - width: 30px; - transform: translateX(-100%); - transition: box-shadow 0.3s; - content: ''; - pointer-events: none; - } - - .@{table-prefix-cls}-container { - &::before, - &::after { - position: absolute; - top: 0; - bottom: 0; - z-index: @zindex-table-fixed; - width: 30px; - transition: box-shadow 0.3s; - content: ''; - pointer-events: none; - } - - &::before { - left: 0; - } - - &::after { - right: 0; - } - } - - &-ping-left { - &:not(.@{table-prefix-cls}-has-fix-left) .@{table-prefix-cls}-container { - position: relative; - - &::before { - box-shadow: inset 10px 0 8px -8px darken(@shadow-color, 5%); - } - } - - .@{table-prefix-cls}-cell-fix-left-first::after, - .@{table-prefix-cls}-cell-fix-left-last::after { - box-shadow: inset 10px 0 8px -8px darken(@shadow-color, 5%); - } - - .@{table-prefix-cls}-cell-fix-left-last::before { - background-color: transparent !important; - } - } - - &-ping-right { - &:not(.@{table-prefix-cls}-has-fix-right) .@{table-prefix-cls}-container { - position: relative; - - &::after { - box-shadow: inset -10px 0 8px -8px darken(@shadow-color, 5%); - } - } - - .@{table-prefix-cls}-cell-fix-right-first::after, - .@{table-prefix-cls}-cell-fix-right-last::after { - box-shadow: inset -10px 0 8px -8px darken(@shadow-color, 5%); - } - } - - &-sticky { - &-holder { - position: sticky; - z-index: @table-sticky-zindex; - background: @component-background; - } - - &-scroll { - position: sticky; - bottom: 0; - z-index: @table-sticky-zindex; - display: flex; - align-items: center; - background: lighten(@table-border-color, 80%); - border-top: 1px solid @table-border-color; - opacity: 0.6; - - &:hover { - transform-origin: center bottom; - } - - &-bar { - height: 8px; - background-color: @table-sticky-scroll-bar-bg; - border-radius: @table-sticky-scroll-bar-radius; - - &:hover { - background-color: @table-sticky-scroll-bar-active-bg; - } - - &-active { - background-color: @table-sticky-scroll-bar-active-bg; - } - } - } - } -} - -@media all and (-ms-high-contrast: none) { - .@{table-prefix-cls} { - &-ping-left { - .@{table-prefix-cls}-cell-fix-left-last::after { - box-shadow: none !important; - } - } - - &-ping-right { - .@{table-prefix-cls}-cell-fix-right-first::after { - box-shadow: none !important; - } - } - } -} - -@import './radius'; -@import './rtl'; diff --git a/components/table/style/index.ts b/components/table/style/index.ts new file mode 100644 index 000000000..2b0984396 --- /dev/null +++ b/components/table/style/index.ts @@ -0,0 +1,409 @@ +import type { CSSObject } from '../../_util/cssinjs'; +import { TinyColor } from '@ctrl/tinycolor'; +import type { FullToken, GenerateStyle } from '../../theme/internal'; +import { genComponentStyleHook, mergeToken } from '../../theme/internal'; +import genBorderedStyle from './bordered'; +import genEllipsisStyle from './ellipsis'; +import genEmptyStyle from './empty'; +import genExpandStyle from './expand'; +import genFilterStyle from './filter'; +import genFixedStyle from './fixed'; +import genPaginationStyle from './pagination'; +import genRadiusStyle from './radius'; +import genRtlStyle from './rtl'; +import genSelectionStyle from './selection'; +import genSizeStyle from './size'; +import genSorterStyle from './sorter'; +import genStickyStyle from './sticky'; +import genSummaryStyle from './summary'; +import { clearFix, resetComponent } from '../../_style'; + +export interface ComponentToken {} + +export interface TableToken extends FullToken<'Table'> { + tableFontSize: number; + tableBg: string; + tableRadius: number; + tablePaddingHorizontal: number; + tablePaddingVertical: number; + tablePaddingHorizontalMiddle: number; + tablePaddingVerticalMiddle: number; + tablePaddingHorizontalSmall: number; + tablePaddingVerticalSmall: number; + tableBorderColor: string; + tableHeaderTextColor: string; + tableHeaderBg: string; + tableFooterTextColor: string; + tableFooterBg: string; + tableHeaderCellSplitColor: string; + tableHeaderSortBg: string; + tableHeaderSortHoverBg: string; + tableHeaderIconColor: string; + tableHeaderIconColorHover: string; + tableBodySortBg: string; + tableFixedHeaderSortActiveBg: string; + tableHeaderFilterActiveBg: string; + tableFilterDropdownBg: string; + tableFilterDropdownHeight: number; + tableRowHoverBg: string; + tableSelectedRowBg: string; + tableSelectedRowHoverBg: string; + + tableFontSizeMiddle: number; + tableFontSizeSmall: number; + tableSelectionColumnWidth: number; + tableExpandIconBg: string; + tableExpandColumnWidth: number; + tableExpandedRowBg: string; + tableFilterDropdownWidth: number; + tableFilterDropdownSearchWidth: number; + + // Z-Index + zIndexTableFixed: number; + zIndexTableSticky: number; + + // Virtual Scroll Bar + tableScrollThumbSize: number; + tableScrollThumbBg: string; + tableScrollThumbBgHover: string; + tableScrollBg: string; +} + +const genTableStyle: GenerateStyle = token => { + const { + componentCls, + fontWeightStrong, + tablePaddingVertical, + tablePaddingHorizontal, + lineWidth, + lineType, + tableBorderColor, + tableFontSize, + tableBg, + tableRadius, + tableHeaderTextColor, + motionDurationMid, + tableHeaderBg, + tableHeaderCellSplitColor, + tableRowHoverBg, + tableSelectedRowBg, + tableSelectedRowHoverBg, + tableFooterTextColor, + tableFooterBg, + paddingContentVerticalLG, + wireframe, + } = token; + const tableBorder = `${lineWidth}px ${lineType} ${tableBorderColor}`; + return { + [`${componentCls}-wrapper`]: { + clear: 'both', + maxWidth: '100%', + ...clearFix(), + + [componentCls]: { + ...resetComponent(token), + fontSize: tableFontSize, + background: tableBg, + borderRadius: `${tableRadius}px ${tableRadius}px 0 0`, + }, + // https://github.com/ant-design/ant-design/issues/17611 + table: { + width: '100%', + textAlign: 'start', + borderRadius: `${tableRadius}px ${tableRadius}px 0 0`, + borderCollapse: 'separate', + borderSpacing: 0, + }, + + // ============================= Cell ============================= + [` + ${componentCls}-thead > tr > th, + ${componentCls}-tbody > tr > td, + tfoot > tr > th, + tfoot > tr > td + `]: { + position: 'relative', + padding: `${paddingContentVerticalLG}px ${tablePaddingHorizontal}px`, + overflowWrap: 'break-word', + }, + + // ============================ Title ============================= + [`${componentCls}-title`]: { + padding: `${tablePaddingVertical}px ${tablePaddingHorizontal}px`, + }, + + // ============================ Header ============================ + [`${componentCls}-thead`]: { + [` + > tr > th, + > tr > td + `]: { + position: 'relative', + color: tableHeaderTextColor, + fontWeight: fontWeightStrong, + textAlign: 'start', + background: tableHeaderBg, + borderBottom: tableBorder, + transition: `background ${motionDurationMid} ease`, + + "&[colspan]:not([colspan='1'])": { + textAlign: 'center', + }, + + [`&:not(:last-child):not(${componentCls}-selection-column):not(${componentCls}-row-expand-icon-cell):not([colspan])::before`]: + { + position: 'absolute', + top: '50%', + insetInlineEnd: 0, + width: 1, + height: '1.6em', + backgroundColor: tableHeaderCellSplitColor, + transform: 'translateY(-50%)', + transition: `background-color ${motionDurationMid}`, + content: '""', + }, + }, + + '> tr:not(:last-child) > th[colspan]': { + borderBottom: 0, + }, + }, + + // ============================ Body ============================ + // Borderless Table has unique hover style, which would be implemented with `borderTop`. + [`${componentCls}:not(${componentCls}-bordered)`]: { + [`${componentCls}-tbody`]: { + '> tr': { + '> td': { + borderTop: tableBorder, + borderBottom: 'transparent', + }, + + '&:last-child > td': { + borderBottom: tableBorder, + }, + + [`&:first-child > td, + &${componentCls}-measure-row + tr > td`]: { + borderTop: 'none', + borderTopColor: 'transparent', + }, + }, + }, + }, + + // Bordered Table remains simple `borderBottom`. + // Ref issue: https://github.com/ant-design/ant-design/issues/38724 + [`${componentCls}${componentCls}-bordered`]: { + [`${componentCls}-tbody`]: { + '> tr': { + '> td': { + borderBottom: tableBorder, + }, + }, + }, + }, + + [`${componentCls}-tbody`]: { + '> tr': { + '> td': { + transition: `background ${motionDurationMid}, border-color ${motionDurationMid}`, + + // ========================= Nest Table =========================== + [` + > ${componentCls}-wrapper:only-child, + > ${componentCls}-expanded-row-fixed > ${componentCls}-wrapper:only-child + `]: { + [componentCls]: { + marginBlock: `-${tablePaddingVertical}px`, + marginInline: `${ + token.tableExpandColumnWidth - tablePaddingHorizontal + }px -${tablePaddingHorizontal}px`, + [`${componentCls}-tbody > tr:last-child > td`]: { + borderBottom: 0, + '&:first-child, &:last-child': { + borderRadius: 0, + }, + }, + }, + }, + }, + + [` + &${componentCls}-row:hover > td, + > td${componentCls}-cell-row-hover + `]: { + background: tableRowHoverBg, + }, + + [`&${componentCls}-row-selected`]: { + '> td': { + background: tableSelectedRowBg, + }, + + '&:hover > td': { + background: tableSelectedRowHoverBg, + }, + }, + }, + }, + + [`${componentCls}:not(${componentCls}-bordered) ${componentCls}-tbody > tr`]: wireframe + ? undefined + : { + [`&${componentCls}-row:hover, &${componentCls}-row${componentCls}-row-selected`]: { + [`+ tr${componentCls}-row > td`]: { + borderTopColor: 'transparent', + }, + }, + + [`&${componentCls}-row:last-child:hover > td, + &${componentCls}-row${componentCls}-row-selected:last-child > td`]: { + borderBottomColor: 'transparent', + }, + + [` + &${componentCls}-row:hover > td, + > td${componentCls}-cell-row-hover, + &${componentCls}-row${componentCls}-row-selected > td + `]: { + borderTopColor: 'transparent', + + '&:first-child': { + borderStartStartRadius: tableRadius, + borderEndStartRadius: tableRadius, + }, + + '&:last-child': { + borderStartEndRadius: tableRadius, + borderEndEndRadius: tableRadius, + }, + }, + }, + + // ============================ Footer ============================ + [`${componentCls}-footer`]: { + padding: `${tablePaddingVertical}px ${tablePaddingHorizontal}px`, + color: tableFooterTextColor, + background: tableFooterBg, + }, + }, + }; +}; + +// ============================== Export ============================== +export default genComponentStyleHook('Table', token => { + const { + controlItemBgActive, + controlItemBgActiveHover, + colorTextPlaceholder, + colorTextHeading, + colorSplit, + colorBorderSecondary, + fontSize, + padding, + paddingXS, + paddingSM, + controlHeight, + colorFillAlter, + colorIcon, + colorIconHover, + opacityLoading, + colorBgContainer, + borderRadiusLG, + colorFillContent, + colorFillSecondary, + controlInteractiveSize: checkboxSize, + } = token; + + const baseColorAction = new TinyColor(colorIcon); + const baseColorActionHover = new TinyColor(colorIconHover); + + const tableSelectedRowBg = controlItemBgActive; + const zIndexTableFixed: number = 2; + + const colorFillSecondarySolid = new TinyColor(colorFillSecondary) + .onBackground(colorBgContainer) + .toHexString(); + const colorFillContentSolid = new TinyColor(colorFillContent) + .onBackground(colorBgContainer) + .toHexString(); + + const colorFillAlterSolid = new TinyColor(colorFillAlter) + .onBackground(colorBgContainer) + .toHexString(); + + const tableToken = mergeToken(token, { + tableFontSize: fontSize, + tableBg: colorBgContainer, + tableRadius: borderRadiusLG, + + tablePaddingVertical: padding, + tablePaddingHorizontal: padding, + tablePaddingVerticalMiddle: paddingSM, + tablePaddingHorizontalMiddle: paddingXS, + tablePaddingVerticalSmall: paddingXS, + tablePaddingHorizontalSmall: paddingXS, + tableBorderColor: colorBorderSecondary, + tableHeaderTextColor: colorTextHeading, + tableHeaderBg: colorFillAlterSolid, + tableFooterTextColor: colorTextHeading, + tableFooterBg: colorFillAlterSolid, + tableHeaderCellSplitColor: colorBorderSecondary, + tableHeaderSortBg: colorFillSecondarySolid, + tableHeaderSortHoverBg: colorFillContentSolid, + tableHeaderIconColor: baseColorAction + .clone() + .setAlpha(baseColorAction.getAlpha() * opacityLoading) + .toRgbString(), + tableHeaderIconColorHover: baseColorActionHover + .clone() + .setAlpha(baseColorActionHover.getAlpha() * opacityLoading) + .toRgbString(), + tableBodySortBg: colorFillAlterSolid, + tableFixedHeaderSortActiveBg: colorFillSecondarySolid, + tableHeaderFilterActiveBg: colorFillContent, + tableFilterDropdownBg: colorBgContainer, + tableRowHoverBg: colorFillAlterSolid, + tableSelectedRowBg, + tableSelectedRowHoverBg: controlItemBgActiveHover, + zIndexTableFixed, + zIndexTableSticky: zIndexTableFixed + 1, + tableFontSizeMiddle: fontSize, + tableFontSizeSmall: fontSize, + tableSelectionColumnWidth: controlHeight, + tableExpandIconBg: colorBgContainer, + tableExpandColumnWidth: checkboxSize + 2 * token.padding, + tableExpandedRowBg: colorFillAlter, + + // Dropdown + tableFilterDropdownWidth: 120, + tableFilterDropdownHeight: 264, + tableFilterDropdownSearchWidth: 140, + + // Virtual Scroll Bar + tableScrollThumbSize: 8, // Mac scroll bar size + tableScrollThumbBg: colorTextPlaceholder, + tableScrollThumbBgHover: colorTextHeading, + tableScrollBg: colorSplit, + }); + + return [ + genTableStyle(tableToken), + genPaginationStyle(tableToken), + genSummaryStyle(tableToken), + genSorterStyle(tableToken), + genFilterStyle(tableToken), + genBorderedStyle(tableToken), + genRadiusStyle(tableToken), + genExpandStyle(tableToken), + genSummaryStyle(tableToken), + genEmptyStyle(tableToken), + genSelectionStyle(tableToken), + genFixedStyle(tableToken), + genStickyStyle(tableToken), + genEllipsisStyle(tableToken), + genSizeStyle(tableToken), + genRtlStyle(tableToken), + ]; +}); diff --git a/components/table/style/index.tsx b/components/table/style/index.tsx deleted file mode 100644 index 50815c77a..000000000 --- a/components/table/style/index.tsx +++ /dev/null @@ -1,16 +0,0 @@ -import '../../style/index.less'; -import './index.less'; - -// style dependencies -// deps-lint-skip: menu -// deps-lint-skip: grid -import '../../button/style'; -import '../../empty/style'; -import '../../radio/style'; -import '../../checkbox/style'; -import '../../dropdown/style'; -import '../../spin/style'; -import '../../pagination/style'; -import '../../tooltip/style'; -import '../../input/style'; -import '../../tree/style'; diff --git a/components/table/style/pagination.ts b/components/table/style/pagination.ts new file mode 100644 index 000000000..cd3f7c2f2 --- /dev/null +++ b/components/table/style/pagination.ts @@ -0,0 +1,39 @@ +import type { CSSObject } from '../../_util/cssinjs'; +import type { GenerateStyle } from '../../theme/internal'; +import type { TableToken } from './index'; + +const genPaginationStyle: GenerateStyle = token => { + const { componentCls, antCls } = token; + return { + [`${componentCls}-wrapper`]: { + // ========================== Pagination ========================== + [`${componentCls}-pagination${antCls}-pagination`]: { + margin: `${token.margin}px 0`, + }, + + [`${componentCls}-pagination`]: { + display: 'flex', + flexWrap: 'wrap', + rowGap: token.paddingXS, + + '> *': { + flex: 'none', + }, + + '&-left': { + justifyContent: 'flex-start', + }, + + '&-center': { + justifyContent: 'center', + }, + + '&-right': { + justifyContent: 'flex-end', + }, + }, + }, + }; +}; + +export default genPaginationStyle; diff --git a/components/table/style/radius.less b/components/table/style/radius.less deleted file mode 100644 index 1927c1a06..000000000 --- a/components/table/style/radius.less +++ /dev/null @@ -1,45 +0,0 @@ -// ================================================================ -// = Border Radio = -// ================================================================ -.@{table-prefix-cls} { - /* title + table */ - &-title { - border-radius: @table-border-radius-base @table-border-radius-base 0 0; - } - - &-title + &-container { - border-top-left-radius: 0; - border-top-right-radius: 0; - - table > thead > tr:first-child { - th:first-child { - border-radius: 0; - } - - th:last-child { - border-radius: 0; - } - } - } - - /* table */ - &-container { - border-top-left-radius: @table-border-radius-base; - border-top-right-radius: @table-border-radius-base; - - table > thead > tr:first-child { - th:first-child { - border-top-left-radius: @table-border-radius-base; - } - - th:last-child { - border-top-right-radius: @table-border-radius-base; - } - } - } - - /* table + footer */ - &-footer { - border-radius: 0 0 @table-border-radius-base @table-border-radius-base; - } -} diff --git a/components/table/style/radius.ts b/components/table/style/radius.ts new file mode 100644 index 000000000..9462573e8 --- /dev/null +++ b/components/table/style/radius.ts @@ -0,0 +1,57 @@ +import type { CSSObject } from '../../_util/cssinjs'; +import type { GenerateStyle } from '../../theme/internal'; +import type { TableToken } from './index'; + +const genRadiusStyle: GenerateStyle = token => { + const { componentCls, tableRadius } = token; + return { + [`${componentCls}-wrapper`]: { + [componentCls]: { + // https://github.com/ant-design/ant-design/issues/39115#issuecomment-1362314574 + [`${componentCls}-title, ${componentCls}-header`]: { + borderRadius: `${tableRadius}px ${tableRadius}px 0 0`, + }, + + [`${componentCls}-title + ${componentCls}-container`]: { + borderStartStartRadius: 0, + borderStartEndRadius: 0, + + table: { + borderRadius: 0, + + '> thead > tr:first-child': { + 'th:first-child': { + borderRadius: 0, + }, + + 'th:last-child': { + borderRadius: 0, + }, + }, + }, + }, + + '&-container': { + borderStartStartRadius: tableRadius, + borderStartEndRadius: tableRadius, + + 'table > thead > tr:first-child': { + '> *:first-child': { + borderStartStartRadius: tableRadius, + }, + + '> *:last-child': { + borderStartEndRadius: tableRadius, + }, + }, + }, + + '&-footer': { + borderRadius: `0 0 ${tableRadius}px ${tableRadius}px`, + }, + }, + }, + }; +}; + +export default genRadiusStyle; diff --git a/components/table/style/resize.less b/components/table/style/resize.less deleted file mode 100644 index 3dfcd71d4..000000000 --- a/components/table/style/resize.less +++ /dev/null @@ -1,41 +0,0 @@ -.@{table-prefix-cls}-resize-handle { - position: absolute; - top: 0; - height: 100% !important; - bottom: 0; - left: auto !important; - right: -8px; - cursor: col-resize; - touch-action: none; - user-select: auto; - width: 16px; - z-index: 1; - &-line { - display: block; - width: 1px; - margin-left: 7px; - height: 100% !important; - background-color: @primary-color; - opacity: 0; - } - &:hover &-line { - opacity: 1; - } -} - -.@{table-prefix-cls}-resize-handle.dragging { - overflow: hidden; - .@{table-prefix-cls}-resize-handle-line { - opacity: 1; - } - &:before { - position: absolute; - top: 0; - bottom: 0; - width: 100%; - content: ' '; - width: 200vw; - transform: translateX(-50%); - opacity: 0; - } -} diff --git a/components/table/style/rtl.less b/components/table/style/rtl.less deleted file mode 100644 index 2aba565e3..000000000 --- a/components/table/style/rtl.less +++ /dev/null @@ -1,168 +0,0 @@ -@import '../../style/themes/index'; -@import '../../style/mixins/index'; - -@table-prefix-cls: ~'@{ant-prefix}-table'; -@table-wrapepr-cls: ~'@{table-prefix-cls}-wrapper'; -@table-wrapepr-rtl-cls: ~'@{table-prefix-cls}-wrapper-rtl'; - -.@{table-prefix-cls}-wrapper { - &-rtl { - direction: rtl; - } -} - -.@{table-prefix-cls} { - &-rtl { - direction: rtl; - } - - table { - .@{table-wrapepr-rtl-cls} & { - text-align: right; - } - } - - // ============================ Header ============================ - &-thead { - > tr { - > th { - &[colspan]:not([colspan='1']) { - .@{table-wrapepr-rtl-cls} & { - text-align: center; - } - } - - &:not(:last-child):not(.@{table-prefix-cls}-selection-column):not(.@{table-prefix-cls}-row-expand-icon-cell):not([colspan])::before { - .@{table-wrapepr-rtl-cls} & { - right: auto; - left: 0; - } - } - - .@{table-wrapepr-rtl-cls} & { - text-align: right; - } - } - } - } - - // ============================= Body ============================= - &-tbody { - > tr { - // ========================= Nest Table =========================== - .@{table-prefix-cls}-wrapper:only-child { - .@{table-prefix-cls}.@{table-prefix-cls}-rtl { - margin: -@table-padding-vertical (@table-padding-horizontal + ceil(@font-size-sm * 1.4)) -@table-padding-vertical -@table-padding-horizontal; - } - } - } - } - - // ========================== Pagination ========================== - &-pagination { - &-left { - .@{table-wrapepr-cls}.@{table-wrapepr-rtl-cls} & { - justify-content: flex-end; - } - } - - &-right { - .@{table-wrapepr-cls}.@{table-wrapepr-rtl-cls} & { - justify-content: flex-start; - } - } - } - - // ================================================================ - // = Function = - // ================================================================ - - // ============================ Sorter ============================ - &-column-sorter { - .@{table-wrapepr-rtl-cls} & { - margin-right: 4px; - margin-left: 0; - } - } - - // ============================ Filter ============================ - &-filter-column-title { - .@{table-wrapepr-rtl-cls} & { - padding: @table-padding-vertical @table-padding-horizontal @table-padding-vertical 2.3em; - } - } - - &-thead tr th.@{table-prefix-cls}-column-has-sorters { - .@{table-prefix-cls}-filter-column-title { - .@{table-prefix-cls}-rtl & { - padding: 0 0 0 2.3em; - } - } - } - - &-filter-trigger { - .@{table-wrapepr-rtl-cls} & { - margin: -4px 4px -4px (-@table-padding-horizontal / 2); - } - } - - // Dropdown - &-filter-dropdown { - // Checkbox - &, - &-submenu { - .@{ant-prefix}-checkbox-wrapper + span { - .@{ant-prefix}-dropdown-rtl &, - .@{ant-prefix}-dropdown-menu-submenu-rtl& { - padding-right: 8px; - padding-left: 0; - } - } - } - } - - // ========================== Selections ========================== - &-selection { - .@{table-wrapepr-rtl-cls} & { - text-align: center; - } - } - - // ========================== Expandable ========================== - &-row-indent { - .@{table-wrapepr-rtl-cls} & { - float: right; - } - } - - &-row-expand-icon { - .@{table-wrapepr-rtl-cls} & { - float: right; - } - - .@{table-prefix-cls}-row-indent + & { - .@{table-wrapepr-rtl-cls} & { - margin-right: 0; - margin-left: @padding-xs; - } - } - - &::after { - .@{table-wrapepr-rtl-cls} & { - transform: rotate(-90deg); - } - } - - &-collapsed::before { - .@{table-wrapepr-rtl-cls} & { - transform: rotate(180deg); - } - } - - &-collapsed::after { - .@{table-wrapepr-rtl-cls} & { - transform: rotate(0deg); - } - } - } -} diff --git a/components/table/style/rtl.ts b/components/table/style/rtl.ts new file mode 100644 index 000000000..cbe4c2718 --- /dev/null +++ b/components/table/style/rtl.ts @@ -0,0 +1,39 @@ +import type { CSSObject } from '../../_util/cssinjs'; +import type { GenerateStyle } from '../../theme/internal'; +import type { TableToken } from './index'; + +const genStyle: GenerateStyle = token => { + const { componentCls } = token; + return { + [`${componentCls}-wrapper-rtl`]: { + direction: 'rtl', + table: { + direction: 'rtl', + }, + + [`${componentCls}-pagination-left`]: { + justifyContent: 'flex-end', + }, + + [`${componentCls}-pagination-right`]: { + justifyContent: 'flex-start', + }, + + [`${componentCls}-row-expand-icon`]: { + '&::after': { + transform: 'rotate(-90deg)', + }, + + '&-collapsed::before': { + transform: 'rotate(180deg)', + }, + + '&-collapsed::after': { + transform: 'rotate(0deg)', + }, + }, + }, + }; +}; + +export default genStyle; diff --git a/components/table/style/selection.ts b/components/table/style/selection.ts new file mode 100644 index 000000000..421bc8e14 --- /dev/null +++ b/components/table/style/selection.ts @@ -0,0 +1,76 @@ +import type { CSSObject } from '../../_util/cssinjs'; +import type { GenerateStyle } from '../../theme/internal'; +import type { TableToken } from './index'; + +const genSelectionStyle: GenerateStyle = token => { + const { + componentCls, + antCls, + iconCls, + fontSizeIcon, + paddingXS, + tableHeaderIconColor, + tableHeaderIconColorHover, + } = token; + return { + [`${componentCls}-wrapper`]: { + // ========================== Selections ========================== + [`${componentCls}-selection-col`]: { + width: token.tableSelectionColumnWidth, + }, + + [`${componentCls}-bordered ${componentCls}-selection-col`]: { + width: token.tableSelectionColumnWidth + paddingXS * 2, + }, + + [` + table tr th${componentCls}-selection-column, + table tr td${componentCls}-selection-column + `]: { + paddingInlineEnd: token.paddingXS, + paddingInlineStart: token.paddingXS, + textAlign: 'center', + + [`${antCls}-radio-wrapper`]: { + marginInlineEnd: 0, + }, + }, + + [`table tr th${componentCls}-selection-column${componentCls}-cell-fix-left`]: { + zIndex: token.zIndexTableFixed + 1, + }, + + [`table tr th${componentCls}-selection-column::after`]: { + backgroundColor: 'transparent !important', + }, + + [`${componentCls}-selection`]: { + position: 'relative', + display: 'inline-flex', + flexDirection: 'column', + }, + + [`${componentCls}-selection-extra`]: { + position: 'absolute', + top: 0, + zIndex: 1, + cursor: 'pointer', + transition: `all ${token.motionDurationSlow}`, + marginInlineStart: '100%', + paddingInlineStart: `${token.tablePaddingHorizontal / 4}px`, + + [iconCls]: { + color: tableHeaderIconColor, + fontSize: fontSizeIcon, + verticalAlign: 'baseline', + + '&:hover': { + color: tableHeaderIconColorHover, + }, + }, + }, + }, + }; +}; + +export default genSelectionStyle; diff --git a/components/table/style/size.less b/components/table/style/size.less deleted file mode 100644 index 2f864ade4..000000000 --- a/components/table/style/size.less +++ /dev/null @@ -1,51 +0,0 @@ -@import (reference) '../../style/themes/index'; - -@table-prefix-cls: ~'@{ant-prefix}-table'; - -.table-size(@size, @padding-vertical, @padding-horizontal, @font-size) { - .@{table-prefix-cls}.@{table-prefix-cls}-@{size} { - font-size: @font-size; - - .@{table-prefix-cls}-title, - .@{table-prefix-cls}-footer, - .@{table-prefix-cls}-thead > tr > th, - .@{table-prefix-cls}-tbody > tr > td, - tfoot > tr > th, - tfoot > tr > td { - padding: @padding-vertical @padding-horizontal; - } - - .@{table-prefix-cls}-filter-trigger { - margin-right: -(@padding-horizontal / 2); - } - - .@{table-prefix-cls}-expanded-row-fixed { - margin: -@padding-vertical -@padding-horizontal; - } - - .@{table-prefix-cls}-tbody { - // ========================= Nest Table =========================== - .@{table-prefix-cls}-wrapper:only-child { - .@{table-prefix-cls} { - margin: -@padding-vertical -@padding-horizontal -@padding-vertical (@padding-horizontal + - ceil((@font-size-sm * 1.4))); - } - } - } - - // https://github.com/ant-design/ant-design/issues/35167 - .@{table-prefix-cls}-selection-column { - padding-inline-start: (@padding-horizontal / 4); - } - } -} - -// ================================================================ -// = Middle = -// ================================================================ -.table-size(~'middle', @table-padding-vertical-md, @table-padding-horizontal-md, @table-font-size-md); - -// ================================================================ -// = Small = -// ================================================================ -.table-size(~'small', @table-padding-vertical-sm, @table-padding-horizontal-sm, @table-font-size-sm); diff --git a/components/table/style/size.ts b/components/table/style/size.ts new file mode 100644 index 000000000..d8ec4c9dc --- /dev/null +++ b/components/table/style/size.ts @@ -0,0 +1,68 @@ +import type { CSSObject } from '../../_util/cssinjs'; +import type { GenerateStyle } from '../../theme/internal'; +import type { TableToken } from './index'; + +const genSizeStyle: GenerateStyle = token => { + const { componentCls } = token; + const getSizeStyle = ( + size: 'small' | 'middle', + paddingVertical: number, + paddingHorizontal: number, + fontSize: number, + ) => ({ + [`${componentCls}${componentCls}-${size}`]: { + fontSize, + [` + ${componentCls}-title, + ${componentCls}-footer, + ${componentCls}-thead > tr > th, + ${componentCls}-tbody > tr > td, + tfoot > tr > th, + tfoot > tr > td + `]: { + padding: `${paddingVertical}px ${paddingHorizontal}px`, + }, + + [`${componentCls}-filter-trigger`]: { + marginInlineEnd: `-${paddingHorizontal / 2}px`, + }, + + [`${componentCls}-expanded-row-fixed`]: { + margin: `-${paddingVertical}px -${paddingHorizontal}px`, + }, + + [`${componentCls}-tbody`]: { + // ========================= Nest Table =========================== + [`${componentCls}-wrapper:only-child ${componentCls}`]: { + marginBlock: `-${paddingVertical}px`, + marginInline: `${ + token.tableExpandColumnWidth - paddingHorizontal + }px -${paddingHorizontal}px`, + }, + }, + + // https://github.com/ant-design/ant-design/issues/35167 + [`${componentCls}-selection-column`]: { + paddingInlineStart: `${paddingHorizontal / 4}px`, + }, + }, + }); + return { + [`${componentCls}-wrapper`]: { + ...getSizeStyle( + 'middle', + token.tablePaddingVerticalMiddle, + token.tablePaddingHorizontalMiddle, + token.tableFontSizeMiddle, + ), + ...getSizeStyle( + 'small', + token.tablePaddingVerticalSmall, + token.tablePaddingHorizontalSmall, + token.tableFontSizeSmall, + ), + }, + }; +}; + +export default genSizeStyle; diff --git a/components/table/style/sorter.ts b/components/table/style/sorter.ts new file mode 100644 index 000000000..af9e676b4 --- /dev/null +++ b/components/table/style/sorter.ts @@ -0,0 +1,101 @@ +import type { CSSObject } from '../../_util/cssinjs'; +import type { GenerateStyle } from '../../theme/internal'; +import type { TableToken } from './index'; + +const genSorterStyle: GenerateStyle = token => { + const { componentCls, marginXXS, fontSizeIcon, tableHeaderIconColor, tableHeaderIconColorHover } = + token; + return { + [`${componentCls}-wrapper`]: { + [`${componentCls}-thead th${componentCls}-column-has-sorters`]: { + outline: 'none', + cursor: 'pointer', + transition: `all ${token.motionDurationSlow}`, + + '&:hover': { + background: token.tableHeaderSortHoverBg, + + '&::before': { + backgroundColor: 'transparent !important', + }, + }, + + '&:focus-visible': { + color: token.colorPrimary, + }, + + // https://github.com/ant-design/ant-design/issues/30969 + [` + &${componentCls}-cell-fix-left:hover, + &${componentCls}-cell-fix-right:hover + `]: { + background: token.tableFixedHeaderSortActiveBg, + }, + }, + + [`${componentCls}-thead th${componentCls}-column-sort`]: { + background: token.tableHeaderSortBg, + + '&::before': { + backgroundColor: 'transparent !important', + }, + }, + + [`td${componentCls}-column-sort`]: { + background: token.tableBodySortBg, + }, + + [`${componentCls}-column-title`]: { + position: 'relative', + zIndex: 1, + flex: 1, + }, + + [`${componentCls}-column-sorters`]: { + display: 'flex', + flex: 'auto', + alignItems: 'center', + justifyContent: 'space-between', + + '&::after': { + position: 'absolute', + inset: 0, + width: '100%', + height: '100%', + content: '""', + }, + }, + + [`${componentCls}-column-sorter`]: { + marginInlineStart: marginXXS, + color: tableHeaderIconColor, + fontSize: 0, + transition: `color ${token.motionDurationSlow}`, + + '&-inner': { + display: 'inline-flex', + flexDirection: 'column', + alignItems: 'center', + }, + + '&-up, &-down': { + fontSize: fontSizeIcon, + + '&.active': { + color: token.colorPrimary, + }, + }, + + [`${componentCls}-column-sorter-up + ${componentCls}-column-sorter-down`]: { + marginTop: '-0.3em', + }, + }, + + [`${componentCls}-column-sorters:hover ${componentCls}-column-sorter`]: { + color: tableHeaderIconColorHover, + }, + }, + }; +}; + +export default genSorterStyle; diff --git a/components/table/style/sticky.ts b/components/table/style/sticky.ts new file mode 100644 index 000000000..6140c8483 --- /dev/null +++ b/components/table/style/sticky.ts @@ -0,0 +1,59 @@ +import type { CSSObject } from '../../_util/cssinjs'; +import type { GenerateStyle } from '../../theme/internal'; +import type { TableToken } from './index'; + +const genStickyStyle: GenerateStyle = token => { + const { + componentCls, + opacityLoading, + tableScrollThumbBg, + tableScrollThumbBgHover, + tableScrollThumbSize, + tableScrollBg, + zIndexTableSticky, + } = token; + const tableBorder = `${token.lineWidth}px ${token.lineType} ${token.tableBorderColor}`; + return { + [`${componentCls}-wrapper`]: { + [`${componentCls}-sticky`]: { + '&-holder': { + position: 'sticky', + zIndex: zIndexTableSticky, + background: token.colorBgContainer, + }, + + '&-scroll': { + position: 'sticky', + bottom: 0, + height: `${tableScrollThumbSize}px !important`, + zIndex: zIndexTableSticky, + display: 'flex', + alignItems: 'center', + background: tableScrollBg, + borderTop: tableBorder, + opacity: opacityLoading, + + '&:hover': { + transformOrigin: 'center bottom', + }, + + // fake scrollbar style of sticky + '&-bar': { + height: tableScrollThumbSize, + backgroundColor: tableScrollThumbBg, + borderRadius: 100, + transition: `all ${token.motionDurationSlow}, transform none`, + position: 'absolute', + bottom: 0, + + '&:hover, &-active': { + backgroundColor: tableScrollThumbBgHover, + }, + }, + }, + }, + }, + }; +}; + +export default genStickyStyle; diff --git a/components/table/style/summary.ts b/components/table/style/summary.ts new file mode 100644 index 000000000..e4b6dfab2 --- /dev/null +++ b/components/table/style/summary.ts @@ -0,0 +1,29 @@ +import type { CSSObject } from '../../_util/cssinjs'; +import type { GenerateStyle } from '../../theme/internal'; +import type { TableToken } from './index'; + +const genSummaryStyle: GenerateStyle = token => { + const { componentCls, lineWidth, tableBorderColor } = token; + const tableBorder = `${lineWidth}px ${token.lineType} ${tableBorderColor}`; + return { + [`${componentCls}-wrapper`]: { + [`${componentCls}-summary`]: { + position: 'relative', + zIndex: token.zIndexTableFixed, + background: token.tableBg, + + '> tr': { + '> th, > td': { + borderBottom: tableBorder, + }, + }, + }, + + [`div${componentCls}-summary`]: { + boxShadow: `0 -${lineWidth}px 0 ${tableBorderColor}`, + }, + }, + }; +}; + +export default genSummaryStyle; diff --git a/components/theme/interface/components.ts b/components/theme/interface/components.ts index 2eb0ac51a..667a026b9 100644 --- a/components/theme/interface/components.ts +++ b/components/theme/interface/components.ts @@ -37,7 +37,7 @@ import type { ComponentToken as SliderComponentToken } from '../../slider/style' import type { ComponentToken as SpaceComponentToken } from '../../space/style'; import type { ComponentToken as SpinComponentToken } from '../../spin/style'; import type { ComponentToken as StepsComponentToken } from '../../steps/style'; -// import type { ComponentToken as TableComponentToken } from '../../table/style'; +import type { ComponentToken as TableComponentToken } from '../../table/style'; // import type { ComponentToken as TabsComponentToken } from '../../tabs/style'; import type { ComponentToken as TagComponentToken } from '../../tag/style'; import type { ComponentToken as TimelineComponentToken } from '../../timeline/style'; @@ -109,7 +109,7 @@ export interface ComponentTokenMap { Message?: MessageComponentToken; Upload?: UploadComponentToken; Tooltip?: TooltipComponentToken; - // Table?: TableComponentToken; + Table?: TableComponentToken; Space?: SpaceComponentToken; Progress?: ProgressComponentToken; // Tour?: TourComponentToken;