From 05e639922eae342e557a6c142c7aa296d81c7f72 Mon Sep 17 00:00:00 2001 From: saller Date: Wed, 5 Jan 2022 16:04:14 +0800 Subject: [PATCH] feat(table): modify table functions and style (#672) * add tree table indent support * fix tree table sort function * fix sort orderby(was't controlled previously) * add filter function for table * expandable column now extends base column(usual data column) * modify table style according to new design --- .../components/config/src/defaultConfig.ts | 2 +- packages/components/icon/demo/all.ts | 1 + packages/components/icon/src/definitions.ts | 5 + packages/components/icon/src/dependencies.ts | 2 + .../components/style/variable/height.less | 1 + packages/components/table/demo/Basic.vue | 6 +- .../components/table/demo/ColSpanRowSpan.md | 2 +- packages/components/table/demo/Expandable.vue | 2 +- packages/components/table/demo/Filterable.md | 14 ++ packages/components/table/demo/Filterable.vue | 105 ++++++++++ .../table/demo/FilterableFilterBy.md | 14 ++ .../table/demo/FilterableFilterBy.vue | 139 +++++++++++++ packages/components/table/demo/Tree.md | 14 ++ packages/components/table/demo/Tree.vue | 182 ++++++++++++++++++ packages/components/table/demo/Virtual.md | 2 +- packages/components/table/docs/Index.zh.md | 14 +- packages/components/table/index.ts | 2 + packages/components/table/src/Table.tsx | 26 ++- .../table/src/composables/useDataSource.ts | 73 +++++-- .../table/src/composables/useExpandable.ts | 20 +- .../table/src/composables/useFilterable.ts | 55 ++++++ .../table/src/composables/useScroll.ts | 9 + .../table/src/composables/useSelectable.ts | 26 +-- .../table/src/composables/useSortable.ts | 25 ++- .../components/table/src/main/ColGroup.tsx | 22 ++- .../components/table/src/main/FixedHolder.tsx | 7 +- .../components/table/src/main/MainTable.tsx | 11 +- .../table/src/main/StickyScroll.tsx | 9 +- .../components/table/src/main/body/Body.tsx | 2 +- .../table/src/main/body/BodyCell.tsx | 62 ++++-- .../table/src/main/body/BodyRow.tsx | 56 +++--- .../table/src/main/body/MeasureRow.tsx | 4 +- .../HeadCellFilterableTrigger.tsx | 56 ++++++ .../HeadCellSortableTrigger.tsx} | 34 ++-- .../table/src/main/head/HeadCell.tsx | 80 ++++++-- .../src/main/head/HeadCellSelectable.tsx | 72 +++++-- .../table/src/other/FilterDropdown.tsx | 41 ++++ .../components/table/src/other/Footer.tsx | 5 +- .../components/table/src/other/Pagination.tsx | 3 +- packages/components/table/src/token.ts | 5 +- packages/components/table/src/types.ts | 41 +++- .../table/style/filterDropdown.less | 24 +++ .../components/table/style/headerIcon.less | 20 ++ packages/components/table/style/index.less | 73 ++++--- packages/components/table/style/size.less | 14 +- .../table/style/themes/default.less | 1 + .../table/style/themes/default.variable.less | 30 ++- scripts/gulp/icons/assets/filter-filled.svg | 1 + 48 files changed, 1188 insertions(+), 226 deletions(-) create mode 100644 packages/components/table/demo/Filterable.md create mode 100644 packages/components/table/demo/Filterable.vue create mode 100644 packages/components/table/demo/FilterableFilterBy.md create mode 100644 packages/components/table/demo/FilterableFilterBy.vue create mode 100644 packages/components/table/demo/Tree.md create mode 100644 packages/components/table/demo/Tree.vue create mode 100644 packages/components/table/src/composables/useFilterable.ts create mode 100644 packages/components/table/src/main/head-trigger/HeadCellFilterableTrigger.tsx rename packages/components/table/src/main/{head/HeadCellSortable.tsx => head-trigger/HeadCellSortableTrigger.tsx} (60%) create mode 100644 packages/components/table/src/other/FilterDropdown.tsx create mode 100644 packages/components/table/style/filterDropdown.less create mode 100644 packages/components/table/style/headerIcon.less create mode 100644 scripts/gulp/icons/assets/filter-filled.svg diff --git a/packages/components/config/src/defaultConfig.ts b/packages/components/config/src/defaultConfig.ts index c73072953..9fd463b23 100644 --- a/packages/components/config/src/defaultConfig.ts +++ b/packages/components/config/src/defaultConfig.ts @@ -255,7 +255,7 @@ const table: TableConfig = { align: 'start', sortable: { nextTooltip: false, orders: ['ascend', 'descend'] }, }, - columnExpandable: { icon: ['plus', 'minus'] }, + columnExpandable: { icon: ['plus-square', 'minus-square'] }, } const tooltip: TooltipConfig = { diff --git a/packages/components/icon/demo/all.ts b/packages/components/icon/demo/all.ts index 65a9d017f..a52ba03eb 100644 --- a/packages/components/icon/demo/all.ts +++ b/packages/components/icon/demo/all.ts @@ -80,6 +80,7 @@ export const allIcons = [ 'file-text', 'file-zip', 'file', + 'filter-filled', 'filter', 'folder-add', 'folder-open', diff --git a/packages/components/icon/src/definitions.ts b/packages/components/icon/src/definitions.ts index 182d7afeb..58815a0a9 100644 --- a/packages/components/icon/src/definitions.ts +++ b/packages/components/icon/src/definitions.ts @@ -410,6 +410,11 @@ export const File = { svg: '', } +export const FilterFilled = { + name: 'filter-filled', + svg: '', +} + export const Filter = { name: 'filter', svg: '', diff --git a/packages/components/icon/src/dependencies.ts b/packages/components/icon/src/dependencies.ts index 470667b5e..d5cb373e8 100644 --- a/packages/components/icon/src/dependencies.ts +++ b/packages/components/icon/src/dependencies.ts @@ -25,6 +25,7 @@ import { Empty, ExclamationCircle, ExclamationCircleFilled, + FilterFilled, InfoCircle, InfoCircleFilled, Left, @@ -67,6 +68,7 @@ export const IDUX_ICON_DEPENDENCIES: IconDefinition[] = [ Empty, // Empty ExclamationCircle, // Message Result Alert Notification ExclamationCircleFilled, // Modal Popconfirm + FilterFilled, // Table InfoCircle, // Message Result Alert Notification InfoCircleFilled, // Modal Left, // date-panel diff --git a/packages/components/style/variable/height.less b/packages/components/style/variable/height.less index aa53d5f1a..d08120fd4 100644 --- a/packages/components/style/variable/height.less +++ b/packages/components/style/variable/height.less @@ -3,5 +3,6 @@ @height-md: 32px; @height-lg: 40px; @height-xl: 48px; +@height-xxl: 64px; @line-height-base: 1.5715; diff --git a/packages/components/table/demo/Basic.vue b/packages/components/table/demo/Basic.vue index 8d473d22f..bd57a9bdd 100644 --- a/packages/components/table/demo/Basic.vue +++ b/packages/components/table/demo/Basic.vue @@ -43,11 +43,11 @@ const columns: TableColumn[] = [ dataKey: 'tags', customRender: ({ value }: TableColumnRenderOption) => value.map(tag => { - let type = tag.length > 5 ? 'warning' : 'success' + let color = tag.length > 5 ? 'warning' : 'success' if (tag === 'loser') { - type = 'error' + color = 'error' } - return h(IxTag, { type }, { default: () => tag.toUpperCase() }) + return h(IxTag, { color }, { default: () => tag.toUpperCase() }) }), }, { diff --git a/packages/components/table/demo/ColSpanRowSpan.md b/packages/components/table/demo/ColSpanRowSpan.md index db3889327..970768b46 100644 --- a/packages/components/table/demo/ColSpanRowSpan.md +++ b/packages/components/table/demo/ColSpanRowSpan.md @@ -2,7 +2,7 @@ title: zh: 行/列合并 en: Merge row and column -order: 60 +order: 10 --- ## zh diff --git a/packages/components/table/demo/Expandable.vue b/packages/components/table/demo/Expandable.vue index 9adbb071e..e334fbfeb 100644 --- a/packages/components/table/demo/Expandable.vue +++ b/packages/components/table/demo/Expandable.vue @@ -66,13 +66,13 @@ const data: Data[] = [ name: 'Joe Black', age: 32, address: 'Sidney No. 1 Lake Park', + description: 'My name is Joe Black, I am 32 years old, living in Sidney No. 1 Lake Park.', }, { key: 4, name: 'Disabled User', age: 99, address: 'Sidney No. 1 Lake Park', - description: 'My name is Joe Black, I am 32 years old, living in Sidney No. 1 Lake Park.', }, ] diff --git a/packages/components/table/demo/Filterable.md b/packages/components/table/demo/Filterable.md new file mode 100644 index 000000000..2e560ea21 --- /dev/null +++ b/packages/components/table/demo/Filterable.md @@ -0,0 +1,14 @@ +--- +title: + zh: 可筛选 + en: Filterable +order: 52 +--- + +## zh + +通过设置 `filterable` 对某一列数据进行筛选。 + +## en + +Sort a column of data by setting `filterable`. diff --git a/packages/components/table/demo/Filterable.vue b/packages/components/table/demo/Filterable.vue new file mode 100644 index 000000000..6ae2cf669 --- /dev/null +++ b/packages/components/table/demo/Filterable.vue @@ -0,0 +1,105 @@ + + + diff --git a/packages/components/table/demo/FilterableFilterBy.md b/packages/components/table/demo/FilterableFilterBy.md new file mode 100644 index 000000000..34236443a --- /dev/null +++ b/packages/components/table/demo/FilterableFilterBy.md @@ -0,0 +1,14 @@ +--- +title: + zh: 受控的筛选 + en: Controlled filterable +order: 53 +--- + +## zh + +如果设置了 `filterable` 的 `filterby`,表格列的筛选将为受控状态。 + +## en + +If `filterby` of `filterable` is set, the filtering of the column will be in a controlled state. diff --git a/packages/components/table/demo/FilterableFilterBy.vue b/packages/components/table/demo/FilterableFilterBy.vue new file mode 100644 index 000000000..d7b45b566 --- /dev/null +++ b/packages/components/table/demo/FilterableFilterBy.vue @@ -0,0 +1,139 @@ + + + diff --git a/packages/components/table/demo/Tree.md b/packages/components/table/demo/Tree.md new file mode 100644 index 000000000..ca77bc6b2 --- /dev/null +++ b/packages/components/table/demo/Tree.md @@ -0,0 +1,14 @@ +--- +title: + zh: 树表格 + en: Tree table +order: 80 +--- + +## zh + +树形表格 + +## en + +Tree table diff --git a/packages/components/table/demo/Tree.vue b/packages/components/table/demo/Tree.vue new file mode 100644 index 000000000..06996d732 --- /dev/null +++ b/packages/components/table/demo/Tree.vue @@ -0,0 +1,182 @@ + + + diff --git a/packages/components/table/demo/Virtual.md b/packages/components/table/demo/Virtual.md index 2c40dbbd0..c5d1fcf70 100644 --- a/packages/components/table/demo/Virtual.md +++ b/packages/components/table/demo/Virtual.md @@ -2,7 +2,7 @@ title: zh: 虚拟滚动 en: Virtual -order: 80 +order: 90 --- ## zh diff --git a/packages/components/table/docs/Index.zh.md b/packages/components/table/docs/Index.zh.md index 7637c0613..9f92cba4b 100644 --- a/packages/components/table/docs/Index.zh.md +++ b/packages/components/table/docs/Index.zh.md @@ -16,6 +16,7 @@ order: 0 | --- | --- | --- | --- | --- | --- | | `v-model:expandedRowKeys` | 展开行的 `key` 数组 | `(string \| number)[]` | - | - | - | | `v-model:selectedRowKeys` | 选中行的 `key` 数组 | `(string \| number)[]` | - | - | - | +| `v-model:expandAllStatus` | 全部展开的状态 | `boolean` | - | - | - | | `borderless` | 是否无边框 | `boolean` | `false` | ✅ | - | | `childrenKey` | 指定树形结构的 `key` | `string` | `children` | - | - | | `columns` | 表格列的配置描述, 参见[TableColumn](#TableColumn) | `TableColumn[]` | - | - | - | @@ -85,14 +86,14 @@ export type TableColumnTitleFn = (options: { title?: string }) => VNodeTypes ##### TableColumnExpandable -可展开列配置的属性,继承 `TableColumnCommon` +可展开列配置的属性,继承 `TableColumnBase` | 名称 | 说明 | 类型 | 默认值 | 全局配置 | 备注 | | --- | --- | --- | --- | --- | --- | | `type` | 列类型 | `'expandable'` | - | - | `type` 设置为 `expandable`,即为展开列 | | `customExpand` | 自定义展开内容 | `string \| TableColumnExpandableExpandFn` | - | - | 类型为 `string` 时,对应插槽名 | | `customIcon` | 自定义展开图标 | `string \| TableColumnExpandableIconFn` | - | - | 类型为 `string` 时,对应插槽名 | -| `disabled` | 设置是否允许行展开 | `(record:T, rowIndex: number) => boolean` | - | - | - | +| `disabled` | 设置是否允许行展开 | `(record:T) => boolean` | - | - | - | | `icon` | 展开按钮图标 | `[string, string]` | `['plus', 'minus']` | ✅ | - | | `indent` | 展示树形数据时,每层缩进的宽度 | `number` | `12` | - | - | | `trigger` | 不通过图标,触发行展开的方式 | `'click' \| 'doubleClick'` | - | - | - | @@ -144,6 +145,15 @@ export interface TableColumnSelectableOption { | `sorter` | 本地模式下,排序的运行函数 | `(curr: T, next: T) => number` | - | - | 参考 [`Array.sort`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/sort) | | `onChange` | 排序规则改变后的回调 | `(currOrderBy?: TableColumnSortOrder) => void` | - | - | 通常用于服务端排序 | +##### TableColumnFilterable + +| 名称 | 说明 | 类型 | 默认值 | 全局配置 | 备注 | +| --- | --- | --- | --- | --- | --- | +| `filters` | 筛选项 | `{ text: string, value: any }[]` | - | - | - | +| `filterBy` | 当前激活的筛选项 | `any[]` | - | - | - | +| `filter` | 本地模式下的筛选函数 | `(currFilterBy: any[], record: T) => boolean` | - | - | - | +| `onChange` | 排序规则改变后的回调 | `(currFilterBy: any[]) => void` | - | - | 通常用于服务端筛选 | + #### TablePagination ```ts diff --git a/packages/components/table/index.ts b/packages/components/table/index.ts index 9fd7aea62..edcf73e10 100644 --- a/packages/components/table/index.ts +++ b/packages/components/table/index.ts @@ -36,4 +36,6 @@ export type { TableColumnFixed, TableColumnSortable, TableColumnSortOrder, + TableColumnFilter, + TableColumnFilterable, } from './src/types' diff --git a/packages/components/table/src/Table.tsx b/packages/components/table/src/Table.tsx index b984cb0a1..37c52b2df 100644 --- a/packages/components/table/src/Table.tsx +++ b/packages/components/table/src/Table.tsx @@ -7,7 +7,7 @@ import type { SpinProps } from '@idux/components/spin' -import { defineComponent, provide } from 'vue' +import { computed, defineComponent, provide } from 'vue' import { isBoolean } from 'lodash-es' @@ -19,6 +19,7 @@ import { IxSpin } from '@idux/components/spin' import { useColumns } from './composables/useColumns' import { useDataSource } from './composables/useDataSource' import { useExpandable } from './composables/useExpandable' +import { useFilterables } from './composables/useFilterable' import { useGetRowKey } from './composables/useGetRowKey' import { usePagination } from './composables/usePagination' import { useScroll } from './composables/useScroll' @@ -36,8 +37,11 @@ import { tableProps } from './types' export default defineComponent({ name: 'IxTable', props: tableProps, - setup(props, { slots }) { + setup(props, { expose, slots }) { const config = useGlobalConfig('table') + const common = useGlobalConfig('common') + const mergedPrefixCls = computed(() => `${common.prefixCls}-table`) + const locale = getLocale('table') const tags = useTags(props) const getRowKey = useGetRowKey(props, config) @@ -45,13 +49,16 @@ export default defineComponent({ const scrollContext = useScroll(props, stickyContext) const columnsContext = useColumns(props, config, scrollContext.scrollBarSizeOnFixedHolder) const sortableContext = useSortable(columnsContext.flattedColumns) + const filterableContext = useFilterables(columnsContext.flattedColumns) + const expandableContext = useExpandable(props, columnsContext.flattedColumns) const tableLayout = useTableLayout(props, columnsContext, scrollContext, stickyContext.isSticky) const { mergedPagination } = usePagination(props, config) - const expandableContext = useExpandable(props, columnsContext.flattedColumns) + const dataContext = useDataSource( props, getRowKey, sortableContext.activeSortable, + filterableContext.activeFilterables, expandableContext.expandedRowKeys, mergedPagination, ) @@ -59,6 +66,7 @@ export default defineComponent({ const context = { props, + mergedPrefixCls, slots, config, locale, @@ -67,6 +75,7 @@ export default defineComponent({ ...columnsContext, ...scrollContext, ...sortableContext, + ...filterableContext, ...stickyContext, tableLayout, mergedPagination, @@ -76,15 +85,20 @@ export default defineComponent({ } provide(TABLE_TOKEN, context) + expose({ scrollTo: scrollContext.scrollTo }) return () => { const header = <ɵHeader v-slots={slots} header={props.header} /> - const footer = renderFooter(slots) - const [paginationTop, paginationBottom] = renderPagination(mergedPagination.value, dataContext.filteredData.value) + const footer = renderFooter(slots, mergedPrefixCls.value) + const [paginationTop, paginationBottom] = renderPagination( + mergedPagination.value, + dataContext.filteredData.value, + mergedPrefixCls.value, + ) const children = [header, paginationTop, , paginationBottom, footer] const spinProps = covertSpinProps(props.spin) const spinWrapper = spinProps ? {children} : children - return
{spinWrapper}
+ return
{spinWrapper}
} }, }) diff --git a/packages/components/table/src/composables/useDataSource.ts b/packages/components/table/src/composables/useDataSource.ts index 91c36ba45..5a81236a6 100644 --- a/packages/components/table/src/composables/useDataSource.ts +++ b/packages/components/table/src/composables/useDataSource.ts @@ -6,6 +6,7 @@ */ import type { Key, TablePagination, TableProps } from '../types' +import type { Filterable } from './useFilterable' import type { GetRowKey } from './useGetRowKey' import type { ActiveSortable } from './useSortable' import type { ComputedRef, Ref } from 'vue' @@ -16,6 +17,7 @@ export function useDataSource( props: TableProps, getRowKey: ComputedRef, activeSortable: ActiveSortable, + activeFilterables: ComputedRef, expandedRowKeys: Ref, mergedPagination: ComputedRef, ): DataSourceContext { @@ -30,17 +32,15 @@ export function useDataSource( covertDataMap(mergedData.value, map) return map }) - // TODO - const filteredData = computed(() => mergedData.value) + + const filteredData = computed(() => filterData(mergedData.value, activeFilterables.value)) const sortedData = computed(() => { const { sorter, orderBy } = activeSortable if (sorter && orderBy) { - const oderFlag = orderBy === 'ascend' ? 1 : -1 - const tempData = filteredData.value.slice() - return tempData.sort((curr, next) => oderFlag * sorter(curr.record, next.record)) - } else { - return filteredData.value + return sortData(filteredData.value, sorter, orderBy) } + + return filteredData.value }) const paginatedData = computed(() => { const pagination = mergedPagination.value @@ -66,11 +66,9 @@ export function useDataSource( const flattedData = computed(() => { const expandedKeys = expandedRowKeys.value if (expandedKeys.length > 0) { - const data: FlattedData[] = [] - paginatedData.value.forEach(item => data.push(...flatData(item, 0, expandedKeys))) - return data + return flatData(paginatedData.value, expandedKeys, 0) } - return paginatedData.value.map(item => ({ ...item, expanded: false, level: 0 })) + return paginatedData.value.map((item, idx) => ({ ...item, expanded: false, level: 0, rowIndex: idx })) }) return { filteredData, flattedData, mergedMap, paginatedMap } @@ -116,16 +114,53 @@ function covertDataMap(mergedData: MergedData[], map: Map) { }) } +function sortData( + mergedData: MergedData[], + sorter: (curr: unknown, next: unknown) => number, + orderBy: 'ascend' | 'descend', +) { + const tempData = mergedData.slice() + const orderFlag = orderBy === 'ascend' ? 1 : -1 + + tempData.forEach(item => { + if (item.children?.length && item.children.length > 0) { + item.children = sortData(item.children, sorter, orderBy) + } + }) + + return tempData.sort((curr, next) => orderFlag * sorter(curr.record, next.record)) +} + +function filterData(mergedData: MergedData[], activeFilterables: Filterable[]): MergedData[] { + return mergedData + .map(item => { + const newItem = { ...item } + + const itemValid = activeFilterables.every(filterable => filterable.filter(filterable.filterBy ?? [], item.record)) + + if (item.children?.length && item.children.length > 0) { + newItem.children = filterData(item.children, activeFilterables) + } + + return (newItem.children && newItem.children.length > 0) || itemValid ? newItem : null + }) + .filter(item => !!item) as MergedData[] +} + // TODO: performance optimization // when virtual scrolling is enabled, this do not need to traverse all nodes -function flatData(data: MergedData, level: number, expandedRowKeys: Key[]) { - const { children, parentKey, record, rowKey } = data - const expanded = expandedRowKeys.includes(rowKey) - const result: FlattedData[] = [{ children, parentKey, record, rowKey, level, expanded }] +function flatData(mergedData: MergedData[], expandedRowKeys: Key[], level = 0) { + return mergedData.reduce((result, item) => { + const { children, parentKey, record, rowKey } = item + const expanded = expandedRowKeys.includes(rowKey) - if (expanded && children) { - children.forEach(subRecord => result.push(...flatData(subRecord, level + 1, expandedRowKeys))) - } + result.push({ children, parentKey, record, rowKey, level, expanded }) - return result + if (expanded && item.children) { + const childrenFlatData = flatData(item.children, expandedRowKeys, level + 1) + result.push(...childrenFlatData) + } + + return result + }, [] as FlattedData[]) } diff --git a/packages/components/table/src/composables/useExpandable.ts b/packages/components/table/src/composables/useExpandable.ts index 9a2579263..3165bdf18 100644 --- a/packages/components/table/src/composables/useExpandable.ts +++ b/packages/components/table/src/composables/useExpandable.ts @@ -7,6 +7,7 @@ import type { Key, TableProps } from '../types' import type { TableColumnMerged, TableColumnMergedExpandable } from './useColumns' +import type { MergedData } from './useDataSource' import type { ComputedRef } from 'vue' import { computed } from 'vue' @@ -35,11 +36,28 @@ export function useExpandable(props: TableProps, flattedColumns: ComputedRef { + if (!expandable.value) { + return true + } + + const { disabled, customExpand } = expandable.value + const { record } = data + + if (disabled?.(record)) { + return true + } + + return !(customExpand || (data.children && data.children.length > 0)) + } + + return { expandable, expandedRowKeys, setExpandedRowKeys, checkExpandDisabled, handleExpandChange } } export interface ExpandableContext { expandable: ComputedRef expandedRowKeys: ComputedRef + setExpandedRowKeys: (value: Key[]) => void + checkExpandDisabled: (data: MergedData) => boolean handleExpandChange: (key: Key, record: unknown) => void } diff --git a/packages/components/table/src/composables/useFilterable.ts b/packages/components/table/src/composables/useFilterable.ts new file mode 100644 index 000000000..3326ee060 --- /dev/null +++ b/packages/components/table/src/composables/useFilterable.ts @@ -0,0 +1,55 @@ +/** + * @license + * + * Use of this source code is governed by an MIT-style license that can be + * found in the LICENSE file at https://github.com/IDuxFE/idux/blob/main/LICENSE + */ + +import type { Key, TableColumnFilterable } from '../types' +import type { TableColumnMerged } from './useColumns' +import type { ComputedRef } from 'vue' + +import { computed, reactive } from 'vue' + +import { callEmit } from '@idux/cdk/utils' + +export function useFilterables(flattedColumns: ComputedRef): FilterableContext { + const tempFilterByMap = reactive(new Map()) + + const filterables = computed(() => + flattedColumns.value + .filter(column => !!column.filterable) + .map(column => { + const { filterable, key } = column + + const onChange = (value: unknown[]) => { + tempFilterByMap.set(key, value) + callEmit(filterable!.onChange, value) + } + + return { + key: column.key, + filters: filterable!.filters, + filterBy: filterable!.filterBy ?? tempFilterByMap.get(key), + filter: filterable!.filter, + onChange, + } + }), + ) + + const activeFilterables = computed(() => filterables.value.filter(f => f.filterBy && f.filterBy.length > 0)) + + return { + filterables, + activeFilterables, + } +} + +export interface Filterable extends TableColumnFilterable { + key: Key +} + +export interface FilterableContext { + filterables: ComputedRef[]> + activeFilterables: ComputedRef[]> +} diff --git a/packages/components/table/src/composables/useScroll.ts b/packages/components/table/src/composables/useScroll.ts index a36f8a8a3..c251ac55c 100644 --- a/packages/components/table/src/composables/useScroll.ts +++ b/packages/components/table/src/composables/useScroll.ts @@ -7,6 +7,7 @@ import type { TableProps } from '../types' import type { StickyContext } from './useSticky' +import type { VirtualScrollInstance, VirtualScrollToFn } from '@idux/cdk/scroll' import type { ComputedRef, Ref } from 'vue' import { computed, onBeforeUnmount, ref } from 'vue' @@ -27,11 +28,18 @@ export function useScroll(props: TableProps, { isSticky, stickyScrollLeft }: Sti isSticky.value ? 0 : scrollVertical.value ? scrollBarSize.value : 0, ) + const scrollTo: VirtualScrollToFn = options => { + if (props.virtual) { + return (scrollBodyRef.value as unknown as VirtualScrollInstance)?.scrollTo(options) + } + } + return { scrollHeadRef, scrollBodyRef, scrollFootRef, handleScroll, + scrollTo, pingedStart, pingedEnd, scrollX, @@ -48,6 +56,7 @@ export interface ScrollContext { scrollBodyRef: Ref scrollFootRef: Ref handleScroll: (evt?: Event, scrollLeft?: number) => void + scrollTo: VirtualScrollToFn pingedStart: Ref pingedEnd: Ref scrollX: ComputedRef diff --git a/packages/components/table/src/composables/useSelectable.ts b/packages/components/table/src/composables/useSelectable.ts index a28c16a1a..e4dadfdc4 100644 --- a/packages/components/table/src/composables/useSelectable.ts +++ b/packages/components/table/src/composables/useSelectable.ts @@ -13,7 +13,7 @@ import type { ComputedRef } from 'vue' import { computed } from 'vue' -import { isString } from 'lodash-es' +import { isNil, isString } from 'lodash-es' import { callEmit, useControlledProp } from '@idux/cdk/utils' @@ -50,17 +50,14 @@ export function useSelectable( const dataMap = mergedMap.value selectedKeys.forEach(key => { const { parentKey } = dataMap.get(key) || {} - if (parentKey) { + if (!isNil(parentKey)) { let parent = dataMap.get(parentKey) if (parent && !selectedKeys.includes(parent.rowKey)) { - if (!disabledRowKeys.includes(parentKey)) { - indeterminateKeySet.add(parentKey) - } - while (parent?.parentKey) { - if (!disabledRowKeys.includes(parent.parentKey)) { - indeterminateKeySet.add(parent.parentKey) + while (parent && !isNil(parent?.rowKey)) { + if (!disabledRowKeys.includes(parent.rowKey)) { + indeterminateKeySet.add(parent.rowKey) } - parent = dataMap.get(parent.parentKey) + parent = !isNil(parent.parentKey) ? dataMap.get(parent.parentKey) : undefined } } } @@ -125,9 +122,10 @@ export function useSelectable( tempRowKeys = tempRowKeys.filter(key => !parentKeys.includes(key) && !childrenKeys.includes(key)) } else { tempRowKeys.push(key) - setParentSelected(dataMap, currData, tempRowKeys, disabledRowKeys) tempRowKeys.push(...childrenKeys) } + + setParentSelected(dataMap, currData, tempRowKeys, disabledRowKeys) } else { tempRowKeys = selected ? [] : [key] } @@ -302,14 +300,18 @@ function setParentSelected( disabledRowKeys: Key[], ) { let parentSelected = true - while (parentSelected && currData?.parentKey) { + while (parentSelected && currData && !isNil(currData.parentKey)) { const parent = dataMap.get(currData.parentKey) if (parent && !disabledRowKeys.includes(currData.parentKey)) { parentSelected = parent.children!.every( item => disabledRowKeys.includes(item.rowKey) || tempRowKeys.includes(item.rowKey), ) + + const parentKeyIdx = tempRowKeys.findIndex(key => key === currData!.parentKey) if (parentSelected) { - tempRowKeys.push(currData.parentKey) + parentKeyIdx < 0 && tempRowKeys.push(currData.parentKey) + } else { + parentKeyIdx > -1 && tempRowKeys.splice(parentKeyIdx, 1) } } currData = parent diff --git a/packages/components/table/src/composables/useSortable.ts b/packages/components/table/src/composables/useSortable.ts index 0797b75cf..d9aeb49e0 100644 --- a/packages/components/table/src/composables/useSortable.ts +++ b/packages/components/table/src/composables/useSortable.ts @@ -9,27 +9,38 @@ import type { Key, TableColumnSortOrder, TableColumnSortable } from '../types' import type { TableColumnMerged } from './useColumns' import type { ComputedRef } from 'vue' -import { computed, reactive, watchEffect } from 'vue' +import { computed, reactive, ref, watch } from 'vue' import { callEmit } from '@idux/cdk/utils' export function useSortable(flattedColumns: ComputedRef): SortableContext { - const activeSortable = reactive({}) const activeSortColumn = computed(() => flattedColumns.value.find(column => column.sortable?.orderBy)) - watchEffect(() => { + const orderByRef = ref<'descend' | 'ascend' | undefined>(activeSortColumn.value?.sortable?.orderBy) + + const tempOrderByRef = ref<'descend' | 'ascend' | undefined>() + const activeSortable = reactive({ + orderBy: orderByRef.value, + }) + + watch(activeSortColumn, () => { const sortColumn = activeSortColumn.value if (sortColumn) { activeSortable.key = sortColumn.key - activeSortable.orderBy = sortColumn.sortable!.orderBy! + orderByRef.value = sortColumn.sortable!.orderBy! activeSortable.sorter = sortColumn.sortable!.sorter } else { activeSortable.key = undefined - activeSortable.orderBy = undefined + orderByRef.value = undefined + tempOrderByRef.value = undefined activeSortable.sorter = undefined } }) + watch([orderByRef, tempOrderByRef], () => { + activeSortable.orderBy = orderByRef.value ?? tempOrderByRef.value + }) + const handleSort = (key: Key, sortable: TableColumnSortable) => { const { orders, sorter, onChange } = sortable @@ -40,9 +51,9 @@ export function useSortable(flattedColumns: ComputedRef): S activeSortable.key = key activeSortable.sorter = sorter } - activeSortable.orderBy = orderBy - callEmit(onChange, activeSortable.orderBy) + tempOrderByRef.value = orderBy + callEmit(onChange, orderBy) } return { activeSortable, handleSort } diff --git a/packages/components/table/src/main/ColGroup.tsx b/packages/components/table/src/main/ColGroup.tsx index e9301b047..53825c8d8 100644 --- a/packages/components/table/src/main/ColGroup.tsx +++ b/packages/components/table/src/main/ColGroup.tsx @@ -24,6 +24,7 @@ export default defineComponent({ columnWidths, columnWidthsWithScrollBar, mergedSelectableOptions, + mergedPrefixCls, } = inject(TABLE_TOKEN)! const isRender = computed(() => flattedColumns.value.some(column => !!column.width || 'type' in column)) return () => { @@ -33,12 +34,12 @@ export default defineComponent({ if (ixFixedHolder) { const widths = columnWidthsWithScrollBar.value children = flattedColumnsWithScrollBar.value.map((column, colIndex) => - renderCol(mergedSelectableOptions, column as TableColumnMerged, widths[colIndex]), + renderCol(mergedPrefixCls, mergedSelectableOptions, column as TableColumnMerged, widths[colIndex]), ) } else if (isRender.value) { const widths = columnWidths.value children = flattedColumns.value.map((column, colIndex) => - renderCol(mergedSelectableOptions, column, widths[colIndex]), + renderCol(mergedPrefixCls, mergedSelectableOptions, column, widths[colIndex]), ) } return {children} @@ -47,19 +48,20 @@ export default defineComponent({ }) function renderCol( + mergedPrefixCls: ComputedRef, mergedSelectableOptions: ComputedRef, column: TableColumnMerged, width?: number, ) { - let className: string | undefined - if ('type' in column) { - const type = column.type - className = `ix-table-col-${type}` - if (type === 'selectable' && mergedSelectableOptions.value) { - className += ' ix-table-selectable-with-options' - } + const prefixCls = mergedPrefixCls.value + const type = 'type' in column && column.type + + const classess = { + [`${prefixCls}-col-${type}`]: !!type, + [`${prefixCls}-selectable-with-options`]: type === 'selectable' && mergedSelectableOptions.value, } + const mergedWidth = width ?? column.width const style = mergedWidth ? { width: convertCssPixel(mergedWidth) } : undefined - return + return } diff --git a/packages/components/table/src/main/FixedHolder.tsx b/packages/components/table/src/main/FixedHolder.tsx index 1d29fe168..645e70c0b 100644 --- a/packages/components/table/src/main/FixedHolder.tsx +++ b/packages/components/table/src/main/FixedHolder.tsx @@ -16,7 +16,7 @@ import ColGroup from './ColGroup' export default defineComponent({ setup(_, { slots }) { - const { scrollHeadRef, handleScroll, scrollX, flattedData, isSticky, mergedSticky, columnWidths } = + const { mergedPrefixCls, scrollHeadRef, handleScroll, scrollX, flattedData, isSticky, mergedSticky, columnWidths } = inject(TABLE_TOKEN)! useScrollEvents(scrollHeadRef, handleScroll) @@ -24,9 +24,10 @@ export default defineComponent({ const isMaxContent = computed(() => scrollX.value === 'max-content') const hasData = computed(() => flattedData.value.length > 0) const classes = computed(() => { + const prefixCls = mergedPrefixCls.value return { - 'ix-table-fixed-holder': true, - 'ix-table-sticky-holder': isSticky.value, + [`${prefixCls}-fixed-holder`]: true, + [`${prefixCls}-sticky-holder`]: isSticky.value, } }) const style = computed(() => { diff --git a/packages/components/table/src/main/MainTable.tsx b/packages/components/table/src/main/MainTable.tsx index 1e1d95f1c..1eb447ad0 100644 --- a/packages/components/table/src/main/MainTable.tsx +++ b/packages/components/table/src/main/MainTable.tsx @@ -30,6 +30,7 @@ export default defineComponent({ setup() { const { props, + mergedPrefixCls, config, changeColumnWidth, flattedData, @@ -95,7 +96,7 @@ export default defineComponent({ onBeforeUnmount(() => offResize(mainTableRef.value, handleWrapperResize)) const classes = computed(() => { - const prefixCls = 'ix-table' + const prefixCls = mergedPrefixCls.value const { borderless = config.borderless, size = config.size } = props return { [`${prefixCls}-container`]: true, @@ -141,6 +142,8 @@ export default defineComponent({ const TableTag = tableTag.value as any let children: VNodeTypes + const prefixCls = mergedPrefixCls.value + if (scrollVertical.value || isSticky.value) { const tableHead = props.headless ? null : ( @@ -152,7 +155,7 @@ export default defineComponent({ if (props.virtual) { const itemRender: VirtualItemRenderFn = ({ item, index }) => { const { expanded, level, record, rowKey } = item - const rowProps = { key: rowKey, expanded, level, record, rowIndex: index, rowKey } + const rowProps = { key: rowKey, expanded, level, record, rowData: item, rowIndex: index, rowKey } return } const contentRender: VirtualContentRenderFn = children => { @@ -190,7 +193,7 @@ export default defineComponent({ ) } else { tableBody = ( -
+
@@ -207,7 +210,7 @@ export default defineComponent({ children = [tableHead, tableBody, tableFoot, sticky] } else { children = ( -
+
{props.headless ? null : } diff --git a/packages/components/table/src/main/StickyScroll.tsx b/packages/components/table/src/main/StickyScroll.tsx index 46db448b3..5f30b7852 100644 --- a/packages/components/table/src/main/StickyScroll.tsx +++ b/packages/components/table/src/main/StickyScroll.tsx @@ -18,7 +18,7 @@ import { TABLE_TOKEN } from '../token' export default defineComponent({ setup() { - const { scrollBodyRef, handleScroll, mergedSticky, stickyScrollLeft } = inject(TABLE_TOKEN)! + const { mergedPrefixCls, scrollBodyRef, handleScroll, mergedSticky, stickyScrollLeft } = inject(TABLE_TOKEN)! const isShow = ref(false) const isActive = ref(false) @@ -54,9 +54,10 @@ export default defineComponent({ }) const scrollBarClasses = computed(() => { + const prefixCls = mergedPrefixCls.value return { - 'ix-table-sticky-scroll-bar': true, - 'ix-table-sticky-scroll-bar-active': isActive.value, + [`${prefixCls}-sticky-scroll-bar`]: true, + [`${prefixCls}-sticky-scroll-bar-active`]: isActive.value, } }) @@ -154,7 +155,7 @@ export default defineComponent({ } return ( -
+
) diff --git a/packages/components/table/src/main/body/Body.tsx b/packages/components/table/src/main/body/Body.tsx index 7c4d48fd1..f2cd7d319 100644 --- a/packages/components/table/src/main/body/Body.tsx +++ b/packages/components/table/src/main/body/Body.tsx @@ -49,7 +49,7 @@ export default defineComponent({ } else { data.forEach((item, rowIndex) => { const { expanded, level, record, rowKey } = item - const rowProps = { key: rowKey, expanded, level, record, rowIndex, rowKey } + const rowProps = { key: rowKey, expanded, level, record, rowData: item, rowIndex, rowKey } children.push() }) } diff --git a/packages/components/table/src/main/body/BodyCell.tsx b/packages/components/table/src/main/body/BodyCell.tsx index a77c4c0ce..0df42924e 100644 --- a/packages/components/table/src/main/body/BodyCell.tsx +++ b/packages/components/table/src/main/body/BodyCell.tsx @@ -33,18 +33,28 @@ type BodyColumn = TableColumnMergedExtra & { export default defineComponent({ props: tableBodyCellProps, setup(props) { - const { slots, activeSortable, fixedColumnKeys, columnOffsets, isSticky, expandable, selectable, bodyColTag } = - inject(TABLE_TOKEN)! + const { + mergedPrefixCls, + slots, + activeSortable, + fixedColumnKeys, + columnOffsets, + isSticky, + expandable, + selectable, + bodyColTag, + } = inject(TABLE_TOKEN)! const dataValue = useDataValue(props) const cellProps = computed(() => { const { key, fixed, align, ellipsis } = props.column as BodyColumn - const prefixCls = 'ix-table' + const prefixCls = mergedPrefixCls.value let classes: Record = { [`${prefixCls}-cell-sorted`]: activeSortable.key === key && !!activeSortable.orderBy, [`${prefixCls}-align-${align}`]: !!align, [`${prefixCls}-ellipsis`]: !!ellipsis, } + let style: StyleValue | undefined if (fixed) { const { lastStartKey, firstEndKey } = fixedColumnKeys.value @@ -75,22 +85,23 @@ export default defineComponent({ return () => { const { type, additional } = props.column as BodyColumn - let children: VNodeTypes | null + let children: VNodeTypes | null = null let title: string | undefined - if (type === 'expandable') { - children = props.disabled ? null : renderExpandableChildren(props, slots, expandable) - } else if (type === 'selectable') { + + if (type === 'selectable') { children = renderSelectableChildren(props, selectable, handleClick) } else { const { ellipsis } = props.column const text = dataValue.value - children = renderChildren(props, slots, text) - title = getColTitle(ellipsis, children, text) + children = text ? renderChildren(props, slots, text) : null + title = children ? getColTitle(ellipsis, children, text) : undefined } + // eslint-disable-next-line @typescript-eslint/no-explicit-any const BodyColTag = bodyColTag.value as any return ( + {type === 'expandable' ? renderExpandableChildren(props, slots, expandable, mergedPrefixCls.value) : null} {children} ) @@ -102,6 +113,10 @@ function useDataValue(props: TableBodyCellProps) { return computed(() => { const { column, record } = props const dataKeys = convertArray(column.dataKey) + if (dataKeys.length <= 0) { + return undefined + } + let value = record for (let index = 0; index < dataKeys.length; index++) { if (!value) { @@ -130,18 +145,31 @@ function renderExpandableChildren( props: TableBodyCellProps, slots: Slots, expandable: ComputedRef, + prefixCls: string, ) { - const { icon, customIcon } = expandable.value! - const record = props.record - const expanded = props.expanded! + const { icon, customIcon, indent } = expandable.value! + const { record, expanded, level, disabled } = props const onExpand = props.handleExpend! - if (isFunction(customIcon)) { - return customIcon({ expanded, record, onExpand }) + const style = { + marginLeft: indent ? convertCssPixel(level * indent) : undefined, } - if (isString(customIcon) && slots[customIcon]) { - return slots[customIcon]!({ expanded, record, onExpand }) + + let iconNode: VNodeTypes | null + if (disabled) { + iconNode = null + } else if (isFunction(customIcon)) { + iconNode = customIcon({ expanded: !!expanded, record, onExpand }) + } else if (isString(customIcon) && slots[customIcon]) { + iconNode = slots[customIcon]!({ expanded, record, onExpand }) + } else { + iconNode = } - return + + return ( + + ) } function renderSelectableChildren( diff --git a/packages/components/table/src/main/body/BodyRow.tsx b/packages/components/table/src/main/body/BodyRow.tsx index 49be9b289..029e3cc2b 100644 --- a/packages/components/table/src/main/body/BodyRow.tsx +++ b/packages/components/table/src/main/body/BodyRow.tsx @@ -17,6 +17,7 @@ import { computed, defineComponent, inject } from 'vue' import { isFunction, isString } from 'lodash-es' +import { FlattedData } from '../../composables/useDataSource' import { TABLE_TOKEN } from '../../token' import { tableBodyRowProps } from '../../types' import BodyCell from './BodyCell' @@ -27,10 +28,12 @@ export default defineComponent({ setup(props) { const { props: tableProps, + mergedPrefixCls, slots, flattedColumns, expandable, handleExpandChange, + checkExpandDisabled, selectable, selectedRowKeys, indeterminateRowKeys, @@ -39,10 +42,10 @@ export default defineComponent({ bodyRowTag, } = inject(TABLE_TOKEN)! - const { expendDisabled, handleExpend, selectDisabled, handleSelect, clickEvents } = useEvents( + const { expandDisabled, handleExpend, selectDisabled, handleSelect, clickEvents } = useEvents( props, - tableProps, expandable, + checkExpandDisabled, handleExpandChange, selectable, handleSelectChange, @@ -52,12 +55,12 @@ export default defineComponent({ const isSelected = computed(() => selectedRowKeys.value.includes(props.rowKey)) const isIndeterminate = computed(() => indeterminateRowKeys.value.includes(props.rowKey)) - const classes = useClasses(props, tableProps, isSelected) + const classes = useClasses(props, tableProps, isSelected, mergedPrefixCls) return () => { const children = renderChildren( props, flattedColumns, - expendDisabled, + expandDisabled.value, handleExpend, isSelected, isIndeterminate, @@ -81,10 +84,15 @@ export default defineComponent({ }, }) -function useClasses(props: TableBodyRowProps, tableProps: TableProps, isSelected: ComputedRef) { +function useClasses( + props: TableBodyRowProps, + tableProps: TableProps, + isSelected: ComputedRef, + mergedPrefixCls: ComputedRef, +) { const rowClassName = computed(() => tableProps.rowClassName?.(props.record, props.rowIndex)) return computed(() => { - const prefixCls = 'ix-table-row' + const prefixCls = `${mergedPrefixCls.value}` const { level, expanded } = props const computeRowClassName = rowClassName.value return { @@ -98,14 +106,14 @@ function useClasses(props: TableBodyRowProps, tableProps: TableProps, isSelected function useEvents( props: TableBodyRowProps, - tableProps: TableProps, expandable: ComputedRef, + checkExpandDisabled: (data: FlattedData) => boolean, handleExpandChange: (key: Key, record: unknown) => void, selectable: ComputedRef, handleSelectChange: (key: Key, record: unknown) => void, currentPageRowKeys: ComputedRef<{ enabledRowKeys: Key[]; disabledRowKeys: Key[] }>, ) { - const expendDisabled = useExpandDisabled(props, tableProps, expandable) + const expandDisabled = computed(() => checkExpandDisabled(props.rowData)) const expendTrigger = computed(() => expandable.value?.trigger) const handleExpend = () => { const { rowKey, record } = props @@ -120,7 +128,7 @@ function useEvents( } const handleClick = () => { - if (expendTrigger.value === 'click' && !expendDisabled.value) { + if (expendTrigger.value === 'click' && !expandDisabled.value) { handleExpend() } if (selectTrigger.value === 'click' && !selectDisabled.value) { @@ -129,7 +137,7 @@ function useEvents( } const handleDblclick = () => { - if (expendTrigger.value === 'dblclick' && !expendDisabled.value) { + if (expendTrigger.value === 'dblclick' && !expandDisabled.value) { handleExpend() } if (selectTrigger.value === 'dblclick' && !selectDisabled.value) { @@ -144,32 +152,13 @@ function useEvents( return { onClick, onDblclick } }) - return { expendDisabled, handleExpend, selectDisabled, handleSelect, clickEvents } -} - -function useExpandDisabled( - props: TableBodyRowProps, - tableProps: TableProps, - expandable: ComputedRef, -) { - return computed(() => { - const column = expandable.value - if (!column) { - return true - } - const { disabled, customExpand } = column - const { record, rowIndex } = props - if (disabled?.(record, rowIndex)) { - return true - } - return !(customExpand || record[tableProps.childrenKey]?.length > 0) - }) + return { expandDisabled, handleExpend, selectDisabled, handleSelect, clickEvents } } function renderChildren( props: TableBodyRowProps, flattedColumns: ComputedRef, - expendDisabled: ComputedRef, + expandDisabled: boolean, handleExpend: () => void, isSelected: ComputedRef, isIndeterminate: ComputedRef, @@ -177,7 +166,7 @@ function renderChildren( handleSelect: () => void, ) { const children: VNodeTypes[] = [] - const { rowIndex, record } = props + const { rowIndex, record, level } = props flattedColumns.value.forEach((column, colIndex) => { const { type, colSpan: getColSpan, rowSpan: getRowSpan, key } = column const colSpan = getColSpan?.(record, rowIndex) @@ -193,11 +182,12 @@ function renderChildren( colIndex, record, column, + level, key, } if (type === 'expandable') { colProps.expanded = props.expanded - colProps.disabled = expendDisabled.value + colProps.disabled = expandDisabled colProps.handleExpend = handleExpend } else if (type === 'selectable') { colProps.selected = isSelected.value diff --git a/packages/components/table/src/main/body/MeasureRow.tsx b/packages/components/table/src/main/body/MeasureRow.tsx index 4bc849dc7..375df413e 100644 --- a/packages/components/table/src/main/body/MeasureRow.tsx +++ b/packages/components/table/src/main/body/MeasureRow.tsx @@ -12,7 +12,7 @@ import MeasureCell from './MeasureCell' export default defineComponent({ setup() { - const { flattedColumns } = inject(TABLE_TOKEN)! + const { mergedPrefixCls, flattedColumns } = inject(TABLE_TOKEN)! const { changeColumnWidth } = inject(tableBodyToken)! return () => { const children = flattedColumns.value.map(column => { @@ -21,7 +21,7 @@ export default defineComponent({ return }) return ( - + {children} ) diff --git a/packages/components/table/src/main/head-trigger/HeadCellFilterableTrigger.tsx b/packages/components/table/src/main/head-trigger/HeadCellFilterableTrigger.tsx new file mode 100644 index 000000000..11bdcc284 --- /dev/null +++ b/packages/components/table/src/main/head-trigger/HeadCellFilterableTrigger.tsx @@ -0,0 +1,56 @@ +/** + * @license + * + * Use of this source code is governed by an MIT-style license that can be + * found in the LICENSE file at https://github.com/IDuxFE/idux/blob/main/LICENSE + */ + +import type { DropdownProps } from '@idux/components/dropdown' + +import { computed, defineComponent, inject } from 'vue' + +import { isArray } from 'lodash-es' + +import { IxDropdown } from '@idux/components/dropdown' +import { IxIcon } from '@idux/components/icon' + +import FilterDropdown from '../../other/FilterDropdown' +import { TABLE_TOKEN } from '../../token' +import { tableHeadCellFilterableTriggerProps } from '../../types' + +export default defineComponent({ + props: tableHeadCellFilterableTriggerProps, + setup(props) { + const { mergedPrefixCls } = inject(TABLE_TOKEN)! + const classes = computed(() => { + const prefixCls = `${mergedPrefixCls.value}-filterable-trigger` + const { filterable } = props + + return { + [prefixCls]: true, + [`${prefixCls}-active`]: isArray(filterable.filterBy) && filterable.filterBy.length > 0, + } + }) + + const renderTrigger = () => ( + evt.stopPropagation()}> + + + ) + const renderDropDown = () => + + return () => { + const { filterable } = props + if (!filterable || filterable.filters.length <= 0) { + return null + } + + const dropdownProps: DropdownProps = { + trigger: 'click', + placement: 'bottomStart', + } + + return + } + }, +}) diff --git a/packages/components/table/src/main/head/HeadCellSortable.tsx b/packages/components/table/src/main/head-trigger/HeadCellSortableTrigger.tsx similarity index 60% rename from packages/components/table/src/main/head/HeadCellSortable.tsx rename to packages/components/table/src/main/head-trigger/HeadCellSortableTrigger.tsx index 10ca23e74..3afe2fea0 100644 --- a/packages/components/table/src/main/head/HeadCellSortable.tsx +++ b/packages/components/table/src/main/head-trigger/HeadCellSortableTrigger.tsx @@ -5,11 +5,13 @@ * found in the LICENSE file at https://github.com/IDuxFE/idux/blob/main/LICENSE */ +import type { TableColumnSortOrder } from '@idux/components/table' +import type { ComputedRef } from 'vue' + import { defineComponent, inject } from 'vue' import { TableLocale } from '@idux/components/i18n' import { IxIcon } from '@idux/components/icon' -import { TableColumnSortOrder } from '@idux/components/table' import { IxTooltip } from '@idux/components/tooltip' import { TABLE_TOKEN } from '../../token' @@ -17,20 +19,15 @@ import { TABLE_TOKEN } from '../../token' export default defineComponent({ // eslint-disable-next-line vue/require-prop-types props: ['activeOrderBy', 'sortable'], - setup(props, { slots }) { - const { locale } = inject(TABLE_TOKEN)! + setup(props) { + const { locale, mergedPrefixCls } = inject(TABLE_TOKEN)! return () => { const { activeOrderBy, sortable } = props const { orders, nextTooltip } = sortable const title = nextTooltip ? getNextTooltipTitle(locale.value, orders!, activeOrderBy) : undefined - const sortableNode = ( - - {slots.default!()} - {renderSortTrigger(orders!, activeOrderBy)} - - ) - return title ? {sortableNode} : sortableNode + const sortableTriggerNode = renderSortTrigger(mergedPrefixCls, orders!, activeOrderBy) + return title ? {sortableTriggerNode} : sortableTriggerNode } }, }) @@ -48,15 +45,24 @@ function getNextTooltipTitle( return nextOrderBy === 'ascend' ? sortAsc : sortDesc } -function renderSortTrigger(orders: TableColumnSortOrder[], activeOrderBy?: TableColumnSortOrder) { +function renderSortTrigger( + mergedPrefixCls: ComputedRef, + orders: TableColumnSortOrder[], + activeOrderBy?: TableColumnSortOrder, +) { + const prefixCls = mergedPrefixCls.value + const upNode = orders!.includes('ascend') ? ( - + ) : undefined const downNode = orders!.includes('descend') ? ( - + ) : undefined return ( - + {upNode} {downNode} diff --git a/packages/components/table/src/main/head/HeadCell.tsx b/packages/components/table/src/main/head/HeadCell.tsx index 69d6d5e50..8cda415f5 100644 --- a/packages/components/table/src/main/head/HeadCell.tsx +++ b/packages/components/table/src/main/head/HeadCell.tsx @@ -6,7 +6,7 @@ */ import type { TableColumnMergedExtra } from '../../composables/useColumns' -import type { TableColumnTitleFn } from '../../types' +import type { TableColumnFilterable, TableColumnSortable, TableColumnTitleFn } from '../../types' import type { Slots, StyleValue, VNodeTypes } from 'vue' import { computed, defineComponent, inject } from 'vue' @@ -18,8 +18,9 @@ import { convertCssPixel } from '@idux/cdk/utils' import { TABLE_TOKEN } from '../../token' import { tableHeadCellProps } from '../../types' import { getColTitle } from '../../utils' +import HeadCellFilterableTrigger from '../head-trigger/HeadCellFilterableTrigger' +import HeadCellSortableTrigger from '../head-trigger/HeadCellSortableTrigger' import HeadCellSelectable from './HeadCellSelectable' -import HeadCellSortable from './HeadCellSortable' type HeadColumn = TableColumnMergedExtra & { type: 'selectable' | 'expandable' | 'scroll-bar' | undefined @@ -29,11 +30,18 @@ type HeadColumn = TableColumnMergedExtra & { export default defineComponent({ props: tableHeadCellProps, setup(props) { - const { slots, fixedColumnKeys, columnOffsetsWithScrollBar, isSticky, activeSortable, handleSort, headColTag } = - inject(TABLE_TOKEN)! - const key = computed(() => props.column.key) - const isSorted = computed(() => activeSortable.key === key.value && !!activeSortable.orderBy) - const activeSortOrderBy = computed(() => (isSorted.value ? activeSortable.orderBy : undefined)) + const { + mergedPrefixCls, + slots, + fixedColumnKeys, + columnOffsetsWithScrollBar, + isSticky, + handleSort, + headColTag, + activeSortable, + filterables, + } = inject(TABLE_TOKEN)! + const onClick = () => { const { key, sortable } = props.column if (sortable) { @@ -45,11 +53,10 @@ export default defineComponent({ const { type, align, hasChildren, fixed, key, colStart, colEnd, sortable, titleColSpan, titleRowSpan } = props.column as HeadColumn - const prefixCls = 'ix-table' + const prefixCls = mergedPrefixCls.value let classes: Record = { [`${prefixCls}-cell-${type}`]: !!type, [`${prefixCls}-cell-sortable`]: !!sortable, - [`${prefixCls}-cell-sorted`]: isSorted.value, [`${prefixCls}-align-${align}`]: !hasChildren && !!align, [`${prefixCls}-align-center`]: hasChildren, } @@ -82,34 +89,46 @@ export default defineComponent({ } }) + const sortable = computed(() => props.column.sortable) + const activeSortOrderBy = computed(() => + activeSortable.key === props.column.key && !!activeSortable.orderBy ? activeSortable.orderBy : undefined, + ) + const filterable = computed(() => filterables.value.find(f => f.key === props.column.key)) + return () => { const { type, additional } = props.column as HeadColumn + const prefixCls = mergedPrefixCls.value + let _title: string | undefined let children: VNodeTypes | undefined - if (type === 'expandable' || type === 'scroll-bar') { + let iconTriggers: (VNodeTypes | null)[] | undefined + if (type === 'scroll-bar') { children = undefined } else if (type === 'selectable') { children = } else { - const { title, customTitle, ellipsis, sortable } = props.column as HeadColumn + const { title, customTitle, ellipsis } = props.column as HeadColumn children = renderChildren(title, customTitle, slots) _title = getColTitle(ellipsis, children!, title) - if (ellipsis || sortable) { - children = {children} - if (sortable) { - children = ( - - {children} - - ) - } + + iconTriggers = [ + renderSortableTrigger(sortable.value, activeSortOrderBy.value), + renderFilterableTrigger(filterable.value), + ] + + if (ellipsis || iconTriggers.some(trigger => !!trigger)) { + children = {children} } } + // eslint-disable-next-line @typescript-eslint/no-explicit-any const HeadColTag = headColTag.value as any return ( - {children} + + {children} + {iconTriggers} + ) } @@ -125,3 +144,22 @@ function renderChildren(title: string | undefined, customTitle: string | TableCo } return children } + +function renderSortableTrigger( + sortable: TableColumnSortable | undefined, + activeSortOrderBy: 'descend' | 'ascend' | undefined, +): VNodeTypes | null { + if (!sortable) { + return null + } + + return +} + +function renderFilterableTrigger(filterable: TableColumnFilterable | undefined): VNodeTypes | null { + if (!filterable) { + return null + } + + return +} diff --git a/packages/components/table/src/main/head/HeadCellSelectable.tsx b/packages/components/table/src/main/head/HeadCellSelectable.tsx index 791a755b3..d77699d34 100644 --- a/packages/components/table/src/main/head/HeadCellSelectable.tsx +++ b/packages/components/table/src/main/head/HeadCellSelectable.tsx @@ -5,10 +5,11 @@ * found in the LICENSE file at https://github.com/IDuxFE/idux/blob/main/LICENSE */ -import type { VNodeTypes } from 'vue' +import type { DropdownProps } from '@idux/components/dropdown' import { computed, defineComponent, inject } from 'vue' +import { useState } from '@idux/cdk/utils' import { IxCheckbox } from '@idux/components/checkbox' import { IxDropdown } from '@idux/components/dropdown' import { IxIcon } from '@idux/components/icon' @@ -19,6 +20,7 @@ import { TABLE_TOKEN } from '../../token' export default defineComponent({ setup() { const { + mergedPrefixCls, paginatedMap, selectable, currentPageRowKeys, @@ -32,31 +34,61 @@ export default defineComponent({ const dataCount = paginatedMap.value.size return dataCount === 0 || dataCount === currentPageRowKeys.value.disabledRowKeys.length }) + const [visible, setVisible] = useState(false) + const prefixCls = computed(() => `${mergedPrefixCls.value}-selectable`) + const triggerClasses = computed(() => { + return { + [`${prefixCls.value}-trigger`]: true, + [`${prefixCls.value}-trigger--open`]: visible.value, + } + }) + + const renderOverlay = () => { + const options = mergedSelectableOptions.value + if (!options) { + return null + } + + return ( + + {options.map(option => ( + + ))} + + ) + } + const renderTrigger = () => + const renderDropDown = () => { + if (!mergedSelectableOptions.value) { + return null + } + + const dropdownProps: DropdownProps = { + 'onUpdate:visible': setVisible, + trigger: 'click', + } + + return + } return () => { const { multiple } = selectable.value! + if (!multiple) { return undefined } - const children: VNodeTypes[] = [] - const checked = currentPageAllSelected.value - const indeterminate = currentPageSomeSelected.value - const checkboxProps = { checked, indeterminate, disabled: disabled.value, onChange: handleHeadSelectChange } - children.push() - const options = mergedSelectableOptions.value - if (options) { - const trigger = - const content = ( - - {options.map(option => ( - - ))} - - ) - const slots = { default: () => trigger, overlay: () => content } - children.push() - } - return children + + return ( +
+ + {renderDropDown()} +
+ ) } }, }) diff --git a/packages/components/table/src/other/FilterDropdown.tsx b/packages/components/table/src/other/FilterDropdown.tsx new file mode 100644 index 000000000..ac7d1c789 --- /dev/null +++ b/packages/components/table/src/other/FilterDropdown.tsx @@ -0,0 +1,41 @@ +/** + * @license + * + * Use of this source code is governed by an MIT-style license that can be + * found in the LICENSE file at https://github.com/IDuxFE/idux/blob/main/LICENSE + */ + +import { defineComponent, inject } from 'vue' + +import { IxCheckbox, IxCheckboxGroup } from '@idux/components/checkbox' + +import { TABLE_TOKEN } from '../token' +import { tableFilterPanelProps } from '../types' + +export default defineComponent({ + props: tableFilterPanelProps, + setup(props) { + const { mergedPrefixCls } = inject(TABLE_TOKEN)! + + return () => { + const prefixCls = `${mergedPrefixCls.value}-filter-dropdown` + const { + filterable: { filters, filterBy, onChange }, + } = props + + return ( +
+ + {filters.map(filter => ( +
+ + {filter.text} + +
+ ))} +
+
+ ) + } + }, +}) diff --git a/packages/components/table/src/other/Footer.tsx b/packages/components/table/src/other/Footer.tsx index 4699dca7a..31fda2782 100644 --- a/packages/components/table/src/other/Footer.tsx +++ b/packages/components/table/src/other/Footer.tsx @@ -7,9 +7,10 @@ import type { Slots, VNode } from 'vue' -export function renderFooter(slots: Slots): VNode | null { +export function renderFooter(slots: Slots, prefixCls: string): VNode | null { if (!slots.footer) { return null } - return + + return
{slots.footer()}
} diff --git a/packages/components/table/src/other/Pagination.tsx b/packages/components/table/src/other/Pagination.tsx index 15595bb99..745d87f0b 100644 --- a/packages/components/table/src/other/Pagination.tsx +++ b/packages/components/table/src/other/Pagination.tsx @@ -16,6 +16,7 @@ import { IxPagination } from '@idux/components/pagination' export function renderPagination( mergedPagination: TablePagination | null, filteredData: MergedData[], + prefixCls: string, ): [VNode | null, VNode | null] { let top: VNode | null = null let bottom: VNode | null = null @@ -23,7 +24,7 @@ export function renderPagination( if (mergedPagination !== null) { const { position } = mergedPagination const [vertical, horizontal] = kebabCase(position).split('-') - const className = `ix-table-pagination ix-table-pagination-${horizontal}` + const className = `${prefixCls}-pagination ${prefixCls}-pagination-${horizontal}` const node = diff --git a/packages/components/table/src/token.ts b/packages/components/table/src/token.ts index f0f2b9b34..0726adee2 100644 --- a/packages/components/table/src/token.ts +++ b/packages/components/table/src/token.ts @@ -8,6 +8,7 @@ import type { ColumnsContext } from './composables/useColumns' import type { DataSourceContext } from './composables/useDataSource' import type { ExpandableContext } from './composables/useExpandable' +import type { FilterableContext } from './composables/useFilterable' import type { GetRowKey } from './composables/useGetRowKey' import type { PaginationContext } from './composables/usePagination' import type { ScrollContext } from './composables/useScroll' @@ -28,9 +29,11 @@ export interface TableContext ScrollContext, SelectableContext, SortableContext, + FilterableContext, StickyContext, TagsContext { props: TableProps + mergedPrefixCls: ComputedRef slots: Slots config: TableConfig locale: ComputedRef @@ -43,7 +46,7 @@ export const TABLE_TOKEN: InjectionKey = Symbol('TABLE_TOKEN') export interface TableBodyContext { mainTableWidth: Ref - changeColumnWidth: (key: Key, width: number) => void + changeColumnWidth: (key: Key, width: number | false) => void } export const tableBodyToken: InjectionKey = Symbol('tableBodyToken') diff --git a/packages/components/table/src/types.ts b/packages/components/table/src/types.ts index 248148efb..f74098a7b 100644 --- a/packages/components/table/src/types.ts +++ b/packages/components/table/src/types.ts @@ -9,6 +9,7 @@ import type { TableColumnMerged, TableColumnMergedExtra } from './composables/useColumns' import type { BreakpointKey } from '@idux/cdk/breakpoint' +import type { VirtualScrollToFn } from '@idux/cdk/scroll' import type { IxInnerPropTypes, IxPublicPropTypes } from '@idux/cdk/utils' import type { EmptyProps } from '@idux/components/empty' import type { HeaderProps } from '@idux/components/header' @@ -16,13 +17,16 @@ import type { PaginationProps } from '@idux/components/pagination' import type { SpinProps } from '@idux/components/spin' import type { DefineComponent, HTMLAttributes, VNodeTypes } from 'vue' -import { VirtualScrollToFn } from '@idux/cdk/scroll' import { IxPropTypes } from '@idux/cdk/utils' +import { FlattedData } from './composables/useDataSource' + export const tableProps = { expandedRowKeys: IxPropTypes.array(), selectedRowKeys: IxPropTypes.array(), + expandedAllStatus: IxPropTypes.bool, + borderless: IxPropTypes.bool, childrenKey: IxPropTypes.string.def('children'), columns: IxPropTypes.array>().def(() => []), @@ -61,7 +65,10 @@ export type TableComponent = DefineComponent< > export type TableInstance = InstanceType> -export type TableColumn = TableColumnBase | TableColumnExpandable | TableColumnSelectable +export type TableColumn = + | TableColumnBase + | TableColumnExpandable + | TableColumnSelectable export interface TableColumnCommon { additional?: { @@ -78,12 +85,13 @@ export interface TableColumnCommon { width?: string | number } -export interface TableColumnBase extends TableColumnCommon { +export interface TableColumnBase extends TableColumnCommon { dataKey?: Key | Key[] editable?: boolean ellipsis?: boolean key?: Key sortable?: TableColumnSortable + filterable?: TableColumnFilterable title?: string children?: TableColumn[] customRender?: string | TableColumnRenderFn @@ -98,12 +106,12 @@ export interface TableColumnRenderOption { export type TableColumnRenderFn = (options: TableColumnRenderOption) => VNodeTypes export type TableColumnTitleFn = (options: { title?: string }) => VNodeTypes -export interface TableColumnExpandable extends TableColumnCommon { +export interface TableColumnExpandable extends TableColumnBase { type: 'expandable' customExpand?: string | TableColumnExpandableExpandFn customIcon?: string | TableColumnExpandableIconFn - disabled?: (record: T, rowIndex: number) => boolean + disabled?: (record: T) => boolean // remove ? icon?: [string, string] indent?: number @@ -181,6 +189,18 @@ export interface TableColumnSortable { onChange?: (currOrderBy?: TableColumnSortOrder) => void } +export interface TableColumnFilter { + text: string + value: V +} + +export interface TableColumnFilterable { + filters: TableColumnFilter[] + filterBy?: V[] + filter: (currFilterBy: V[], record: T) => boolean + onChange?: (currFilterBy: V[]) => void +} + export interface TableSticky { offsetHead?: number offsetFoot?: number @@ -208,6 +228,7 @@ export const tableBodyRowProps = { rowIndex: IxPropTypes.number.isRequired, level: IxPropTypes.number.isRequired, record: IxPropTypes.any.isRequired, + rowData: IxPropTypes.object().isRequired, rowKey: IxPropTypes.oneOfType([String, Number]).isRequired, } @@ -216,6 +237,7 @@ export type TableBodyRowProps = IxInnerPropTypes export const tableBodyCellProps = { column: IxPropTypes.object().isRequired, colIndex: IxPropTypes.number.isRequired, + level: IxPropTypes.number.isRequired, record: IxPropTypes.any.isRequired, rowIndex: IxPropTypes.number.isRequired, disabled: IxPropTypes.bool, @@ -234,3 +256,12 @@ export const tableMeasureCellProps = { } export type TableMeasureCellProps = IxInnerPropTypes + +export const tableFilterPanelProps = { + filterable: IxPropTypes.object().isRequired, +} + +export type TableFilterPanelProps = IxInnerPropTypes + +export const tableHeadCellFilterableTriggerProps = tableFilterPanelProps +export type TableHeadCellFilterableTriggerProps = TableFilterPanelProps diff --git a/packages/components/table/style/filterDropdown.less b/packages/components/table/style/filterDropdown.less new file mode 100644 index 000000000..119728681 --- /dev/null +++ b/packages/components/table/style/filterDropdown.less @@ -0,0 +1,24 @@ +.@{table-prefix}-filter-dropdown { + display: inline-flex; + padding: @table-filter-dropdown-padding; + background-color: @table-filter-dropdown-background-color; + box-shadow: @table-filter-dropdown-box-shadow; + + &-group { + display: inline-flex; + flex-direction: column; + } + + &-item { + min-width: @table-filter-dropdown-width; + height: @table-filter-dropdown-item-height; + padding: @table-filter-dropdown-item-padding; + display: inline-flex; + align-items: center; + justify-content: flex-start; + + &:hover { + background-color: @table-filter-dropdown-item-hover-background-color; + } + } +} \ No newline at end of file diff --git a/packages/components/table/style/headerIcon.less b/packages/components/table/style/headerIcon.less new file mode 100644 index 000000000..7b93ae3a4 --- /dev/null +++ b/packages/components/table/style/headerIcon.less @@ -0,0 +1,20 @@ +.header-icon() { + min-width: @table-head-icon-size; + min-height: @table-head-icon-size; + display: inline-flex; + align-items: center; + justify-content: center; + border-radius: 1px; + font-size: @table-head-icon-font-size; + color: @table-head-icon-color; + transition: color, background @table-head-icon-transition-duration; + cursor: pointer; + + &:hover { + background-color: @table-head-icon-hover-backgroud-color; + } + + &-active { + color: @color-primary; + } +} \ No newline at end of file diff --git a/packages/components/table/style/index.less b/packages/components/table/style/index.less index 7ae30e83b..360cb41de 100644 --- a/packages/components/table/style/index.less +++ b/packages/components/table/style/index.less @@ -6,11 +6,14 @@ @import './fixed.less'; @import './radius.less'; @import './size.less'; +@import './headerIcon.less'; +@import './filterDropdown.less'; .@{table-prefix} { .reset-component(); .clearfix(); + color: @table-color; max-width: 100%; &-container { @@ -49,6 +52,7 @@ color: @table-head-color; background: @table-head-background; font-weight: @table-head-font-weight; + line-height: @table-head-line-height; overflow-wrap: break-word; border-bottom: @table-border-width @table-border-style @table-border-color; @@ -78,6 +82,7 @@ td { position: relative; border-bottom: @table-border-width @table-border-style @table-border-color; + line-height: @table-body-line-height; overflow-wrap: break-word; transition: background 0.3s; @@ -120,13 +125,19 @@ &-cell-title { position: relative; z-index: 1; - flex: 1; } &-cell-scroll-bar { box-shadow: 0 1px 0 1px @table-head-background; } + & &-head-cell-wrapper, + & &-head-icon-triggers { + display: inline-flex; + align-items: center; + justify-content: flex-start; + } + &-pagination { display: flex; flex-wrap: wrap; @@ -149,6 +160,14 @@ } } + & &-expandable-icon { + width: @table-expandable-icon-size; + height: @table-expandable-icon-size; + font-size: @table-expandable-icon-size; + margin-right: @table-icon-margin; + color: @table-expandable-icon-color; + } + // --------- Selectable --------- & &-col-selectable { @@ -160,10 +179,19 @@ } & &-cell-selectable { - display: flex; - align-items: center; - & > .@{idux-prefix}-dropdown-trigger { - margin-left: 4px; + .@{table-prefix}-selectable { + display: flex; + align-items: center; + + &-trigger { + margin-left: @table-icon-margin; + color: @table-head-icon-color; + cursor: pointer; + + &--open { + transform: rotateX(180deg); + } + } } } @@ -189,32 +217,21 @@ } } - & th&-cell-sorted, - & td&-cell-sorted { - background: @table-body-hover-background; - } + &-sortable-trigger { + .header-icon(); - &-sortable { - display: flex; - flex: auto; - align-items: center; - justify-content: space-between; + flex-direction: column; + font-size: 8px; + margin-left: @table-icon-margin; - &-trigger { - display: inline-flex; - flex-direction: column; - align-items: center; - color: @table-head-icon-color; - font-size: 10px; - transition: color 0.3s; + .@{idux-prefix}-icon + .@{idux-prefix}-icon { + margin-top: -0.2em; + } + } - &-active { - color: @color-primary; - } + &-filterable-trigger { + .header-icon(); - .@{idux-prefix}-icon + .@{idux-prefix}-icon { - margin-top: -0.2em; - } - } + margin-left: @table-icon-margin; } } diff --git a/packages/components/table/style/size.less b/packages/components/table/style/size.less index ee867ec1d..5e62a467b 100644 --- a/packages/components/table/style/size.less +++ b/packages/components/table/style/size.less @@ -1,8 +1,12 @@ -.table-size(@size, @padding-vertical, @padding-horizontal, @font-size) { +.table-size(@size, @head-height, @padding-vertical, @padding-horizontal, @font-size) { .@{table-prefix}-@{size} { font-size: @font-size; - th, + th { + padding: 0 @padding-horizontal; + height: @head-height; + } + td { padding: @padding-vertical @padding-horizontal; } @@ -11,6 +15,6 @@ } } -.table-size(~'lg', @table-padding-vertical-lg, @table-padding-horizontal-lg, @table-font-size-lg); -.table-size(~'md', @table-padding-vertical-md, @table-padding-horizontal-md, @table-font-size-md); -.table-size(~'sm', @table-padding-vertical-sm, @table-padding-horizontal-sm, @table-font-size-sm); +.table-size(~'lg', @table-head-height-lg, @table-padding-vertical-lg, @table-padding-horizontal-lg, @table-font-size-lg); +.table-size(~'md', @table-head-height-md, @table-padding-vertical-md, @table-padding-horizontal-md, @table-font-size-md); +.table-size(~'sm', @table-head-height-sm, @table-padding-vertical-sm, @table-padding-horizontal-sm, @table-font-size-sm); diff --git a/packages/components/table/style/themes/default.less b/packages/components/table/style/themes/default.less index 329463be6..00f3aac53 100644 --- a/packages/components/table/style/themes/default.less +++ b/packages/components/table/style/themes/default.less @@ -1,3 +1,4 @@ @import '../../../style/themes/default.less'; +@import '../../../form/style/themes/default.variable.less'; @import '../index.less'; @import './default.variable.less'; diff --git a/packages/components/table/style/themes/default.variable.less b/packages/components/table/style/themes/default.variable.less index f79d035ac..e78da9e28 100644 --- a/packages/components/table/style/themes/default.variable.less +++ b/packages/components/table/style/themes/default.variable.less @@ -1,3 +1,12 @@ +@table-color: @text-color; + +@table-head-line-height: @line-height-base; +@table-body-line-height: @line-height-base; + +@table-head-height-lg: @height-xxl; +@table-head-height-md: @height-xl; +@table-head-height-sm: @height-md; + @table-padding-vertical-lg: @spacing-lg; @table-padding-horizontal-lg: @spacing-lg; @table-padding-vertical-md: @spacing-md; @@ -5,7 +14,7 @@ @table-padding-vertical-sm: @spacing-sm; @table-padding-horizontal-sm: @spacing-sm; -@table-font-size-lg: @font-size-md; +@table-font-size-lg: @font-size-lg; @table-font-size-md: @font-size-md; @table-font-size-sm: @font-size-sm; @@ -17,10 +26,29 @@ @table-border-radius: @border-radius-sm; @table-head-background: @background-color-light; @table-head-color: @color-black; +@table-head-split-height: 16px; @table-head-split-color: rgba(0, 0, 0, 0.06); + @table-head-icon-color: @color-black; +@table-head-icon-size: 16px; +@table-head-icon-font-size: @font-size-xs; +@table-head-icon-hover-backgroud-color: @color-graphite-l40; @table-head-font-weight: @font-weight-lg; +@table-head-icon-transition-duration: @transition-duration-base; @table-body-hover-background: @background-color-light; @table-pagination-margin: @spacing-lg 0; + +@table-icon-margin: @spacing-xs; + +@table-expandable-icon-size: @font-size-md; +@table-expandable-icon-color: @color-black; + +@table-filter-dropdown-width: 120px; +@table-filter-dropdown-padding: @spacing-xs 0; +@table-filter-dropdown-box-shadow: @shadow-bottom-md; +@table-filter-dropdown-background-color: @form-background-color; +@table-filter-dropdown-item-height: @height-md; +@table-filter-dropdown-item-padding: 0 @spacing-sm; +@table-filter-dropdown-item-hover-background-color: @background-color-light; \ No newline at end of file diff --git a/scripts/gulp/icons/assets/filter-filled.svg b/scripts/gulp/icons/assets/filter-filled.svg new file mode 100644 index 000000000..a8ecb9e13 --- /dev/null +++ b/scripts/gulp/icons/assets/filter-filled.svg @@ -0,0 +1 @@ + \ No newline at end of file