refactor: table (#4641)
* refactor: table * refactor: table * refactor: table * refactor: table * refactor: table * refactor: table * refactor: table * refactor: table * refactor: table * fix: column not pass to cell * doc: uppate table * fix: update bodyCell headerCell * doc: remove examples * refactor: table * fix: table title not work * fix: table selection * fix: table checkStrictly * refactor: table * fix: table template error * feat: table support summary * test: update snap * perf: table * docs(table): fix ajax demo (#4639) * test: update table * refactor: remove old table * doc: update table doc * doc: update doc * doc: update select * doc: update summary Co-authored-by: John <John60676@qq.com>pull/4671/head
parent
1aee88e0ba
commit
21502ea6b0
|
@ -1,6 +1,12 @@
|
|||
let cached;
|
||||
/* eslint-disable no-param-reassign */
|
||||
|
||||
let cached: number;
|
||||
|
||||
export default function getScrollBarSize(fresh?: boolean) {
|
||||
if (typeof document === 'undefined') {
|
||||
return 0;
|
||||
}
|
||||
|
||||
export default function getScrollBarSize(fresh) {
|
||||
if (fresh || cached === undefined) {
|
||||
const inner = document.createElement('div');
|
||||
inner.style.width = '100%';
|
||||
|
@ -10,8 +16,8 @@ export default function getScrollBarSize(fresh) {
|
|||
const outerStyle = outer.style;
|
||||
|
||||
outerStyle.position = 'absolute';
|
||||
outerStyle.top = 0;
|
||||
outerStyle.left = 0;
|
||||
outerStyle.top = '0';
|
||||
outerStyle.left = '0';
|
||||
outerStyle.pointerEvents = 'none';
|
||||
outerStyle.visibility = 'hidden';
|
||||
outerStyle.width = '200px';
|
||||
|
@ -36,3 +42,21 @@ export default function getScrollBarSize(fresh) {
|
|||
}
|
||||
return cached;
|
||||
}
|
||||
|
||||
function ensureSize(str: string) {
|
||||
const match = str.match(/^(.*)px$/);
|
||||
const value = Number(match?.[1]);
|
||||
return Number.isNaN(value) ? getScrollBarSize() : value;
|
||||
}
|
||||
|
||||
export function getTargetScrollBarSize(target: HTMLElement) {
|
||||
if (typeof document === 'undefined' || !target || !(target instanceof Element)) {
|
||||
return { width: 0, height: 0 };
|
||||
}
|
||||
|
||||
const { width, height } = getComputedStyle(target, '::-webkit-scrollbar');
|
||||
return {
|
||||
width: ensureSize(width),
|
||||
height: ensureSize(height),
|
||||
};
|
||||
}
|
|
@ -29,6 +29,7 @@ const parseStyleText = (cssText = '', camel) => {
|
|||
const res = {};
|
||||
const listDelimiter = /;(?![^(]*\))/g;
|
||||
const propertyDelimiter = /:(.+)/;
|
||||
if (typeof cssText === 'object') return cssText;
|
||||
cssText.split(listDelimiter).forEach(function (item) {
|
||||
if (item) {
|
||||
const tmp = item.split(propertyDelimiter);
|
||||
|
|
|
@ -0,0 +1,14 @@
|
|||
import type { UnwrapRef } from 'vue';
|
||||
import { reactive, toRef } from 'vue';
|
||||
|
||||
/**
|
||||
* Reactively pick fields from a reactive object
|
||||
*
|
||||
* @see https://vueuse.js.org/reactivePick
|
||||
*/
|
||||
export function reactivePick<T extends object, K extends keyof T>(
|
||||
obj: T,
|
||||
...keys: K[]
|
||||
): { [S in K]: UnwrapRef<T[S]> } {
|
||||
return reactive(Object.fromEntries(keys.map(k => [k, toRef(obj, k)]))) as any;
|
||||
}
|
|
@ -0,0 +1,42 @@
|
|||
import { isRef, reactive } from 'vue';
|
||||
import type { Ref } from 'vue';
|
||||
type MaybeRef<T> = T | Ref<T>;
|
||||
/**
|
||||
* Converts ref to reactive.
|
||||
*
|
||||
* @see https://vueuse.org/toReactive
|
||||
* @param objectRef A ref of object
|
||||
*/
|
||||
export function toReactive<T extends object>(objectRef: MaybeRef<T>): T {
|
||||
if (!isRef(objectRef)) return reactive(objectRef) as T;
|
||||
|
||||
const proxy = new Proxy(
|
||||
{},
|
||||
{
|
||||
get(_, p, receiver) {
|
||||
return Reflect.get(objectRef.value, p, receiver);
|
||||
},
|
||||
set(_, p, value) {
|
||||
(objectRef.value as any)[p] = value;
|
||||
return true;
|
||||
},
|
||||
deleteProperty(_, p) {
|
||||
return Reflect.deleteProperty(objectRef.value, p);
|
||||
},
|
||||
has(_, p) {
|
||||
return Reflect.has(objectRef.value, p);
|
||||
},
|
||||
ownKeys() {
|
||||
return Object.keys(objectRef.value);
|
||||
},
|
||||
getOwnPropertyDescriptor() {
|
||||
return {
|
||||
enumerable: true,
|
||||
configurable: true,
|
||||
};
|
||||
},
|
||||
},
|
||||
);
|
||||
|
||||
return reactive(proxy) as T;
|
||||
}
|
|
@ -62,4 +62,9 @@ export function getDataAndAriaProps(props) {
|
|||
}, {});
|
||||
}
|
||||
|
||||
export function toPx(val) {
|
||||
if (typeof val === 'number') return `${val}px`;
|
||||
return val;
|
||||
}
|
||||
|
||||
export { isOn, cacheStringFunction, camelize, hyphenate, capitalize, resolvePropValue };
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
import type { ExtractPropTypes } from 'vue';
|
||||
import { defineComponent, inject, nextTick } from 'vue';
|
||||
import PropTypes from '../_util/vue-types';
|
||||
import classNames from '../_util/classNames';
|
||||
|
@ -9,11 +10,8 @@ import type { RadioChangeEvent } from '../radio/interface';
|
|||
import type { EventHandler } from '../_util/EventInterface';
|
||||
function noop() {}
|
||||
|
||||
export default defineComponent({
|
||||
name: 'ACheckbox',
|
||||
inheritAttrs: false,
|
||||
__ANT_CHECKBOX: true,
|
||||
props: {
|
||||
export const checkboxProps = () => {
|
||||
return {
|
||||
prefixCls: PropTypes.string,
|
||||
defaultChecked: PropTypes.looseBool,
|
||||
checked: PropTypes.looseBool,
|
||||
|
@ -27,7 +25,17 @@ export default defineComponent({
|
|||
autofocus: PropTypes.looseBool,
|
||||
onChange: PropTypes.func,
|
||||
'onUpdate:checked': PropTypes.func,
|
||||
},
|
||||
skipGroup: PropTypes.looseBool,
|
||||
};
|
||||
};
|
||||
|
||||
export type CheckboxProps = Partial<ExtractPropTypes<ReturnType<typeof checkboxProps>>>;
|
||||
|
||||
export default defineComponent({
|
||||
name: 'ACheckbox',
|
||||
inheritAttrs: false,
|
||||
__ANT_CHECKBOX: true,
|
||||
props: checkboxProps(),
|
||||
emits: ['change', 'update:checked'],
|
||||
setup() {
|
||||
return {
|
||||
|
@ -38,6 +46,9 @@ export default defineComponent({
|
|||
|
||||
watch: {
|
||||
value(value, prevValue) {
|
||||
if (this.skipGroup) {
|
||||
return;
|
||||
}
|
||||
nextTick(() => {
|
||||
const { checkboxGroupContext: checkboxGroup = {} } = this;
|
||||
if (checkboxGroup.registerValue && checkboxGroup.cancelValue) {
|
||||
|
@ -85,7 +96,7 @@ export default defineComponent({
|
|||
const props = getOptionProps(this);
|
||||
const { checkboxGroupContext: checkboxGroup, $attrs } = this;
|
||||
const children = getSlot(this);
|
||||
const { indeterminate, prefixCls: customizePrefixCls, ...restProps } = props;
|
||||
const { indeterminate, prefixCls: customizePrefixCls, skipGroup, ...restProps } = props;
|
||||
const getPrefixCls = this.configProvider.getPrefixCls;
|
||||
const prefixCls = getPrefixCls('checkbox', customizePrefixCls);
|
||||
const {
|
||||
|
@ -101,7 +112,7 @@ export default defineComponent({
|
|||
prefixCls,
|
||||
...restAttrs,
|
||||
};
|
||||
if (checkboxGroup) {
|
||||
if (checkboxGroup && !skipGroup) {
|
||||
checkboxProps.onChange = (...args) => {
|
||||
this.$emit('change', ...args);
|
||||
checkboxGroup.toggleOption({ label: children, value: props.value });
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
import type { App, Plugin } from 'vue';
|
||||
import Checkbox from './Checkbox';
|
||||
import Checkbox, { checkboxProps } from './Checkbox';
|
||||
import CheckboxGroup from './Group';
|
||||
export type { CheckboxProps } from './Checkbox';
|
||||
|
||||
Checkbox.Group = CheckboxGroup;
|
||||
|
||||
|
@ -10,7 +11,7 @@ Checkbox.install = function (app: App) {
|
|||
app.component(CheckboxGroup.name, CheckboxGroup);
|
||||
return app;
|
||||
};
|
||||
export { CheckboxGroup };
|
||||
export { CheckboxGroup, checkboxProps };
|
||||
export default Checkbox as typeof Checkbox &
|
||||
Plugin & {
|
||||
readonly Group: typeof CheckboxGroup;
|
||||
|
|
|
@ -164,7 +164,22 @@ export { default as Steps, Step } from './steps';
|
|||
export type { SwitchProps } from './switch';
|
||||
export { default as Switch } from './switch';
|
||||
|
||||
export { default as Table, TableColumn, TableColumnGroup } from './table';
|
||||
export type {
|
||||
TableProps,
|
||||
TablePaginationConfig,
|
||||
ColumnGroupType as TableColumnGroupType,
|
||||
ColumnType as TableColumnType,
|
||||
ColumnProps as TableColumnProps,
|
||||
ColumnsType as TableColumnsType,
|
||||
} from './table';
|
||||
export {
|
||||
default as Table,
|
||||
TableColumn,
|
||||
TableColumnGroup,
|
||||
TableSummary,
|
||||
TableSummaryRow,
|
||||
TableSummaryCell,
|
||||
} from './table';
|
||||
|
||||
export type { TransferProps } from './transfer';
|
||||
export { default as Transfer } from './transfer';
|
||||
|
|
|
@ -113,32 +113,47 @@ exports[`renders ./components/empty/demo/config-provider.vue correctly 1`] = `
|
|||
<div class="ant-spin-nested-loading">
|
||||
<!---->
|
||||
<div class="ant-spin-container">
|
||||
<div class="ant-table ant-table-default ant-table-empty ant-table-scroll-position-left">
|
||||
<div class="ant-table ant-table-empty" style="margin-top: 8px;">
|
||||
<!---->
|
||||
<div class="ant-table-content">
|
||||
<!---->
|
||||
<div class="ant-table-body">
|
||||
<table class="">
|
||||
<colgroup>
|
||||
<col data-key="0">
|
||||
<col data-key="1">
|
||||
</colgroup>
|
||||
<div class="ant-table-container">
|
||||
<div class="ant-table-content">
|
||||
<table style="table-layout: auto;">
|
||||
<colgroup></colgroup>
|
||||
<thead class="ant-table-thead">
|
||||
<tr>
|
||||
<th colstart="0" colspan="1" colend="0" rowspan="1" class=""><span class="ant-table-header-column"><div><span class="ant-table-column-title">Name</span><span class="ant-table-column-sorter"><!----></span>
|
||||
</div></span>
|
||||
<!---->
|
||||
</th>
|
||||
<th colstart="1" colspan="1" colend="1" rowspan="1" class=""><span class="ant-table-header-column"><div><span class="ant-table-column-title">Age</span><span class="ant-table-column-sorter"><!----></span>
|
||||
</div></span>
|
||||
<th class="ant-table-cell" colstart="0" colend="0">
|
||||
<!---->Name
|
||||
</th>
|
||||
<th class="ant-table-cell" colstart="1" colend="1">
|
||||
<!---->Age
|
||||
</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody class="ant-table-tbody">
|
||||
<!---->
|
||||
<tr class="ant-table-placeholder">
|
||||
<td colspan="2" class="ant-table-cell">
|
||||
<!---->No data
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
<!---->
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
<!---->
|
||||
</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody class="ant-table-tbody"></tbody>
|
||||
</table>
|
||||
</div>
|
||||
<div class="ant-table-placeholder">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<h3>List</h3>
|
||||
<div class="ant-list ant-list-split">
|
||||
<!---->
|
||||
<!---->
|
||||
<div class="ant-spin-nested-loading">
|
||||
<!---->
|
||||
<div class="ant-spin-container">
|
||||
<div class="ant-list-empty-text">
|
||||
<div class="ant-empty ant-empty-normal">
|
||||
<div class="ant-empty-image"><svg class="ant-empty-img-simple" width="64" height="41" viewBox="0 0 64 41">
|
||||
<g transform="translate(0 1)" fill="none" fill-rule="evenodd">
|
||||
|
@ -153,39 +168,11 @@ exports[`renders ./components/empty/demo/config-provider.vue correctly 1`] = `
|
|||
<!---->
|
||||
</div>
|
||||
</div>
|
||||
<!---->
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<h3>List</h3>
|
||||
<div class="ant-list ant-list-split">
|
||||
<!---->
|
||||
<!---->
|
||||
<div class="ant-spin-nested-loading">
|
||||
<!---->
|
||||
<div class="ant-spin-container">
|
||||
<div class="ant-list-empty-text">
|
||||
<div class="ant-empty ant-empty-normal">
|
||||
<div class="ant-empty-image"><svg class="ant-empty-img-simple" width="64" height="41" viewBox="0 0 64 41">
|
||||
<g transform="translate(0 1)" fill="none" fill-rule="evenodd">
|
||||
<ellipse class="ant-empty-img-simple-ellipse" fill="#F5F5F5" cx="32" cy="33" rx="32" ry="7"></ellipse>
|
||||
<g class="ant-empty-img-simple-g" fill-rule="nonzero" stroke="#D9D9D9">
|
||||
<path d="M55 12.76L44.854 1.258C44.367.474 43.656 0 42.907 0H21.093c-.749 0-1.46.474-1.947 1.257L9 12.761V22h46v-9.24z"></path>
|
||||
<path d="M41.613 15.931c0-1.605.994-2.93 2.227-2.931H55v18.137C55 33.26 53.68 35 52.05 35h-40.1C10.32 35 9 33.259 9 31.137V13h11.16c1.233 0 2.227 1.323 2.227 2.928v.022c0 1.605 1.005 2.901 2.237 2.901h14.752c1.232 0 2.237-1.308 2.237-2.913v-.007z" fill="#FAFAFA" class="ant-empty-img-simple-path"></path>
|
||||
</g>
|
||||
</g>
|
||||
</svg></div>
|
||||
<p class="ant-empty-description">No Data</p>
|
||||
<!---->
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!---->
|
||||
</div>
|
||||
<!---->
|
||||
<!---->
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -9,6 +9,7 @@ import type { ValidateMessages } from '../form/interface';
|
|||
import type { TransferLocale } from '../transfer';
|
||||
import type { PickerLocale as DatePickerLocale } from '../date-picker/generatePicker';
|
||||
import type { PaginationLocale } from '../pagination/Pagination';
|
||||
import type { TableLocale } from '../table/interface';
|
||||
|
||||
interface TransferLocaleForEmpty {
|
||||
description: string;
|
||||
|
@ -16,7 +17,7 @@ interface TransferLocaleForEmpty {
|
|||
export interface Locale {
|
||||
locale: string;
|
||||
Pagination?: PaginationLocale;
|
||||
Table?: Record<string, any>;
|
||||
Table?: TableLocale;
|
||||
Popconfirm?: Record<string, any>;
|
||||
Upload?: Record<string, any>;
|
||||
Form?: {
|
||||
|
|
|
@ -24,43 +24,43 @@ Select component to select value from options.
|
|||
|
||||
| Property | Description | Type | Default | Version |
|
||||
| --- | --- | --- | --- | --- |
|
||||
| allowClear | Show clear button. | boolean | false | |
|
||||
| autoClearSearchValue | Whether the current search will be cleared on selecting an item. Only applies when `mode` is set to `multiple` or `tags`. | boolean | true | |
|
||||
| autofocus | Get focus by default | boolean | false | |
|
||||
| bordered | Whether has border style | boolean | true | |
|
||||
| defaultActiveFirstOption | Whether active first option by default | boolean | true | |
|
||||
| disabled | Whether disabled select | boolean | false | |
|
||||
| dropdownClassName | className of dropdown menu | string | - | |
|
||||
| dropdownMatchSelectWidth | Whether dropdown's width is same with select. | boolean | true | |
|
||||
| dropdownRender | Customize dropdown content | ({menuNode: VNode, props}) => VNode \| v-slot | - | |
|
||||
| dropdownStyle | style of dropdown menu | object | - | |
|
||||
| dropdownMenuStyle | additional style applied to dropdown menu | object | - | |
|
||||
| filterOption | If true, filter options by input, if function, filter options against it. The function will receive two arguments, `inputValue` and `option`, if the function returns `true`, the option will be included in the filtered set; Otherwise, it will be excluded. | boolean or function(inputValue, option) | true | |
|
||||
| firstActiveValue | Value of action option by default | string\|string\[] | - | |
|
||||
| getPopupContainer | Parent Node which the selector should be rendered to. Default to `body`. When position issues happen, try to modify it into scrollable content and position it relative. | function(triggerNode) | () => document.body | |
|
||||
| labelInValue | whether to embed label in value, turn the format of value from `string` to `{key: string, label: vNodes}` | boolean | false | |
|
||||
| maxTagCount | Max tag count to show | number | - | |
|
||||
| maxTagPlaceholder | Placeholder for not showing tags | slot/function(omittedValues) | - | |
|
||||
| maxTagTextLength | Max text length to show | number | - | |
|
||||
| mode | Set mode of Select | 'multiple' \| 'tags' | - | |
|
||||
| notFoundContent | Specify content to show when no result matches.. | string\|slot | 'Not Found' | |
|
||||
| optionFilterProp | Which prop value of option will be used for filter if filterOption is true | string | value | |
|
||||
| optionLabelProp | Which prop value of option will render as content of select. | string | `value` for `combobox`, `children` for other modes | |
|
||||
| placeholder | Placeholder of select | string\|slot | - | |
|
||||
| showSearch | Whether show search input in single mode. | boolean | false | |
|
||||
| showArrow | Whether to show the drop-down arrow | boolean | true | |
|
||||
| size | Size of Select input. `default` `large` `small` | string | default | |
|
||||
| suffixIcon | The custom suffix icon | VNode \| slot | - | |
|
||||
| removeIcon | The custom remove icon | VNode \| slot | - | |
|
||||
| clearIcon | The custom clear icon | VNode \| slot | - | |
|
||||
| menuItemSelectedIcon | The custom menuItemSelected icon | VNode \| slot | - | |
|
||||
| tokenSeparators | Separator used to tokenize on tag/multiple mode | string\[] | | |
|
||||
| value(v-model) | Current selected option. | string\|number\|string\[]\|number\[] | - | |
|
||||
| options | Data of the selectOption, manual construction work is no longer needed if this property has been set | array<{value, label, [disabled, key, title]}> | \[] | |
|
||||
| allowClear | Show clear button. | boolean | false | |
|
||||
| autoClearSearchValue | Whether the current search will be cleared on selecting an item. Only applies when `mode` is set to `multiple` or `tags`. | boolean | true | |
|
||||
| autofocus | Get focus by default | boolean | false | |
|
||||
| bordered | Whether has border style | boolean | true | |
|
||||
| defaultActiveFirstOption | Whether active first option by default | boolean | true | |
|
||||
| disabled | Whether disabled select | boolean | false | |
|
||||
| dropdownClassName | className of dropdown menu | string | - | |
|
||||
| dropdownMatchSelectWidth | Whether dropdown's width is same with select. | boolean | true | |
|
||||
| dropdownRender | Customize dropdown content | ({menuNode: VNode, props}) => VNode \| v-slot | - | |
|
||||
| dropdownStyle | style of dropdown menu | object | - | |
|
||||
| dropdownMenuStyle | additional style applied to dropdown menu | object | - | |
|
||||
| filterOption | If true, filter options by input, if function, filter options against it. The function will receive two arguments, `inputValue` and `option`, if the function returns `true`, the option will be included in the filtered set; Otherwise, it will be excluded. | boolean or function(inputValue, option) | true | |
|
||||
| firstActiveValue | Value of action option by default | string\|string\[] | - | |
|
||||
| getPopupContainer | Parent Node which the selector should be rendered to. Default to `body`. When position issues happen, try to modify it into scrollable content and position it relative. | function(triggerNode) | () => document.body | |
|
||||
| labelInValue | whether to embed label in value, turn the format of value from `string` to `{key: string, label: vNodes}` | boolean | false | |
|
||||
| maxTagCount | Max tag count to show | number | - | |
|
||||
| maxTagPlaceholder | Placeholder for not showing tags | slot/function(omittedValues) | - | |
|
||||
| maxTagTextLength | Max text length to show | number | - | |
|
||||
| mode | Set mode of Select | 'multiple' \| 'tags' | - | |
|
||||
| notFoundContent | Specify content to show when no result matches.. | string\|slot | 'Not Found' | |
|
||||
| optionFilterProp | Which prop value of option will be used for filter if filterOption is true | string | value | |
|
||||
| optionLabelProp | Which prop value of option will render as content of select. | string | `value` for `combobox`, `children` for other modes | |
|
||||
| placeholder | Placeholder of select | string\|slot | - | |
|
||||
| showSearch | Whether show search input in single mode. | boolean | false | |
|
||||
| showArrow | Whether to show the drop-down arrow | boolean | true | |
|
||||
| size | Size of Select input. `default` `large` `small` | string | default | |
|
||||
| suffixIcon | The custom suffix icon | VNode \| slot | - | |
|
||||
| removeIcon | The custom remove icon | VNode \| slot | - | |
|
||||
| clearIcon | The custom clear icon | VNode \| slot | - | |
|
||||
| menuItemSelectedIcon | The custom menuItemSelected icon | VNode \| slot | - | |
|
||||
| tokenSeparators | Separator used to tokenize on tag/multiple mode | string\[] | | |
|
||||
| value(v-model) | Current selected option. | string\|number\|string\[]\|number\[] | - | |
|
||||
| options | Data of the selectOption, manual construction work is no longer needed if this property has been set | array<{value, label, [disabled, key, title]}> | \[] | |
|
||||
| option | custom render option by slot | v-slot:option="{value, label, [disabled, key, title]}" | - | 2.2.5 |
|
||||
| defaultOpen | Initial open state of dropdown | boolean | - | |
|
||||
| open | Controlled open state of dropdown | boolean | - | |
|
||||
| loading | indicate loading state | Boolean | false | |
|
||||
| defaultOpen | Initial open state of dropdown | boolean | - | |
|
||||
| open | Controlled open state of dropdown | boolean | - | |
|
||||
| loading | indicate loading state | Boolean | false | |
|
||||
|
||||
> Note, if you find that the drop-down menu scrolls with the page, or you need to trigger Select in other popup layers, please try to use `getPopupContainer={triggerNode => triggerNode.parentElement}` to fix the drop-down popup rendering node in the parent element of the trigger .
|
||||
|
||||
|
@ -109,3 +109,11 @@ Select component to select value from options.
|
|||
### The dropdown is closed when click `dropdownRender` area?
|
||||
|
||||
See the [dropdownRender example](/components/select/#components-select-demo-custom-dropdown).
|
||||
|
||||
### Why is `placeholder` not displayed?
|
||||
|
||||
`placeholder` will only be displayed when `value = undefined`, and other values such as null, 0,'', etc. are meaningful values for the JS language.
|
||||
|
||||
You can check [JS Language Specification](https://262.ecma-international.org/5.1/#sec-4.3.9) for further details.
|
||||
|
||||
You can also check [antd issue](https://github.com/ant-design/ant-design/issues/2367) to view the discussion.
|
||||
|
|
|
@ -25,42 +25,42 @@ cover: https://gw.alipayobjects.com/zos/alicdn/_0XzgOis7/Select.svg
|
|||
|
||||
| 参数 | 说明 | 类型 | 默认值 | 版本 |
|
||||
| --- | --- | --- | --- | --- |
|
||||
| allowClear | 支持清除 | boolean | false | |
|
||||
| autoClearSearchValue | 是否在选中项后清空搜索框,只在 `mode` 为 `multiple` 或 `tags` 时有效。 | boolean | true | |
|
||||
| autofocus | 默认获取焦点 | boolean | false | |
|
||||
| bordered | 是否有边框 | boolean | true | |
|
||||
| defaultActiveFirstOption | 是否默认高亮第一个选项。 | boolean | true | |
|
||||
| disabled | 是否禁用 | boolean | false | |
|
||||
| dropdownClassName | 下拉菜单的 className 属性 | string | - | |
|
||||
| dropdownMatchSelectWidth | 下拉菜单和选择器同宽 | boolean | true | 」
|
||||
| dropdownRender | 自定义下拉框内容 | ({menuNode: VNode, props}) => VNode \| v-slot | - | |
|
||||
| dropdownStyle | 下拉菜单的 style 属性 | object | - | |
|
||||
| dropdownMenuStyle | dropdown 菜单自定义样式 | object | - | |
|
||||
| filterOption | 是否根据输入项进行筛选。当其为一个函数时,会接收 `inputValue` `option` 两个参数,当 `option` 符合筛选条件时,应返回 `true`,反之则返回 `false`。 | boolean or function(inputValue, option) | true | |
|
||||
| firstActiveValue | 默认高亮的选项 | string\|string\[] | - | |
|
||||
| getPopupContainer | 菜单渲染父节点。默认渲染到 body 上,如果你遇到菜单滚动定位问题,试试修改为滚动的区域,并相对其定位。 | Function(triggerNode) | () => document.body | |
|
||||
| labelInValue | 是否把每个选项的 label 包装到 value 中,会把 Select 的 value 类型从 `string` 变为 `{key: string, label: vNodes}` 的格式 | boolean | false | |
|
||||
| maxTagCount | 最多显示多少个 tag | number | - | |
|
||||
| maxTagPlaceholder | 隐藏 tag 时显示的内容 | slot/function(omittedValues) | - | |
|
||||
| maxTagTextLength | 最大显示的 tag 文本长度 | number | - | |
|
||||
| mode | 设置 Select 的模式为多选或标签 | 'multiple' \| 'tags' \| 'combobox' | - | |
|
||||
| notFoundContent | 当下拉列表为空时显示的内容 | string\|slot | 'Not Found' | |
|
||||
| optionFilterProp | 搜索时过滤对应的 option 属性,不支持 children | string | value | |
|
||||
| optionLabelProp | 回填到选择框的 Option 的属性值,默认是 Option 的子元素。比如在子元素需要高亮效果时,此值可以设为 `value`。 | string | `children` (combobox 模式下为 `value`) | |
|
||||
| placeholder | 选择框默认文字 | string\|slot | - | |
|
||||
| showSearch | 使单选模式可搜索 | boolean | false | |
|
||||
| showArrow | 是否显示下拉小箭头 | boolean | true | |
|
||||
| size | 选择框大小,可选 `large` `small` | string | default | |
|
||||
| suffixIcon | 自定义的选择框后缀图标 | VNode \| slot | - | |
|
||||
| removeIcon | 自定义的多选框清除图标 | VNode \| slot | - | |
|
||||
| clearIcon | 自定义的多选框清空图标 | VNode \| slot | - | |
|
||||
| menuItemSelectedIcon | 自定义当前选中的条目图标 | VNode \| slot | - | |
|
||||
| tokenSeparators | 在 tags 和 multiple 模式下自动分词的分隔符 | string\[] | | |
|
||||
| value(v-model) | 指定当前选中的条目 | string\|string\[]\|number\|number\[] | - | |
|
||||
| options | options 数据,如果设置则不需要手动构造 selectOption 节点 | array<{value, label, [disabled, key, title]}> | \[] | |
|
||||
| allowClear | 支持清除 | boolean | false | |
|
||||
| autoClearSearchValue | 是否在选中项后清空搜索框,只在 `mode` 为 `multiple` 或 `tags` 时有效。 | boolean | true | |
|
||||
| autofocus | 默认获取焦点 | boolean | false | |
|
||||
| bordered | 是否有边框 | boolean | true | |
|
||||
| defaultActiveFirstOption | 是否默认高亮第一个选项。 | boolean | true | |
|
||||
| disabled | 是否禁用 | boolean | false | |
|
||||
| dropdownClassName | 下拉菜单的 className 属性 | string | - | |
|
||||
| dropdownMatchSelectWidth | 下拉菜单和选择器同宽 | boolean | true | 」 |
|
||||
| dropdownRender | 自定义下拉框内容 | ({menuNode: VNode, props}) => VNode \| v-slot | - | |
|
||||
| dropdownStyle | 下拉菜单的 style 属性 | object | - | |
|
||||
| dropdownMenuStyle | dropdown 菜单自定义样式 | object | - | |
|
||||
| filterOption | 是否根据输入项进行筛选。当其为一个函数时,会接收 `inputValue` `option` 两个参数,当 `option` 符合筛选条件时,应返回 `true`,反之则返回 `false`。 | boolean or function(inputValue, option) | true | |
|
||||
| firstActiveValue | 默认高亮的选项 | string\|string\[] | - | |
|
||||
| getPopupContainer | 菜单渲染父节点。默认渲染到 body 上,如果你遇到菜单滚动定位问题,试试修改为滚动的区域,并相对其定位。 | Function(triggerNode) | () => document.body | |
|
||||
| labelInValue | 是否把每个选项的 label 包装到 value 中,会把 Select 的 value 类型从 `string` 变为 `{key: string, label: vNodes}` 的格式 | boolean | false | |
|
||||
| maxTagCount | 最多显示多少个 tag | number | - | |
|
||||
| maxTagPlaceholder | 隐藏 tag 时显示的内容 | slot/function(omittedValues) | - | |
|
||||
| maxTagTextLength | 最大显示的 tag 文本长度 | number | - | |
|
||||
| mode | 设置 Select 的模式为多选或标签 | 'multiple' \| 'tags' \| 'combobox' | - | |
|
||||
| notFoundContent | 当下拉列表为空时显示的内容 | string\|slot | 'Not Found' | |
|
||||
| optionFilterProp | 搜索时过滤对应的 option 属性,不支持 children | string | value | |
|
||||
| optionLabelProp | 回填到选择框的 Option 的属性值,默认是 Option 的子元素。比如在子元素需要高亮效果时,此值可以设为 `value`。 | string | `children` (combobox 模式下为 `value`) | |
|
||||
| placeholder | 选择框默认文字 | string\|slot | - | |
|
||||
| showSearch | 使单选模式可搜索 | boolean | false | |
|
||||
| showArrow | 是否显示下拉小箭头 | boolean | true | |
|
||||
| size | 选择框大小,可选 `large` `small` | string | default | |
|
||||
| suffixIcon | 自定义的选择框后缀图标 | VNode \| slot | - | |
|
||||
| removeIcon | 自定义的多选框清除图标 | VNode \| slot | - | |
|
||||
| clearIcon | 自定义的多选框清空图标 | VNode \| slot | - | |
|
||||
| menuItemSelectedIcon | 自定义当前选中的条目图标 | VNode \| slot | - | |
|
||||
| tokenSeparators | 在 tags 和 multiple 模式下自动分词的分隔符 | string\[] | | |
|
||||
| value(v-model) | 指定当前选中的条目 | string\|string\[]\|number\|number\[] | - | |
|
||||
| options | options 数据,如果设置则不需要手动构造 selectOption 节点 | array<{value, label, [disabled, key, title]}> | \[] | |
|
||||
| option | 通过 option 插槽,自定义节点 | v-slot:option="{value, label, [disabled, key, title]}" | - | 2.2.5 |
|
||||
| defaultOpen | 是否默认展开下拉菜单 | boolean | - | |
|
||||
| open | 是否展开下拉菜单 | boolean | - | |
|
||||
| defaultOpen | 是否默认展开下拉菜单 | boolean | - | |
|
||||
| open | 是否展开下拉菜单 | boolean | - | |
|
||||
|
||||
> 注意,如果发现下拉菜单跟随页面滚动,或者需要在其他弹层中触发 Select,请尝试使用 `getPopupContainer={triggerNode => triggerNode.parentNode}` 将下拉弹层渲染节点固定在触发器的父元素中。
|
||||
|
||||
|
@ -109,3 +109,11 @@ cover: https://gw.alipayobjects.com/zos/alicdn/_0XzgOis7/Select.svg
|
|||
### 点击 `dropdownRender` 里的内容浮层关闭怎么办?
|
||||
|
||||
看下 [dropdownRender 例子](/components/select-cn/#components-select-demo-custom-dropdown) 里的说明。
|
||||
|
||||
### 为什么 `placeholder` 不显示 ?
|
||||
|
||||
`placeholder` 只有在 value = undefined 才会显示,对于其它的 null、0、'' 等等对于 JS 语言都是有意义的值。
|
||||
|
||||
你可以查看 [JS 语言规范](https://262.ecma-international.org/5.1/#sec-4.3.9) 进一步了解详情。
|
||||
|
||||
也可以查看 [antd issue](https://github.com/ant-design/ant-design/issues/2367) 查看讨论情况。
|
||||
|
|
|
@ -556,8 +556,8 @@
|
|||
@table-header-bg: @background-color-light;
|
||||
@table-header-color: @heading-color;
|
||||
@table-header-sort-bg: @background-color-base;
|
||||
@table-body-sort-bg: rgba(0, 0, 0, 0.01);
|
||||
@table-row-hover-bg: @primary-1;
|
||||
@table-body-sort-bg: #fafafa;
|
||||
@table-row-hover-bg: @background-color-light;
|
||||
@table-selected-row-color: inherit;
|
||||
@table-selected-row-bg: @primary-1;
|
||||
@table-body-selected-sort-bg: @table-selected-row-bg;
|
||||
|
@ -565,15 +565,31 @@
|
|||
@table-expanded-row-bg: #fbfbfb;
|
||||
@table-padding-vertical: 16px;
|
||||
@table-padding-horizontal: 16px;
|
||||
@table-padding-vertical-md: (@table-padding-vertical * 3 / 4);
|
||||
@table-padding-horizontal-md: (@table-padding-horizontal / 2);
|
||||
@table-padding-vertical-sm: (@table-padding-vertical / 2);
|
||||
@table-padding-horizontal-sm: (@table-padding-horizontal / 2);
|
||||
@table-border-color: @border-color-split;
|
||||
@table-border-radius-base: @border-radius-base;
|
||||
@table-footer-bg: @background-color-light;
|
||||
@table-footer-color: @heading-color;
|
||||
@table-header-bg-sm: transparent;
|
||||
@table-header-bg-sm: @table-header-bg;
|
||||
@table-font-size: @font-size-base;
|
||||
@table-font-size-md: @table-font-size;
|
||||
@table-font-size-sm: @table-font-size;
|
||||
@table-header-cell-split-color: rgba(0, 0, 0, 0.06);
|
||||
// Sorter
|
||||
// Legacy: `table-header-sort-active-bg` is used for hover not real active
|
||||
@table-header-sort-active-bg: darken(@table-header-bg, 3%);
|
||||
@table-header-sort-active-bg: rgba(0, 0, 0, 0.04);
|
||||
// Filter
|
||||
@table-header-filter-active-bg: darken(@table-header-sort-active-bg, 5%);
|
||||
@table-header-filter-active-bg: rgba(0, 0, 0, 0.04);
|
||||
@table-filter-btns-bg: inherit;
|
||||
@table-filter-dropdown-bg: @component-background;
|
||||
@table-expand-icon-bg: @component-background;
|
||||
@table-selection-column-width: 32px;
|
||||
// Sticky
|
||||
@table-sticky-scroll-bar-bg: fade(#000, 35%);
|
||||
@table-sticky-scroll-bar-radius: 4px;
|
||||
|
||||
// Tag
|
||||
// --
|
||||
|
|
|
@ -1,9 +1,10 @@
|
|||
import { defineComponent } from 'vue';
|
||||
import { columnProps } from './interface';
|
||||
import type { ColumnType } from './interface';
|
||||
|
||||
export default defineComponent({
|
||||
export type ColumnProps<RecordType = unknown> = ColumnType<RecordType>;
|
||||
export default defineComponent<ColumnProps>({
|
||||
name: 'ATableColumn',
|
||||
props: columnProps,
|
||||
slots: ['title', 'filterIcon'],
|
||||
render() {
|
||||
return null;
|
||||
},
|
||||
|
|
|
@ -1,15 +1,9 @@
|
|||
import { defineComponent } from 'vue';
|
||||
import PropTypes, { withUndefined } from '../_util/vue-types';
|
||||
import { tuple } from '../_util/type';
|
||||
import type { ColumnGroupProps } from '../vc-table/sugar/ColumnGroup';
|
||||
|
||||
export default defineComponent({
|
||||
export default defineComponent<ColumnGroupProps<any>>({
|
||||
name: 'ATableColumnGroup',
|
||||
props: {
|
||||
fixed: withUndefined(
|
||||
PropTypes.oneOfType([PropTypes.looseBool, PropTypes.oneOf(tuple('left', 'right'))]),
|
||||
),
|
||||
title: PropTypes.any,
|
||||
},
|
||||
slots: ['title'],
|
||||
__ANT_TABLE_COLUMN_GROUP: true,
|
||||
render() {
|
||||
return null;
|
||||
|
|
|
@ -0,0 +1,40 @@
|
|||
import classNames from '../_util/classNames';
|
||||
import type { TableLocale } from './interface';
|
||||
|
||||
interface DefaultExpandIconProps<RecordType> {
|
||||
prefixCls: string;
|
||||
onExpand: (record: RecordType, e: MouseEvent) => void;
|
||||
record: RecordType;
|
||||
expanded: boolean;
|
||||
expandable: boolean;
|
||||
}
|
||||
|
||||
function renderExpandIcon(locale: TableLocale) {
|
||||
return function expandIcon<RecordType>({
|
||||
prefixCls,
|
||||
onExpand,
|
||||
record,
|
||||
expanded,
|
||||
expandable,
|
||||
}: DefaultExpandIconProps<RecordType>) {
|
||||
const iconPrefix = `${prefixCls}-row-expand-icon`;
|
||||
|
||||
return (
|
||||
<button
|
||||
type="button"
|
||||
onClick={e => {
|
||||
onExpand(record, e!);
|
||||
e.stopPropagation();
|
||||
}}
|
||||
class={classNames(iconPrefix, {
|
||||
[`${iconPrefix}-spaced`]: !expandable,
|
||||
[`${iconPrefix}-expanded`]: expandable && expanded,
|
||||
[`${iconPrefix}-collapsed`]: expandable && !expanded,
|
||||
})}
|
||||
aria-label={expanded ? locale.collapse : locale.expand}
|
||||
/>
|
||||
);
|
||||
};
|
||||
}
|
||||
|
||||
export default renderExpandIcon;
|
|
@ -1,20 +0,0 @@
|
|||
import type { FunctionalComponent } from 'vue';
|
||||
|
||||
export interface FilterDropdownMenuWrapperProps {
|
||||
class?: string;
|
||||
}
|
||||
|
||||
const FilterDropdownMenuWrapper: FunctionalComponent<FilterDropdownMenuWrapperProps> = (
|
||||
props,
|
||||
{ slots },
|
||||
) => {
|
||||
return (
|
||||
<div class={props.class} onClick={e => e.stopPropagation()}>
|
||||
{slots.default?.()}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
FilterDropdownMenuWrapper.inheritAttrs = false;
|
||||
|
||||
export default FilterDropdownMenuWrapper;
|
|
@ -1,42 +0,0 @@
|
|||
import { computed, defineComponent } from 'vue';
|
||||
import Checkbox from '../checkbox';
|
||||
import Radio from '../radio';
|
||||
import { SelectionBoxProps } from './interface';
|
||||
import BaseMixin from '../_util/BaseMixin';
|
||||
import { getOptionProps } from '../_util/props-util';
|
||||
|
||||
export default defineComponent({
|
||||
name: 'SelectionBox',
|
||||
mixins: [BaseMixin],
|
||||
inheritAttrs: false,
|
||||
props: SelectionBoxProps,
|
||||
|
||||
setup(props) {
|
||||
return {
|
||||
checked: computed(() => {
|
||||
const { store, defaultSelection, rowIndex } = props;
|
||||
let checked = false;
|
||||
if (store.selectionDirty) {
|
||||
checked = store.selectedRowKeys.indexOf(rowIndex) >= 0;
|
||||
} else {
|
||||
checked =
|
||||
store.selectedRowKeys.indexOf(rowIndex) >= 0 || defaultSelection.indexOf(rowIndex) >= 0;
|
||||
}
|
||||
return checked;
|
||||
}),
|
||||
};
|
||||
},
|
||||
render() {
|
||||
const { type, rowIndex, ...rest } = { ...getOptionProps(this), ...this.$attrs } as any;
|
||||
const { checked } = this;
|
||||
const checkboxProps = {
|
||||
checked,
|
||||
...rest,
|
||||
};
|
||||
if (type === 'radio') {
|
||||
checkboxProps.value = rowIndex;
|
||||
return <Radio {...checkboxProps} />;
|
||||
}
|
||||
return <Checkbox {...checkboxProps} />;
|
||||
},
|
||||
});
|
|
@ -1,188 +0,0 @@
|
|||
import DownOutlined from '@ant-design/icons-vue/DownOutlined';
|
||||
import Checkbox from '../checkbox';
|
||||
import Dropdown from '../dropdown';
|
||||
import Menu from '../menu';
|
||||
import classNames from '../_util/classNames';
|
||||
import { SelectionCheckboxAllProps } from './interface';
|
||||
import BaseMixin from '../_util/BaseMixin';
|
||||
import { computed, defineComponent } from 'vue';
|
||||
|
||||
function checkSelection({
|
||||
store,
|
||||
getCheckboxPropsByItem,
|
||||
getRecordKey,
|
||||
data,
|
||||
type,
|
||||
byDefaultChecked,
|
||||
}) {
|
||||
return byDefaultChecked
|
||||
? data[type]((item, i) => getCheckboxPropsByItem(item, i).defaultChecked)
|
||||
: data[type]((item, i) => store.selectedRowKeys.indexOf(getRecordKey(item, i)) >= 0);
|
||||
}
|
||||
|
||||
function getIndeterminateState(props) {
|
||||
const { store, data } = props;
|
||||
if (!data.length) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const someCheckedNotByDefaultChecked =
|
||||
checkSelection({
|
||||
...props,
|
||||
data,
|
||||
type: 'some',
|
||||
byDefaultChecked: false,
|
||||
}) &&
|
||||
!checkSelection({
|
||||
...props,
|
||||
data,
|
||||
type: 'every',
|
||||
byDefaultChecked: false,
|
||||
});
|
||||
const someCheckedByDefaultChecked =
|
||||
checkSelection({
|
||||
...props,
|
||||
data,
|
||||
type: 'some',
|
||||
byDefaultChecked: true,
|
||||
}) &&
|
||||
!checkSelection({
|
||||
...props,
|
||||
data,
|
||||
type: 'every',
|
||||
byDefaultChecked: true,
|
||||
});
|
||||
|
||||
if (store.selectionDirty) {
|
||||
return someCheckedNotByDefaultChecked;
|
||||
}
|
||||
return someCheckedNotByDefaultChecked || someCheckedByDefaultChecked;
|
||||
}
|
||||
|
||||
function getCheckState(props) {
|
||||
const { store, data } = props;
|
||||
if (!data.length) {
|
||||
return false;
|
||||
}
|
||||
if (store.selectionDirty) {
|
||||
return checkSelection({
|
||||
...props,
|
||||
data,
|
||||
type: 'every',
|
||||
byDefaultChecked: false,
|
||||
});
|
||||
}
|
||||
return (
|
||||
checkSelection({
|
||||
...props,
|
||||
data,
|
||||
type: 'every',
|
||||
byDefaultChecked: false,
|
||||
}) ||
|
||||
checkSelection({
|
||||
...props,
|
||||
data,
|
||||
type: 'every',
|
||||
byDefaultChecked: true,
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
export default defineComponent({
|
||||
name: 'SelectionCheckboxAll',
|
||||
mixins: [BaseMixin],
|
||||
inheritAttrs: false,
|
||||
props: SelectionCheckboxAllProps,
|
||||
|
||||
setup(props) {
|
||||
return {
|
||||
defaultSelections: [],
|
||||
checked: computed(() => {
|
||||
return getCheckState(props);
|
||||
}),
|
||||
indeterminate: computed(() => {
|
||||
return getIndeterminateState(props);
|
||||
}),
|
||||
};
|
||||
},
|
||||
|
||||
created() {
|
||||
const { $props: props } = this;
|
||||
this.defaultSelections = props.hideDefaultSelections
|
||||
? []
|
||||
: [
|
||||
{
|
||||
key: 'all',
|
||||
text: props.locale.selectAll,
|
||||
},
|
||||
{
|
||||
key: 'invert',
|
||||
text: props.locale.selectInvert,
|
||||
},
|
||||
];
|
||||
},
|
||||
methods: {
|
||||
handleSelectAllChange(e) {
|
||||
const { checked } = e.target;
|
||||
this.$emit('select', checked ? 'all' : 'removeAll', 0, null);
|
||||
},
|
||||
|
||||
renderMenus(selections) {
|
||||
return selections.map((selection, index) => {
|
||||
return (
|
||||
<Menu.Item key={selection.key || index}>
|
||||
<div
|
||||
onClick={() => {
|
||||
this.$emit('select', selection.key, index, selection.onSelect);
|
||||
}}
|
||||
>
|
||||
{selection.text}
|
||||
</div>
|
||||
</Menu.Item>
|
||||
);
|
||||
});
|
||||
},
|
||||
},
|
||||
|
||||
render() {
|
||||
const { disabled, prefixCls, selections, getPopupContainer, checked, indeterminate } = this;
|
||||
|
||||
const selectionPrefixCls = `${prefixCls}-selection`;
|
||||
|
||||
let customSelections = null;
|
||||
|
||||
if (selections) {
|
||||
const newSelections = Array.isArray(selections)
|
||||
? this.defaultSelections.concat(selections)
|
||||
: this.defaultSelections;
|
||||
|
||||
const menu = (
|
||||
<Menu class={`${selectionPrefixCls}-menu`} selectedKeys={[]}>
|
||||
{this.renderMenus(newSelections)}
|
||||
</Menu>
|
||||
);
|
||||
|
||||
customSelections =
|
||||
newSelections.length > 0 ? (
|
||||
<Dropdown getPopupContainer={getPopupContainer} overlay={menu}>
|
||||
<div class={`${selectionPrefixCls}-down`}>
|
||||
<DownOutlined />
|
||||
</div>
|
||||
</Dropdown>
|
||||
) : null;
|
||||
}
|
||||
|
||||
return (
|
||||
<div class={selectionPrefixCls}>
|
||||
<Checkbox
|
||||
class={classNames({ [`${selectionPrefixCls}-select-all-custom`]: customSelections })}
|
||||
checked={checked}
|
||||
indeterminate={indeterminate}
|
||||
disabled={disabled}
|
||||
onChange={this.handleSelectAllChange}
|
||||
/>
|
||||
{customSelections}
|
||||
</div>
|
||||
);
|
||||
},
|
||||
});
|
File diff suppressed because it is too large
Load Diff
|
@ -1,129 +0,0 @@
|
|||
import * as Vue from 'vue';
|
||||
import { mount } from '@vue/test-utils';
|
||||
import SelectionBox from '../SelectionBox';
|
||||
|
||||
const getDefaultStore = selectedRowKeys => {
|
||||
return Vue.reactive({
|
||||
selectedRowKeys: selectedRowKeys || [],
|
||||
selectionDirty: false,
|
||||
});
|
||||
};
|
||||
|
||||
describe('SelectionBox', () => {
|
||||
it('unchecked by selectedRowKeys ', () => {
|
||||
const wrapper = mount(SelectionBox, {
|
||||
props: {
|
||||
store: getDefaultStore(),
|
||||
rowIndex: '1',
|
||||
disabled: false,
|
||||
onChange: () => {},
|
||||
defaultSelection: [],
|
||||
},
|
||||
listeners: {
|
||||
change: () => {},
|
||||
},
|
||||
sync: false,
|
||||
});
|
||||
|
||||
expect(wrapper.vm.checked).toEqual(false);
|
||||
});
|
||||
|
||||
it('checked by selectedRowKeys ', () => {
|
||||
const wrapper = mount(SelectionBox, {
|
||||
props: {
|
||||
store: getDefaultStore(['1']),
|
||||
rowIndex: '1',
|
||||
disabled: false,
|
||||
onChange: () => {},
|
||||
defaultSelection: [],
|
||||
},
|
||||
sync: false,
|
||||
});
|
||||
|
||||
expect(wrapper.vm.checked).toEqual(true);
|
||||
});
|
||||
|
||||
it('checked by defaultSelection', () => {
|
||||
const wrapper = mount(SelectionBox, {
|
||||
props: {
|
||||
store: getDefaultStore(),
|
||||
rowIndex: '1',
|
||||
disabled: false,
|
||||
onChange: () => {},
|
||||
defaultSelection: ['1'],
|
||||
},
|
||||
sync: false,
|
||||
});
|
||||
expect(wrapper.vm.checked).toEqual(true);
|
||||
});
|
||||
|
||||
it('checked when store change', () => {
|
||||
const store = getDefaultStore();
|
||||
const wrapper = mount(SelectionBox, {
|
||||
props: {
|
||||
store,
|
||||
rowIndex: '1',
|
||||
disabled: false,
|
||||
onChange: () => {},
|
||||
defaultSelection: [],
|
||||
},
|
||||
sync: false,
|
||||
});
|
||||
|
||||
store.selectedRowKeys = ['1'];
|
||||
store.selectionDirty = true;
|
||||
|
||||
expect(wrapper.vm.checked).toEqual(true);
|
||||
});
|
||||
|
||||
it('passes props to Checkbox', done => {
|
||||
const checkboxProps = {
|
||||
name: 'testName',
|
||||
id: 'testId',
|
||||
};
|
||||
const wrapper = mount(SelectionBox, {
|
||||
props: {
|
||||
store: getDefaultStore(),
|
||||
rowIndex: '1',
|
||||
disabled: false,
|
||||
onChange: () => {},
|
||||
defaultSelection: ['1'],
|
||||
...checkboxProps,
|
||||
},
|
||||
sync: false,
|
||||
});
|
||||
Vue.nextTick(() => {
|
||||
wrapper.findAllComponents({ name: 'ACheckbox' }).forEach(box => {
|
||||
expect(box.props().name).toEqual(checkboxProps.name);
|
||||
expect(box.props().id).toEqual(checkboxProps.id);
|
||||
});
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('passes props to Radios', done => {
|
||||
const radioProps = {
|
||||
name: 'testName',
|
||||
id: 'testId',
|
||||
};
|
||||
const wrapper = mount(SelectionBox, {
|
||||
props: {
|
||||
store: getDefaultStore(),
|
||||
rowIndex: '1',
|
||||
disabled: false,
|
||||
onChange: () => {},
|
||||
defaultSelection: ['1'],
|
||||
type: 'radio',
|
||||
...radioProps,
|
||||
},
|
||||
sync: false,
|
||||
});
|
||||
Vue.nextTick(() => {
|
||||
wrapper.findAllComponents({ name: 'ARadio' }).forEach(radio => {
|
||||
expect(radio.props().name).toEqual(radioProps.name);
|
||||
expect(radio.props().id).toEqual(radioProps.id);
|
||||
});
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
|
@ -2,6 +2,7 @@ import { mount } from '@vue/test-utils';
|
|||
import Table from '..';
|
||||
import * as Vue from 'vue';
|
||||
import { asyncExpect } from '@/tests/utils';
|
||||
import { sleep } from '../../../tests/utils';
|
||||
|
||||
describe('Table.pagination', () => {
|
||||
const columns = [
|
||||
|
@ -33,7 +34,7 @@ describe('Table.pagination', () => {
|
|||
}
|
||||
|
||||
function renderedNames(wrapper) {
|
||||
return wrapper.findAllComponents({ name: 'TableRow' }).map(row => {
|
||||
return wrapper.findAllComponents({ name: 'BodyRow' }).map(row => {
|
||||
return row.props().record.name;
|
||||
});
|
||||
}
|
||||
|
@ -76,7 +77,7 @@ describe('Table.pagination', () => {
|
|||
});
|
||||
});
|
||||
|
||||
xit('paginate data', done => {
|
||||
it('paginate data', done => {
|
||||
const wrapper = mount(Table, getTableOptions());
|
||||
Vue.nextTick(() => {
|
||||
expect(renderedNames(wrapper)).toEqual(['Jack', 'Lucy']);
|
||||
|
@ -89,7 +90,7 @@ describe('Table.pagination', () => {
|
|||
});
|
||||
});
|
||||
|
||||
xit('repaginates when pageSize change', () => {
|
||||
it('repaginates when pageSize change', () => {
|
||||
const wrapper = mount(Table, getTableOptions());
|
||||
wrapper.setProps({ pagination: { pageSize: 1 } });
|
||||
Vue.nextTick(() => {
|
||||
|
@ -97,7 +98,7 @@ describe('Table.pagination', () => {
|
|||
});
|
||||
});
|
||||
|
||||
xit('fires change event', done => {
|
||||
it('fires change event', done => {
|
||||
const handleChange = jest.fn();
|
||||
const handlePaginationChange = jest.fn();
|
||||
const noop = () => {};
|
||||
|
@ -108,8 +109,8 @@ describe('Table.pagination', () => {
|
|||
...pagination,
|
||||
onChange: handlePaginationChange,
|
||||
onShowSizeChange: noop,
|
||||
onChange: handleChange,
|
||||
},
|
||||
onChange: handleChange,
|
||||
}),
|
||||
);
|
||||
Vue.nextTick(() => {
|
||||
|
@ -131,6 +132,7 @@ describe('Table.pagination', () => {
|
|||
{ key: 2, name: 'Tom' },
|
||||
{ key: 3, name: 'Jerry' },
|
||||
],
|
||||
action: 'paginate',
|
||||
},
|
||||
);
|
||||
|
||||
|
@ -156,7 +158,7 @@ describe('Table.pagination', () => {
|
|||
|
||||
// https://github.com/ant-design/ant-design/issues/4532
|
||||
// https://codepen.io/afc163/pen/pWVRJV?editors=001
|
||||
xit('should display pagination as prop pagination change between true and false', async () => {
|
||||
it('should display pagination as prop pagination change between true and false', async () => {
|
||||
const wrapper = mount(Table, getTableOptions());
|
||||
await asyncExpect(() => {
|
||||
expect(wrapper.findAll('.ant-pagination')).toHaveLength(1);
|
||||
|
@ -182,8 +184,8 @@ describe('Table.pagination', () => {
|
|||
});
|
||||
await asyncExpect(() => {
|
||||
expect(wrapper.findAll('.ant-pagination')).toHaveLength(1);
|
||||
expect(wrapper.findAll('.ant-pagination-item')).toHaveLength(1); // pageSize will be 10
|
||||
expect(renderedNames(wrapper)).toHaveLength(4);
|
||||
expect(wrapper.findAll('.ant-pagination-item')).toHaveLength(2); // pageSize will be 10
|
||||
expect(renderedNames(wrapper)).toEqual(['Tom', 'Jerry']);
|
||||
});
|
||||
});
|
||||
|
||||
|
@ -202,30 +204,30 @@ describe('Table.pagination', () => {
|
|||
});
|
||||
});
|
||||
|
||||
xit('specify the position of pagination', async () => {
|
||||
const wrapper = mount(Table, getTableOptions({ pagination: { position: 'top' } }));
|
||||
it('specify the position of pagination', async () => {
|
||||
const wrapper = mount(Table, getTableOptions({ pagination: { position: ['topLeft'] } }));
|
||||
await asyncExpect(() => {
|
||||
expect(wrapper.findAll('.ant-spin-container > *')).toHaveLength(2);
|
||||
expect(wrapper.findAll('.ant-spin-container > *')[0].findAll('.ant-pagination')).toHaveLength(
|
||||
1,
|
||||
);
|
||||
wrapper.setProps({ pagination: { position: 'bottom' } });
|
||||
expect(wrapper.findAll('.ant-pagination')).toHaveLength(1);
|
||||
wrapper.setProps({ pagination: { position: 'bottomRight' } });
|
||||
}, 0);
|
||||
await asyncExpect(() => {
|
||||
expect(wrapper.findAll('.ant-spin-container > *')).toHaveLength(2);
|
||||
expect(wrapper.findAll('.ant-spin-container > *')[1].findAll('.ant-pagination')).toHaveLength(
|
||||
1,
|
||||
);
|
||||
wrapper.setProps({ pagination: { position: 'both' } });
|
||||
expect(wrapper.findAll('.ant-pagination')).toHaveLength(1);
|
||||
wrapper.setProps({ pagination: { position: ['topLeft', 'bottomRight'] } });
|
||||
}, 0);
|
||||
await asyncExpect(() => {
|
||||
expect(wrapper.findAll('.ant-spin-container > *')).toHaveLength(3);
|
||||
expect(wrapper.findAll('.ant-spin-container > *')[0].findAll('.ant-pagination')).toHaveLength(
|
||||
1,
|
||||
);
|
||||
expect(wrapper.findAll('.ant-spin-container > *')[2].findAll('.ant-pagination')).toHaveLength(
|
||||
1,
|
||||
);
|
||||
expect(wrapper.findAll('.ant-pagination')).toHaveLength(2);
|
||||
}, 0);
|
||||
wrapper.setProps({ pagination: { position: ['none', 'none'] } });
|
||||
await sleep();
|
||||
expect(wrapper.findAll('.ant-pagination')).toHaveLength(0);
|
||||
wrapper.setProps({ pagination: { position: ['invalid'] } });
|
||||
await sleep();
|
||||
expect(wrapper.findAll('.ant-pagination')).toHaveLength(1);
|
||||
wrapper.setProps({ pagination: { position: ['invalid', 'invalid'] } });
|
||||
await sleep();
|
||||
expect(wrapper.findAll('.ant-pagination')).toHaveLength(1);
|
||||
});
|
||||
});
|
||||
|
|
|
@ -4,6 +4,15 @@ import Table from '..';
|
|||
import { sleep } from '../../../tests/utils';
|
||||
|
||||
describe('Table.rowSelection', () => {
|
||||
const errorSpy = jest.spyOn(console, 'error').mockImplementation(() => {});
|
||||
|
||||
afterEach(() => {
|
||||
errorSpy.mockReset();
|
||||
});
|
||||
|
||||
afterAll(() => {
|
||||
errorSpy.mockRestore();
|
||||
});
|
||||
const columns = [
|
||||
{
|
||||
title: 'Name',
|
||||
|
@ -30,13 +39,13 @@ describe('Table.rowSelection', () => {
|
|||
};
|
||||
}
|
||||
function renderedNames(wrapper) {
|
||||
return wrapper.findAllComponents({ name: 'TableRow' }).map(row => {
|
||||
return wrapper.findAllComponents({ name: 'BodyRow' }).map(row => {
|
||||
return row.props().record.name;
|
||||
});
|
||||
}
|
||||
|
||||
function getStore(wrapper) {
|
||||
return wrapper.vm.$refs.table.store;
|
||||
function getSelections(wrapper) {
|
||||
return [...wrapper.vm.table.selectedKeySet].sort();
|
||||
}
|
||||
|
||||
it('select by checkbox', async () => {
|
||||
|
@ -46,26 +55,17 @@ describe('Table.rowSelection', () => {
|
|||
checkboxAll.element.checked = true;
|
||||
checkboxAll.trigger('change');
|
||||
await asyncExpect(() => {
|
||||
expect(getStore(wrapper)).toEqual({
|
||||
selectedRowKeys: [0, 1, 2, 3],
|
||||
selectionDirty: true,
|
||||
});
|
||||
expect(getSelections(wrapper)).toEqual([0, 1, 2, 3]);
|
||||
});
|
||||
checkboxes[1].element.checked = false;
|
||||
checkboxes[1].trigger('change');
|
||||
await asyncExpect(() => {
|
||||
expect(getStore(wrapper)).toEqual({
|
||||
selectedRowKeys: [1, 2, 3],
|
||||
selectionDirty: true,
|
||||
});
|
||||
expect(getSelections(wrapper)).toEqual([1, 2, 3]);
|
||||
});
|
||||
checkboxes[1].element.checked = true;
|
||||
checkboxes[1].trigger('change');
|
||||
await asyncExpect(() => {
|
||||
expect(getStore(wrapper)).toEqual({
|
||||
selectedRowKeys: [1, 2, 3, 0],
|
||||
selectionDirty: true,
|
||||
});
|
||||
expect(getSelections(wrapper)).toEqual([0, 1, 2, 3]);
|
||||
});
|
||||
});
|
||||
|
||||
|
@ -77,103 +77,84 @@ describe('Table.rowSelection', () => {
|
|||
radios[0].element.checked = true;
|
||||
radios[0].trigger('change');
|
||||
await asyncExpect(() => {
|
||||
expect(getStore(wrapper)).toEqual({
|
||||
selectedRowKeys: [0],
|
||||
selectionDirty: true,
|
||||
});
|
||||
expect(getSelections(wrapper)).toEqual([0]);
|
||||
});
|
||||
radios[radios.length - 1].element.checked = true;
|
||||
radios[radios.length - 1].trigger('change');
|
||||
await asyncExpect(() => {
|
||||
expect(getStore(wrapper)).toEqual({
|
||||
selectedRowKeys: [3],
|
||||
selectionDirty: true,
|
||||
});
|
||||
expect(getSelections(wrapper)).toEqual([3]);
|
||||
});
|
||||
});
|
||||
|
||||
xit('pass getCheckboxProps to checkbox', async () => {
|
||||
it('pass getCheckboxProps to checkbox', async () => {
|
||||
const rowSelection = {
|
||||
getCheckboxProps: record => ({
|
||||
props: {
|
||||
disabled: record.name === 'Lucy',
|
||||
name: record.name,
|
||||
},
|
||||
disabled: record.name === 'Lucy',
|
||||
name: record.name,
|
||||
}),
|
||||
};
|
||||
|
||||
const wrapper = mount(Table, getTableOptions({ rowSelection }));
|
||||
const checkboxes = wrapper.findAll('input');
|
||||
await sleep();
|
||||
expect(checkboxes[1].props.disabled).toBe(false);
|
||||
expect(checkboxes[1].vnode.data.attrs.name).toEqual(data[0].name);
|
||||
expect(checkboxes[2].vnode.data.attrs.disabled).toBe(true);
|
||||
expect(checkboxes[2].vnode.data.attrs.name).toEqual(data[1].name);
|
||||
expect(checkboxes[1].wrapperElement.disabled).toBe(false);
|
||||
expect(checkboxes[1].wrapperElement.name).toEqual(data[0].name);
|
||||
expect(checkboxes[2].wrapperElement.disabled).toBe(true);
|
||||
expect(checkboxes[2].wrapperElement.name).toEqual(data[1].name);
|
||||
});
|
||||
|
||||
xit('works with pagination', async () => {
|
||||
it('works with pagination', async () => {
|
||||
const wrapper = mount(Table, getTableOptions({ pagination: { pageSize: 2 } }));
|
||||
|
||||
const checkboxAll = wrapper.findAllComponents({ name: 'SelectionCheckboxAll' });
|
||||
await sleep();
|
||||
const checkboxAll = wrapper.find('input');
|
||||
checkboxAll.wrapperElement.checked = true;
|
||||
checkboxAll.trigger('change');
|
||||
const pagers = wrapper.findAllComponents({ name: 'Pager' });
|
||||
checkboxAll.find('input').element.checked = true;
|
||||
checkboxAll.find('input').trigger('change');
|
||||
await asyncExpect(() => {
|
||||
expect(checkboxAll.vm.$data).toEqual({ checked: true, indeterminate: false });
|
||||
expect(wrapper.findComponent({ name: 'ACheckbox' }).props()).toEqual(
|
||||
expect.objectContaining({ checked: true, indeterminate: false }),
|
||||
);
|
||||
});
|
||||
pagers[1].trigger('click');
|
||||
await asyncExpect(() => {
|
||||
expect(checkboxAll.vm.$data).toEqual({ checked: false, indeterminate: false });
|
||||
expect(wrapper.findComponent({ name: 'ACheckbox' }).props()).toEqual(
|
||||
expect.objectContaining({ checked: false, indeterminate: false }),
|
||||
);
|
||||
});
|
||||
pagers[0].trigger('click');
|
||||
await asyncExpect(() => {
|
||||
expect(checkboxAll.vm.$data).toEqual({ checked: true, indeterminate: false });
|
||||
expect(wrapper.findComponent({ name: 'ACheckbox' }).props()).toEqual(
|
||||
expect.objectContaining({ checked: true, indeterminate: false }),
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
// https://github.com/ant-design/ant-design/issues/4020
|
||||
xit('handles defaultChecked', async () => {
|
||||
it('handles defaultChecked', async () => {
|
||||
const rowSelection = {
|
||||
getCheckboxProps: record => {
|
||||
return {
|
||||
props: {
|
||||
defaultChecked: record.key === 0,
|
||||
},
|
||||
defaultChecked: record.key === 0,
|
||||
};
|
||||
},
|
||||
};
|
||||
|
||||
const wrapper = mount(Table, getTableOptions({ rowSelection }));
|
||||
mount(Table, getTableOptions({ rowSelection }));
|
||||
|
||||
await asyncExpect(() => {
|
||||
const checkboxs = wrapper.findAll('input');
|
||||
expect(checkboxs[1].vnode.data.domProps.checked).toBe(true);
|
||||
expect(checkboxs.at(2).vnode.data.domProps.checked).toBe(false);
|
||||
checkboxs.at(2).element.checked = true;
|
||||
checkboxs.at(2).trigger('change');
|
||||
}, 0);
|
||||
|
||||
await asyncExpect(() => {
|
||||
const checkboxs = wrapper.findAll('input');
|
||||
expect(checkboxs[1].vnode.data.domProps.checked).toBe(true);
|
||||
expect(checkboxs.at(2).vnode.data.domProps.checked).toBe(true);
|
||||
}, 1000);
|
||||
expect(errorSpy).toHaveBeenCalledWith(
|
||||
'Warning: [ant-design-vue: Table] Do not set `checked` or `defaultChecked` in `getCheckboxProps`. Please use `selectedRowKeys` instead.',
|
||||
);
|
||||
});
|
||||
|
||||
it('can be controlled', async () => {
|
||||
const wrapper = mount(Table, getTableOptions({ rowSelection: { selectedRowKeys: [0] } }));
|
||||
|
||||
expect(getStore(wrapper)).toEqual({
|
||||
selectedRowKeys: [0],
|
||||
selectionDirty: false,
|
||||
});
|
||||
expect(getSelections(wrapper)).toEqual([0]);
|
||||
|
||||
wrapper.setProps({ rowSelection: { selectedRowKeys: [1] } });
|
||||
await asyncExpect(() => {
|
||||
expect(getStore(wrapper)).toEqual({
|
||||
selectedRowKeys: [1],
|
||||
selectionDirty: false,
|
||||
});
|
||||
expect(getSelections(wrapper)).toEqual([1]);
|
||||
});
|
||||
});
|
||||
|
||||
|
@ -216,7 +197,7 @@ describe('Table.rowSelection', () => {
|
|||
});
|
||||
});
|
||||
|
||||
xit('render with default selection correctly', async () => {
|
||||
it('render with default selection correctly', async () => {
|
||||
const rowSelection = {
|
||||
selections: true,
|
||||
};
|
||||
|
@ -224,7 +205,7 @@ describe('Table.rowSelection', () => {
|
|||
const dropdownWrapper = mount(
|
||||
{
|
||||
render() {
|
||||
return wrapper.find({ name: 'Trigger' }).vm.getComponent();
|
||||
return wrapper.findComponent({ name: 'Trigger' }).vm.getComponent();
|
||||
},
|
||||
},
|
||||
{ sync: false },
|
||||
|
@ -232,14 +213,12 @@ describe('Table.rowSelection', () => {
|
|||
await asyncExpect(() => {
|
||||
expect(dropdownWrapper.html()).toMatchSnapshot();
|
||||
});
|
||||
|
||||
await asyncExpect(() => {});
|
||||
});
|
||||
|
||||
xit('click select all selection', () => {
|
||||
const handleSelectAll = jest.fn();
|
||||
it('click select all selection', () => {
|
||||
const handleChange = jest.fn();
|
||||
const rowSelection = {
|
||||
onSelectAll: handleSelectAll,
|
||||
onChange: handleChange,
|
||||
selections: true,
|
||||
};
|
||||
const wrapper = mount(Table, getTableOptions({ rowSelection }));
|
||||
|
@ -247,17 +226,17 @@ describe('Table.rowSelection', () => {
|
|||
const dropdownWrapper = mount(
|
||||
{
|
||||
render() {
|
||||
return wrapper.find({ name: 'Trigger' }).vm.getComponent();
|
||||
return wrapper.findComponent({ name: 'Trigger' }).vm.getComponent();
|
||||
},
|
||||
},
|
||||
{ sync: false },
|
||||
);
|
||||
dropdownWrapper.findAll('.ant-dropdown-menu-item > div')[0].trigger('click');
|
||||
dropdownWrapper.findAll('.ant-dropdown-menu-item')[0].trigger('click');
|
||||
|
||||
expect(handleSelectAll).toBeCalledWith(true, data, data);
|
||||
expect(handleChange.mock.calls[0][0]).toEqual([0, 1, 2, 3]);
|
||||
});
|
||||
|
||||
xit('fires selectInvert event', () => {
|
||||
it('fires selectInvert event', async () => {
|
||||
const handleSelectInvert = jest.fn();
|
||||
const rowSelection = {
|
||||
onSelectInvert: handleSelectInvert,
|
||||
|
@ -267,25 +246,28 @@ describe('Table.rowSelection', () => {
|
|||
const checkboxes = wrapper.findAll('input');
|
||||
checkboxes[1].element.checked = true;
|
||||
checkboxes[1].trigger('change');
|
||||
await sleep();
|
||||
const dropdownWrapper = mount(
|
||||
{
|
||||
render() {
|
||||
return wrapper.find({ name: 'Trigger' }).vm.getComponent();
|
||||
return wrapper.findComponent({ name: 'Trigger' }).vm.getComponent();
|
||||
},
|
||||
},
|
||||
{ sync: false },
|
||||
);
|
||||
const div = dropdownWrapper.findAll('.ant-dropdown-menu-item > div');
|
||||
div.at(div.length - 1).trigger('click');
|
||||
const div = dropdownWrapper.findAll('li.ant-dropdown-menu-item');
|
||||
div.at(1).trigger('click');
|
||||
|
||||
expect(handleSelectInvert).toBeCalledWith([1, 2, 3]);
|
||||
});
|
||||
|
||||
xit('fires selection event', () => {
|
||||
it('fires selection event', () => {
|
||||
const handleSelectOdd = jest.fn();
|
||||
const handleSelectEven = jest.fn();
|
||||
const rowSelection = {
|
||||
selections: [
|
||||
Table.SELECTION_ALL,
|
||||
Table.SELECTION_INVERT,
|
||||
{
|
||||
key: 'odd',
|
||||
text: '奇数项',
|
||||
|
@ -303,21 +285,21 @@ describe('Table.rowSelection', () => {
|
|||
const dropdownWrapper = mount(
|
||||
{
|
||||
render() {
|
||||
return wrapper.find({ name: 'Trigger' }).vm.getComponent();
|
||||
return wrapper.findComponent({ name: 'Trigger' }).vm.getComponent();
|
||||
},
|
||||
},
|
||||
{ sync: false },
|
||||
);
|
||||
expect(dropdownWrapper.findAll('.ant-dropdown-menu-item').length).toBe(4);
|
||||
|
||||
dropdownWrapper.findAll('.ant-dropdown-menu-item > div').at(2).trigger('click');
|
||||
dropdownWrapper.findAll('.ant-dropdown-menu-item')[2].trigger('click');
|
||||
expect(handleSelectOdd).toBeCalledWith([0, 1, 2, 3]);
|
||||
|
||||
dropdownWrapper.findAll('.ant-dropdown-menu-item > div').at(3).trigger('click');
|
||||
dropdownWrapper.findAll('.ant-dropdown-menu-item').at(3).trigger('click');
|
||||
expect(handleSelectEven).toBeCalledWith([0, 1, 2, 3]);
|
||||
});
|
||||
|
||||
xit('could hide default selection options', () => {
|
||||
it('could hide default selection options', () => {
|
||||
const rowSelection = {
|
||||
hideDefaultSelections: true,
|
||||
selections: [
|
||||
|
@ -335,7 +317,7 @@ describe('Table.rowSelection', () => {
|
|||
const dropdownWrapper = mount(
|
||||
{
|
||||
render() {
|
||||
return wrapper.find({ name: 'Trigger' }).vm.getComponent();
|
||||
return wrapper.findComponent({ name: 'Trigger' }).vm.getComponent();
|
||||
},
|
||||
},
|
||||
{ sync: false },
|
||||
|
@ -343,7 +325,7 @@ describe('Table.rowSelection', () => {
|
|||
expect(dropdownWrapper.findAll('.ant-dropdown-menu-item').length).toBe(2);
|
||||
});
|
||||
|
||||
xit('handle custom selection onSelect correctly when hide default selection options', () => {
|
||||
it('handle custom selection onSelect correctly when hide default selection options', () => {
|
||||
const handleSelectOdd = jest.fn();
|
||||
const handleSelectEven = jest.fn();
|
||||
const rowSelection = {
|
||||
|
@ -366,25 +348,25 @@ describe('Table.rowSelection', () => {
|
|||
const dropdownWrapper = mount(
|
||||
{
|
||||
render() {
|
||||
return wrapper.find({ name: 'Trigger' }).vm.getComponent();
|
||||
return wrapper.findComponent({ name: 'Trigger' }).vm.getComponent();
|
||||
},
|
||||
},
|
||||
{ sync: false },
|
||||
);
|
||||
expect(dropdownWrapper.findAll('.ant-dropdown-menu-item').length).toBe(2);
|
||||
|
||||
dropdownWrapper.findAll('.ant-dropdown-menu-item > div')[0].trigger('click');
|
||||
dropdownWrapper.findAll('.ant-dropdown-menu-item')[0].trigger('click');
|
||||
expect(handleSelectOdd).toBeCalledWith([0, 1, 2, 3]);
|
||||
|
||||
dropdownWrapper.findAll('.ant-dropdown-menu-item > div')[1].trigger('click');
|
||||
dropdownWrapper.findAll('.ant-dropdown-menu-item')[1].trigger('click');
|
||||
expect(handleSelectEven).toBeCalledWith([0, 1, 2, 3]);
|
||||
});
|
||||
|
||||
// https:// github.com/ant-design/ant-design/issues/4245
|
||||
xit('handles disabled checkbox correctly when dataSource changes', async () => {
|
||||
it('handles disabled checkbox correctly when dataSource changes', async () => {
|
||||
const rowSelection = {
|
||||
getCheckboxProps: record => {
|
||||
return { props: { disabled: record.disabled } };
|
||||
return { disabled: record.disabled };
|
||||
},
|
||||
};
|
||||
const wrapper = mount(Table, getTableOptions({ rowSelection }));
|
||||
|
@ -396,14 +378,14 @@ describe('Table.rowSelection', () => {
|
|||
wrapper.setProps({ dataSource: newData });
|
||||
});
|
||||
await asyncExpect(() => {
|
||||
wrapper.findAll('input').wrappers.forEach(checkbox => {
|
||||
expect(checkbox.vnode.data.attrs.disabled).toBe(true);
|
||||
wrapper.findAll('input').forEach(checkbox => {
|
||||
expect(checkbox.wrapperElement.disabled).toBe(true);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
// https://github.com/ant-design/ant-design/issues/4779
|
||||
xit('should not switch pagination when select record', async () => {
|
||||
it('should not switch pagination when select record', async () => {
|
||||
const newData = [];
|
||||
for (let i = 0; i < 20; i += 1) {
|
||||
newData.push({
|
||||
|
@ -418,7 +400,7 @@ describe('Table.rowSelection', () => {
|
|||
dataSource: newData,
|
||||
}),
|
||||
);
|
||||
const pager = wrapper.findAll({ name: 'Pager' });
|
||||
const pager = wrapper.findAllComponents({ name: 'Pager' });
|
||||
pager.at(pager.length - 1).trigger('click'); // switch to second page
|
||||
wrapper.findAll('input')[0].element.checked = true;
|
||||
wrapper.findAll('input')[0].trigger('change');
|
||||
|
@ -460,7 +442,7 @@ describe('Table.rowSelection', () => {
|
|||
});
|
||||
|
||||
// https://github.com/ant-design/ant-design/issues/10629
|
||||
xit('should keep all checked state when remove item from dataSource', async () => {
|
||||
it('should keep all checked state when remove item from dataSource', async () => {
|
||||
const wrapper = mount(Table, {
|
||||
props: {
|
||||
columns,
|
||||
|
@ -510,7 +492,7 @@ describe('Table.rowSelection', () => {
|
|||
sync: false,
|
||||
});
|
||||
await asyncExpect(() => {
|
||||
expect(wrapper.findAll('thead tr div')[0].text()).toBe('多选');
|
||||
expect(wrapper.findAll('thead tr th')[0].text()).toBe('多选');
|
||||
});
|
||||
await asyncExpect(() => {
|
||||
wrapper.setProps({
|
||||
|
@ -521,12 +503,12 @@ describe('Table.rowSelection', () => {
|
|||
});
|
||||
});
|
||||
await asyncExpect(() => {
|
||||
expect(wrapper.findAll('thead tr div')[0].text()).toBe('单选');
|
||||
expect(wrapper.findAll('thead tr th')[0].text()).toBe('单选');
|
||||
});
|
||||
});
|
||||
|
||||
// https://github.com/ant-design/ant-design/issues/11384
|
||||
xit('should keep item even if in filter', async () => {
|
||||
it('should keep item even if in filter', async () => {
|
||||
const filterColumns = [
|
||||
{
|
||||
title: 'Name',
|
||||
|
@ -563,11 +545,21 @@ describe('Table.rowSelection', () => {
|
|||
const dropdownWrapper = mount(
|
||||
{
|
||||
render() {
|
||||
return wrapper.find({ name: 'Trigger' }).vm.getComponent();
|
||||
return wrapper.findComponent({ name: 'Trigger' }).vm.getComponent();
|
||||
},
|
||||
},
|
||||
{ sync: false },
|
||||
);
|
||||
await sleep();
|
||||
function clickFilter(indexList) {
|
||||
indexList.forEach(index => {
|
||||
dropdownWrapper
|
||||
.findAll('.ant-dropdown-menu-item .ant-checkbox-wrapper')
|
||||
.at(index)
|
||||
.trigger('click');
|
||||
});
|
||||
dropdownWrapper.find('.ant-table-filter-dropdown-btns .ant-btn-primary').trigger('click');
|
||||
}
|
||||
|
||||
function clickItem() {
|
||||
wrapper.findAll(
|
||||
|
@ -577,10 +569,7 @@ describe('Table.rowSelection', () => {
|
|||
}
|
||||
|
||||
// Check Jack
|
||||
dropdownWrapper.findAll('.ant-dropdown-menu-item .ant-checkbox-wrapper')[0].trigger('click');
|
||||
dropdownWrapper
|
||||
.find('.ant-table-filter-dropdown-btns .ant-table-filter-dropdown-link.confirm')
|
||||
.trigger('click');
|
||||
clickFilter([0]);
|
||||
await asyncExpect(() => {
|
||||
expect(wrapper.findAll('tbody tr').length).toBe(1);
|
||||
});
|
||||
|
@ -592,19 +581,9 @@ describe('Table.rowSelection', () => {
|
|||
expect(onChange.mock.calls[0][1].length).toBe(1);
|
||||
});
|
||||
|
||||
await asyncExpect(() => {
|
||||
dropdownWrapper.findAll('.ant-dropdown-menu-item .ant-checkbox-wrapper')[0].trigger('click');
|
||||
});
|
||||
// Check Lucy
|
||||
clickFilter([0, 1]);
|
||||
|
||||
await asyncExpect(() => {
|
||||
// Check Lucy
|
||||
dropdownWrapper.findAll('.ant-dropdown-menu-item .ant-checkbox-wrapper')[1].trigger('click');
|
||||
});
|
||||
await asyncExpect(() => {
|
||||
dropdownWrapper
|
||||
.find('.ant-table-filter-dropdown-btns .ant-table-filter-dropdown-link.confirm')
|
||||
.trigger('click');
|
||||
});
|
||||
await asyncExpect(() => {
|
||||
expect(wrapper.findAll('tbody tr').length).toBe(1);
|
||||
});
|
||||
|
@ -617,7 +596,7 @@ describe('Table.rowSelection', () => {
|
|||
});
|
||||
});
|
||||
|
||||
xit('render correctly when set childrenColumnName', async () => {
|
||||
it('render correctly when set childrenColumnName', async () => {
|
||||
const newDatas = [
|
||||
{
|
||||
key: 1,
|
||||
|
@ -652,16 +631,18 @@ describe('Table.rowSelection', () => {
|
|||
});
|
||||
|
||||
const checkboxes = wrapper.findAll('input');
|
||||
const checkboxAll = wrapper.findAllComponents({ name: 'SelectionCheckboxAll' });
|
||||
|
||||
checkboxes[1].element.checked = true;
|
||||
checkboxes[1].trigger('change');
|
||||
expect(checkboxAll.$data).toEqual({ indeterminate: true, checked: false });
|
||||
|
||||
await sleep();
|
||||
expect(wrapper.findComponent({ name: 'ACheckbox' }).props()).toEqual(
|
||||
expect.objectContaining({ checked: false, indeterminate: true }),
|
||||
);
|
||||
checkboxes[2].element.checked = true;
|
||||
checkboxes[2].trigger('change');
|
||||
await asyncExpect(() => {
|
||||
expect(checkboxAll.vm.$data).toEqual({ indeterminate: false, checked: true });
|
||||
expect(wrapper.findComponent({ name: 'ACheckbox' }).props()).toEqual(
|
||||
expect.objectContaining({ checked: true, indeterminate: false }),
|
||||
);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
@ -9,6 +9,7 @@ describe('Table.sorter', () => {
|
|||
const column = {
|
||||
title: 'Name',
|
||||
dataIndex: 'name',
|
||||
key: 'name',
|
||||
sorter: sorterFn,
|
||||
};
|
||||
|
||||
|
@ -38,7 +39,7 @@ describe('Table.sorter', () => {
|
|||
}
|
||||
|
||||
function renderedNames(wrapper) {
|
||||
return wrapper.findAllComponents({ name: 'TableRow' }).wrappers.map(row => {
|
||||
return wrapper.findAllComponents({ name: 'BodyRow' }).map(row => {
|
||||
return row.props().record.name;
|
||||
});
|
||||
}
|
||||
|
@ -51,7 +52,7 @@ describe('Table.sorter', () => {
|
|||
});
|
||||
});
|
||||
|
||||
xit('default sort order ascend', done => {
|
||||
it('default sort order ascend', done => {
|
||||
const wrapper = mount(
|
||||
Table,
|
||||
getTableOptions(
|
||||
|
@ -67,7 +68,7 @@ describe('Table.sorter', () => {
|
|||
});
|
||||
});
|
||||
|
||||
xit('default sort order descend', done => {
|
||||
it('default sort order descend', done => {
|
||||
const wrapper = mount(
|
||||
Table,
|
||||
getTableOptions(
|
||||
|
@ -104,7 +105,7 @@ describe('Table.sorter', () => {
|
|||
});
|
||||
});
|
||||
|
||||
xit('can be controlled by sortOrder', done => {
|
||||
it('can be controlled by sortOrder', done => {
|
||||
const wrapper = mount(
|
||||
Table,
|
||||
getTableOptions({
|
||||
|
@ -148,7 +149,7 @@ describe('Table.sorter', () => {
|
|||
});
|
||||
});
|
||||
|
||||
xit('works with grouping columns in controlled mode', done => {
|
||||
it('works with grouping columns in controlled mode', done => {
|
||||
const columns = [
|
||||
{
|
||||
title: 'group',
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import { shallowMount as shallow, mount } from '@vue/test-utils';
|
||||
import { mount } from '@vue/test-utils';
|
||||
import Table from '..';
|
||||
import * as Vue from 'vue';
|
||||
import mountTest from '../../../tests/shared/mountTest';
|
||||
|
@ -55,7 +55,7 @@ describe('Table', () => {
|
|||
dataIndex: 'name',
|
||||
},
|
||||
];
|
||||
const wrapper = shallow(Table, {
|
||||
const wrapper = mount(Table, {
|
||||
props: {
|
||||
columns,
|
||||
},
|
||||
|
@ -70,7 +70,7 @@ describe('Table', () => {
|
|||
];
|
||||
wrapper.setProps({ columns: newColumns });
|
||||
await sleep();
|
||||
expect(wrapper.vm.columns).toStrictEqual(newColumns);
|
||||
expect(wrapper.find('th').text()).toEqual('Title');
|
||||
});
|
||||
|
||||
it('loading with Spin', async () => {
|
||||
|
|
|
@ -5,52 +5,49 @@ exports[`Table.pagination renders pagination correctly 1`] = `
|
|||
<div class="ant-spin-nested-loading">
|
||||
<!---->
|
||||
<div class="ant-spin-container">
|
||||
<div class="ant-table ant-table-default ant-table-scroll-position-left">
|
||||
<div class="ant-table">
|
||||
<!---->
|
||||
<div class="ant-table-content">
|
||||
<!---->
|
||||
<div class="ant-table-body">
|
||||
<table class="">
|
||||
<colgroup>
|
||||
<col data-key="name">
|
||||
</colgroup>
|
||||
<div class="ant-table-container">
|
||||
<div class="ant-table-content">
|
||||
<table style="table-layout: auto;">
|
||||
<colgroup></colgroup>
|
||||
<thead class="ant-table-thead">
|
||||
<tr>
|
||||
<th colstart="0" colspan="1" colend="0" rowspan="1" class=""><span class="ant-table-header-column"><div><span class="ant-table-column-title">Name</span><span class="ant-table-column-sorter"><!----></span>
|
||||
</div></span>
|
||||
<!---->
|
||||
</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody class="ant-table-tbody">
|
||||
<tr class="ant-table-row ant-table-row-level-0" data-row-key="0">
|
||||
<td class="">
|
||||
<th class="ant-table-cell" colstart="0" colend="0">
|
||||
<!---->Name
|
||||
</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody class="ant-table-tbody">
|
||||
<!---->
|
||||
<!---->Jack
|
||||
</td>
|
||||
</tr>
|
||||
<tr class="ant-table-row ant-table-row-level-0" data-row-key="1">
|
||||
<td class="">
|
||||
<tr data-row-key="0" class="ant-table-row ant-table-row-level-0">
|
||||
<td class="ant-table-cell ant-table-cell-with-append">
|
||||
<!---->Jack
|
||||
</td>
|
||||
</tr>
|
||||
<!---->
|
||||
<!---->Lucy
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
<tr data-row-key="1" class="ant-table-row ant-table-row-level-0">
|
||||
<td class="ant-table-cell ant-table-cell-with-append">
|
||||
<!---->Lucy
|
||||
</td>
|
||||
</tr>
|
||||
<!---->
|
||||
</tbody>
|
||||
<!---->
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
<!---->
|
||||
<!---->
|
||||
</div>
|
||||
<ul unselectable="on" class="ant-pagination my-page">
|
||||
<!---->
|
||||
<li title="Previous Page" class="ant-pagination-prev ant-pagination-disabled" aria-disabled="true"><button class="ant-pagination-item-link" type="button" tabindex="-1" disabled=""><span role="img" aria-label="left" class="anticon anticon-left"><svg focusable="false" class="" data-icon="left" width="1em" height="1em" fill="currentColor" aria-hidden="true" viewBox="64 64 896 896"><path d="M724 218.3V141c0-6.7-7.7-10.4-12.9-6.3L260.3 486.8a31.86 31.86 0 000 50.3l450.8 352.1c5.3 4.1 12.9.4 12.9-6.3v-77.3c0-4.9-2.3-9.6-6.1-12.6l-360-281 360-281.1c3.8-3 6.1-7.7 6.1-12.6z"></path></svg></span></button></li>
|
||||
<li title="1" tabindex="0" class="ant-pagination-item ant-pagination-item-1 ant-pagination-item-active"><a rel="nofollow">1</a></li>
|
||||
<li title="2" tabindex="0" class="ant-pagination-item ant-pagination-item-2"><a rel="nofollow">2</a></li>
|
||||
<li title="Next Page" tabindex="0" class="ant-pagination-next" aria-disabled="false"><button class="ant-pagination-item-link" type="button" tabindex="-1"><span role="img" aria-label="right" class="anticon anticon-right"><svg focusable="false" class="" data-icon="right" width="1em" height="1em" fill="currentColor" aria-hidden="true" viewBox="64 64 896 896"><path d="M765.7 486.8L314.9 134.7A7.97 7.97 0 00302 141v77.3c0 4.9 2.3 9.6 6.1 12.6l360 281.1-360 281.1c-3.9 3-6.1 7.7-6.1 12.6V883c0 6.7 7.7 10.4 12.9 6.3l450.8-352.1a31.96 31.96 0 000-50.4z"></path></svg></span></button></li>
|
||||
<!---->
|
||||
</ul>
|
||||
</div>
|
||||
<ul unselectable="on" class="ant-pagination my-page ant-table-pagination">
|
||||
<!---->
|
||||
<li title="Previous Page" class="ant-pagination-prev ant-pagination-disabled" aria-disabled="true"><button class="ant-pagination-item-link" type="button" tabindex="-1" disabled=""><span role="img" aria-label="left" class="anticon anticon-left"><svg focusable="false" class="" data-icon="left" width="1em" height="1em" fill="currentColor" aria-hidden="true" viewBox="64 64 896 896"><path d="M724 218.3V141c0-6.7-7.7-10.4-12.9-6.3L260.3 486.8a31.86 31.86 0 000 50.3l450.8 352.1c5.3 4.1 12.9.4 12.9-6.3v-77.3c0-4.9-2.3-9.6-6.1-12.6l-360-281 360-281.1c3.8-3 6.1-7.7 6.1-12.6z"></path></svg></span></button></li>
|
||||
<li title="1" tabindex="0" class="ant-pagination-item ant-pagination-item-1 ant-pagination-item-active"><a rel="nofollow">1</a></li>
|
||||
<li title="2" tabindex="0" class="ant-pagination-item ant-pagination-item-2"><a rel="nofollow">2</a></li>
|
||||
<li title="Next Page" tabindex="0" class="ant-pagination-next" aria-disabled="false"><button class="ant-pagination-item-link" type="button" tabindex="-1"><span role="img" aria-label="right" class="anticon anticon-right"><svg focusable="false" class="" data-icon="right" width="1em" height="1em" fill="currentColor" aria-hidden="true" viewBox="64 64 896 896"><path d="M765.7 486.8L314.9 134.7A7.97 7.97 0 00302 141v77.3c0 4.9 2.3 9.6 6.1 12.6l360 281.1-360 281.1c-3.9 3-6.1 7.7-6.1 12.6V883c0 6.7 7.7 10.4 12.9 6.3l450.8-352.1a31.96 31.96 0 000-50.4z"></path></svg></span></button></li>
|
||||
<!---->
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
|
|
|
@ -5,108 +5,117 @@ exports[`Table.rowSelection fix selection column on the left 1`] = `
|
|||
<div class="ant-spin-nested-loading">
|
||||
<!---->
|
||||
<div class="ant-spin-container">
|
||||
<div class="ant-table ant-table-default ant-table-scroll-position-left ant-table-scroll-position-right">
|
||||
<div class="ant-table ant-table-has-fix-left">
|
||||
<!---->
|
||||
<div class="ant-table-content">
|
||||
<div class="ant-table-scroll">
|
||||
<!---->
|
||||
<div class="ant-table-body">
|
||||
<table class="">
|
||||
<colgroup>
|
||||
<col data-key="selection-column" class="ant-table-selection-col">
|
||||
<col data-key="name">
|
||||
</colgroup>
|
||||
<thead class="ant-table-thead">
|
||||
<tr>
|
||||
<th colstart="0" colspan="1" colend="0" rowspan="1" class="ant-table-selection-column"><span class="ant-table-header-column"><div><span class="ant-table-column-title"><div class="ant-table-selection"><label class="ant-checkbox-wrapper"><span class="ant-checkbox"><input type="checkbox" class="ant-checkbox-input"><span class="ant-checkbox-inner"></span></span>
|
||||
<!----></label>
|
||||
<div class="ant-table-container">
|
||||
<div class="ant-table-content">
|
||||
<table style="table-layout: auto;">
|
||||
<colgroup>
|
||||
<col class="ant-table-selection-col">
|
||||
</colgroup>
|
||||
<thead class="ant-table-thead">
|
||||
<tr>
|
||||
<th class="ant-table-cell ant-table-cell-fix-left ant-table-cell-fix-left-last ant-table-selection-column" style="position: sticky; left: 0px;" colstart="0" colend="0">
|
||||
<!---->
|
||||
<div class="ant-table-selection"><label class="ant-checkbox-wrapper"><span class="ant-checkbox"><input type="checkbox" class="ant-checkbox-input"><span class="ant-checkbox-inner"></span></span>
|
||||
<!---->
|
||||
</label>
|
||||
<!---->
|
||||
</div></span><span class="ant-table-column-sorter"><!----></span>
|
||||
</div></span>
|
||||
<!---->
|
||||
</th>
|
||||
<th colstart="1" colspan="1" colend="1" rowspan="1" class=""><span class="ant-table-header-column"><div><span class="ant-table-column-title">Name</span><span class="ant-table-column-sorter"><!----></span>
|
||||
</div></span>
|
||||
</div>
|
||||
</th>
|
||||
<th class="ant-table-cell" colstart="1" colend="1">
|
||||
<!---->Name
|
||||
</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody class="ant-table-tbody">
|
||||
<!---->
|
||||
<tr data-row-key="0" class="ant-table-row ant-table-row-level-0">
|
||||
<td class="ant-table-cell ant-table-cell-fix-left ant-table-cell-fix-left-last ant-table-cell-with-append ant-table-selection-column" style="position: sticky; left: 0px;">
|
||||
<!----><label class="ant-checkbox-wrapper"><span class="ant-checkbox"><input type="checkbox" class="ant-checkbox-input"><span class="ant-checkbox-inner"></span></span>
|
||||
<!---->
|
||||
</label>
|
||||
</td>
|
||||
<td class="ant-table-cell ant-table-cell-with-append">
|
||||
<!---->Jack
|
||||
</td>
|
||||
</tr>
|
||||
<!---->
|
||||
<tr data-row-key="1" class="ant-table-row ant-table-row-level-0">
|
||||
<td class="ant-table-cell ant-table-cell-fix-left ant-table-cell-fix-left-last ant-table-cell-with-append ant-table-selection-column" style="position: sticky; left: 0px;">
|
||||
<!----><label class="ant-checkbox-wrapper"><span class="ant-checkbox"><input type="checkbox" class="ant-checkbox-input"><span class="ant-checkbox-inner"></span></span>
|
||||
<!---->
|
||||
</label>
|
||||
</td>
|
||||
<td class="ant-table-cell ant-table-cell-with-append">
|
||||
<!---->Lucy
|
||||
</td>
|
||||
</tr>
|
||||
<!---->
|
||||
<tr data-row-key="2" class="ant-table-row ant-table-row-level-0">
|
||||
<td class="ant-table-cell ant-table-cell-fix-left ant-table-cell-fix-left-last ant-table-cell-with-append ant-table-selection-column" style="position: sticky; left: 0px;">
|
||||
<!----><label class="ant-checkbox-wrapper"><span class="ant-checkbox"><input type="checkbox" class="ant-checkbox-input"><span class="ant-checkbox-inner"></span></span>
|
||||
<!---->
|
||||
</label>
|
||||
</td>
|
||||
<td class="ant-table-cell ant-table-cell-with-append">
|
||||
<!---->Tom
|
||||
</td>
|
||||
</tr>
|
||||
<!---->
|
||||
<tr data-row-key="3" class="ant-table-row ant-table-row-level-0">
|
||||
<td class="ant-table-cell ant-table-cell-fix-left ant-table-cell-fix-left-last ant-table-cell-with-append ant-table-selection-column" style="position: sticky; left: 0px;">
|
||||
<!----><label class="ant-checkbox-wrapper"><span class="ant-checkbox"><input type="checkbox" class="ant-checkbox-input"><span class="ant-checkbox-inner"></span></span>
|
||||
<!---->
|
||||
</label>
|
||||
</td>
|
||||
<td class="ant-table-cell ant-table-cell-with-append">
|
||||
<!---->Jerry
|
||||
</td>
|
||||
</tr>
|
||||
<!---->
|
||||
</tbody>
|
||||
<!---->
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
<!---->
|
||||
</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody class="ant-table-tbody">
|
||||
<tr class="ant-table-row ant-table-row-level-0" data-row-key="0">
|
||||
<td class="ant-table-selection-column">
|
||||
<!---->
|
||||
<!----><span><label class="ant-checkbox-wrapper"><span class="ant-checkbox"><input type="checkbox" class="ant-checkbox-input"><span class="ant-checkbox-inner"></span></span>
|
||||
<!----></label></span>
|
||||
</td>
|
||||
<td class="">
|
||||
<!---->
|
||||
<!---->Jack
|
||||
</td>
|
||||
</tr>
|
||||
<tr class="ant-table-row ant-table-row-level-0" data-row-key="1">
|
||||
<td class="ant-table-selection-column">
|
||||
<!---->
|
||||
<!----><span><label class="ant-checkbox-wrapper"><span class="ant-checkbox"><input type="checkbox" class="ant-checkbox-input"><span class="ant-checkbox-inner"></span></span>
|
||||
<!----></label></span>
|
||||
</td>
|
||||
<td class="">
|
||||
<!---->
|
||||
<!---->Lucy
|
||||
</td>
|
||||
</tr>
|
||||
<tr class="ant-table-row ant-table-row-level-0" data-row-key="2">
|
||||
<td class="ant-table-selection-column">
|
||||
<!---->
|
||||
<!----><span><label class="ant-checkbox-wrapper"><span class="ant-checkbox"><input type="checkbox" class="ant-checkbox-input"><span class="ant-checkbox-inner"></span></span>
|
||||
<!----></label></span>
|
||||
</td>
|
||||
<td class="">
|
||||
<!---->
|
||||
<!---->Tom
|
||||
</td>
|
||||
</tr>
|
||||
<tr class="ant-table-row ant-table-row-level-0" data-row-key="3">
|
||||
<td class="ant-table-selection-column">
|
||||
<!---->
|
||||
<!----><span><label class="ant-checkbox-wrapper"><span class="ant-checkbox"><input type="checkbox" class="ant-checkbox-input"><span class="ant-checkbox-inner"></span></span>
|
||||
<!----></label></span>
|
||||
</td>
|
||||
<td class="">
|
||||
<!---->
|
||||
<!---->Jerry
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
<!---->
|
||||
<!---->
|
||||
<ul unselectable="on" class="ant-pagination ant-table-pagination ant-table-pagination-right">
|
||||
<!---->
|
||||
<li title="Previous Page" class="ant-pagination-prev ant-pagination-disabled" aria-disabled="true"><button class="ant-pagination-item-link" type="button" tabindex="-1" disabled=""><span role="img" aria-label="left" class="anticon anticon-left"><svg focusable="false" class="" data-icon="left" width="1em" height="1em" fill="currentColor" aria-hidden="true" viewBox="64 64 896 896"><path d="M724 218.3V141c0-6.7-7.7-10.4-12.9-6.3L260.3 486.8a31.86 31.86 0 000 50.3l450.8 352.1c5.3 4.1 12.9.4 12.9-6.3v-77.3c0-4.9-2.3-9.6-6.1-12.6l-360-281 360-281.1c3.8-3 6.1-7.7 6.1-12.6z"></path></svg></span></button></li>
|
||||
<li title="1" tabindex="0" class="ant-pagination-item ant-pagination-item-1 ant-pagination-item-active"><a rel="nofollow">1</a></li>
|
||||
<li title="Next Page" class="ant-pagination-next ant-pagination-disabled" aria-disabled="true"><button class="ant-pagination-item-link" type="button" tabindex="-1" disabled=""><span role="img" aria-label="right" class="anticon anticon-right"><svg focusable="false" class="" data-icon="right" width="1em" height="1em" fill="currentColor" aria-hidden="true" viewBox="64 64 896 896"><path d="M765.7 486.8L314.9 134.7A7.97 7.97 0 00302 141v77.3c0 4.9 2.3 9.6 6.1 12.6l360 281.1-360 281.1c-3.9 3-6.1 7.7-6.1 12.6V883c0 6.7 7.7 10.4 12.9 6.3l450.8-352.1a31.96 31.96 0 000-50.4z"></path></svg></span></button></li>
|
||||
<!---->
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<ul unselectable="on" class="ant-pagination ant-table-pagination">
|
||||
<!---->
|
||||
<li title="Previous Page" class="ant-pagination-prev ant-pagination-disabled" aria-disabled="true"><button class="ant-pagination-item-link" type="button" tabindex="-1" disabled=""><span role="img" aria-label="left" class="anticon anticon-left"><svg focusable="false" class="" data-icon="left" width="1em" height="1em" fill="currentColor" aria-hidden="true" viewBox="64 64 896 896"><path d="M724 218.3V141c0-6.7-7.7-10.4-12.9-6.3L260.3 486.8a31.86 31.86 0 000 50.3l450.8 352.1c5.3 4.1 12.9.4 12.9-6.3v-77.3c0-4.9-2.3-9.6-6.1-12.6l-360-281 360-281.1c3.8-3 6.1-7.7 6.1-12.6z"></path></svg></span></button></li>
|
||||
<li title="1" tabindex="0" class="ant-pagination-item ant-pagination-item-1 ant-pagination-item-active"><a rel="nofollow">1</a></li>
|
||||
<li title="Next Page" class="ant-pagination-next ant-pagination-disabled" aria-disabled="true"><button class="ant-pagination-item-link" type="button" tabindex="-1" disabled=""><span role="img" aria-label="right" class="anticon anticon-right"><svg focusable="false" class="" data-icon="right" width="1em" height="1em" fill="currentColor" aria-hidden="true" viewBox="64 64 896 896"><path d="M765.7 486.8L314.9 134.7A7.97 7.97 0 00302 141v77.3c0 4.9 2.3 9.6 6.1 12.6l360 281.1-360 281.1c-3.9 3-6.1 7.7-6.1 12.6V883c0 6.7 7.7 10.4 12.9 6.3l450.8-352.1a31.96 31.96 0 000-50.4z"></path></svg></span></button></li>
|
||||
<!---->
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
|
||||
exports[`Table.rowSelection render with default selection correctly 1`] = `
|
||||
<div>
|
||||
<div class="ant-dropdown ant-dropdown-placement-bottomLeft" style="display: none;">
|
||||
<ul role="menu" tabindex="0" class="ant-dropdown-menu ant-dropdown-menu-vertical ant-dropdown-menu-root ant-dropdown-menu-light ant-table-selection-menu ant-dropdown-content">
|
||||
<li role="menuitem" class="ant-dropdown-menu-item">
|
||||
<div>Select current page</div>
|
||||
</li>
|
||||
<li role="menuitem" class="ant-dropdown-menu-item">
|
||||
<div>Invert current page</div>
|
||||
</li>
|
||||
</ul>
|
||||
<!---->
|
||||
<div class="ant-dropdown" style="pointer-events: none; display: none;">
|
||||
<div class="ant-dropdown-content">
|
||||
<!---->
|
||||
<ul class="ant-dropdown-menu ant-dropdown-menu-root ant-dropdown-menu-vertical ant-dropdown-menu-light" role="menu" data-menu-list="true">
|
||||
<!---->
|
||||
<li class="ant-dropdown-menu-item ant-dropdown-menu-item-only-child" role="menuitem" tabindex="-1" data-menu-id="all" aria-disabled="false">
|
||||
<!----><span class="ant-dropdown-menu-title-content">Select all data</span>
|
||||
</li>
|
||||
<!---->
|
||||
<li class="ant-dropdown-menu-item ant-dropdown-menu-item-only-child" role="menuitem" tabindex="-1" data-menu-id="invert" aria-disabled="false">
|
||||
<!----><span class="ant-dropdown-menu-title-content">Invert current page</span>
|
||||
</li>
|
||||
<!---->
|
||||
<li class="ant-dropdown-menu-item ant-dropdown-menu-item-only-child" role="menuitem" tabindex="-1" data-menu-id="none" aria-disabled="false">
|
||||
<!----><span class="ant-dropdown-menu-title-content">Clear all data</span>
|
||||
</li>
|
||||
<!---->
|
||||
<!---->
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
|
|
|
@ -3,8 +3,9 @@
|
|||
exports[`Table.sorter renders sorter icon correctly 1`] = `
|
||||
<thead class="ant-table-thead">
|
||||
<tr>
|
||||
<th colstart="0" colspan="1" colend="0" rowspan="1" class="ant-table-column-has-actions ant-table-column-has-sorters"><span class="ant-table-header-column"><div class="ant-table-column-sorters"><span class="ant-table-column-title">Name</span><span class="ant-table-column-sorter"><div title="Sort" class="ant-table-column-sorter-inner ant-table-column-sorter-inner-full"><span role="img" aria-label="caret-up" class="anticon anticon-caret-up ant-table-column-sorter-up off"><svg focusable="false" class="" data-icon="caret-up" width="1em" height="1em" fill="currentColor" aria-hidden="true" viewBox="0 0 1024 1024"><path d="M858.9 689L530.5 308.2c-9.4-10.9-27.5-10.9-37 0L165.1 689c-12.2 14.2-1.2 35 18.5 35h656.8c19.7 0 30.7-20.8 18.5-35z"></path></svg></span><span role="img" aria-label="caret-down" class="anticon anticon-caret-down ant-table-column-sorter-down off"><svg focusable="false" class="" data-icon="caret-down" width="1em" height="1em" fill="currentColor" aria-hidden="true" viewBox="0 0 1024 1024"><path d="M840.4 300H183.6c-19.7 0-30.7 20.8-18.5 35l328.4 380.8c9.4 10.9 27.5 10.9 37 0L858.9 335c12.2-14.2 1.2-35-18.5-35z"></path></svg></span></div></span></div></span>
|
||||
<th class="ant-table-cell ant-table-column-has-sorters" colstart="0" colend="0">
|
||||
<!---->
|
||||
<div class="ant-table-column-sorters"><span class="ant-table-column-title">Name</span><span class="ant-table-column-sorter ant-table-column-sorter-full"><span class="ant-table-column-sorter-inner"><span role="img" aria-label="caret-up" class="anticon anticon-caret-up ant-table-column-sorter-up"><svg focusable="false" class="" data-icon="caret-up" width="1em" height="1em" fill="currentColor" aria-hidden="true" viewBox="0 0 1024 1024"><path d="M858.9 689L530.5 308.2c-9.4-10.9-27.5-10.9-37 0L165.1 689c-12.2 14.2-1.2 35 18.5 35h656.8c19.7 0 30.7-20.8 18.5-35z"></path></svg></span><span role="img" aria-label="caret-down" class="anticon anticon-caret-down ant-table-column-sorter-down"><svg focusable="false" class="" data-icon="caret-down" width="1em" height="1em" fill="currentColor" aria-hidden="true" viewBox="0 0 1024 1024"><path d="M840.4 300H183.6c-19.7 0-30.7 20.8-18.5 35l328.4 380.8c9.4 10.9 27.5 10.9 37 0L858.9 335c12.2-14.2 1.2-35-18.5-35z"></path></svg></span></span></span></div>
|
||||
</th>
|
||||
</tr>
|
||||
</thead>
|
||||
|
|
|
@ -5,67 +5,60 @@ exports[`Table align column should not override cell style 1`] = `
|
|||
<div class="ant-spin-nested-loading">
|
||||
<!---->
|
||||
<div class="ant-spin-container">
|
||||
<div class="ant-table ant-table-default ant-table-scroll-position-left">
|
||||
<div class="ant-table">
|
||||
<!---->
|
||||
<div class="ant-table-content">
|
||||
<!---->
|
||||
<div class="ant-table-body">
|
||||
<table class="">
|
||||
<colgroup>
|
||||
<col data-key="name">
|
||||
<col data-key="age">
|
||||
</colgroup>
|
||||
<div class="ant-table-container">
|
||||
<div class="ant-table-content">
|
||||
<table style="table-layout: auto;">
|
||||
<colgroup></colgroup>
|
||||
<thead class="ant-table-thead">
|
||||
<tr>
|
||||
<th colstart="0" colspan="1" colend="0" rowspan="1" class=""><span class="ant-table-header-column"><div><span class="ant-table-column-title">Name</span><span class="ant-table-column-sorter"><!----></span>
|
||||
</div></span>
|
||||
<!---->
|
||||
</th>
|
||||
<th colstart="1" colspan="1" colend="1" rowspan="1" style="text-align: center;" class="ant-table-align-center"><span class="ant-table-header-column"><div><span class="ant-table-column-title">Age</span><span class="ant-table-column-sorter"><!----></span>
|
||||
</div></span>
|
||||
<th class="ant-table-cell" colstart="0" colend="0">
|
||||
<!---->Name
|
||||
</th>
|
||||
<th class="ant-table-cell" style="text-align: center;" colstart="1" colend="1">
|
||||
<!---->Age
|
||||
</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody class="ant-table-tbody">
|
||||
<!---->
|
||||
<tr data-row-key="1" class="ant-table-row ant-table-row-level-0">
|
||||
<td class="ant-table-cell ant-table-cell-with-append">
|
||||
<!---->
|
||||
<!---->
|
||||
</td>
|
||||
<td style="color: red; text-align: center;" class="ant-table-cell ant-table-cell-with-append">
|
||||
<!---->32
|
||||
</td>
|
||||
</tr>
|
||||
<!---->
|
||||
<tr data-row-key="2" class="ant-table-row ant-table-row-level-0">
|
||||
<td class="ant-table-cell ant-table-cell-with-append">
|
||||
<!---->
|
||||
<!---->
|
||||
</td>
|
||||
<td style="color: red; text-align: center;" class="ant-table-cell ant-table-cell-with-append">
|
||||
<!---->42
|
||||
</td>
|
||||
</tr>
|
||||
<!---->
|
||||
</tbody>
|
||||
<!---->
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
<!---->
|
||||
</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody class="ant-table-tbody">
|
||||
<tr class="ant-table-row ant-table-row-level-0" data-row-key="1">
|
||||
<td class="">
|
||||
<!---->
|
||||
<!---->
|
||||
<!---->
|
||||
</td>
|
||||
<td class="" style="text-align: center; color: red;">
|
||||
<!---->
|
||||
<!---->32
|
||||
</td>
|
||||
</tr>
|
||||
<tr class="ant-table-row ant-table-row-level-0" data-row-key="2">
|
||||
<td class="">
|
||||
<!---->
|
||||
<!---->
|
||||
<!---->
|
||||
</td>
|
||||
<td class="" style="text-align: center; color: red;">
|
||||
<!---->
|
||||
<!---->42
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
<!---->
|
||||
<!---->
|
||||
<ul unselectable="on" class="ant-pagination ant-table-pagination ant-table-pagination-right">
|
||||
<!---->
|
||||
<li title="Previous Page" class="ant-pagination-prev ant-pagination-disabled" aria-disabled="true"><button class="ant-pagination-item-link" type="button" tabindex="-1" disabled=""><span role="img" aria-label="left" class="anticon anticon-left"><svg focusable="false" class="" data-icon="left" width="1em" height="1em" fill="currentColor" aria-hidden="true" viewBox="64 64 896 896"><path d="M724 218.3V141c0-6.7-7.7-10.4-12.9-6.3L260.3 486.8a31.86 31.86 0 000 50.3l450.8 352.1c5.3 4.1 12.9.4 12.9-6.3v-77.3c0-4.9-2.3-9.6-6.1-12.6l-360-281 360-281.1c3.8-3 6.1-7.7 6.1-12.6z"></path></svg></span></button></li>
|
||||
<li title="1" tabindex="0" class="ant-pagination-item ant-pagination-item-1 ant-pagination-item-active"><a rel="nofollow">1</a></li>
|
||||
<li title="Next Page" class="ant-pagination-next ant-pagination-disabled" aria-disabled="true"><button class="ant-pagination-item-link" type="button" tabindex="-1" disabled=""><span role="img" aria-label="right" class="anticon anticon-right"><svg focusable="false" class="" data-icon="right" width="1em" height="1em" fill="currentColor" aria-hidden="true" viewBox="64 64 896 896"><path d="M765.7 486.8L314.9 134.7A7.97 7.97 0 00302 141v77.3c0 4.9 2.3 9.6 6.1 12.6l360 281.1-360 281.1c-3.9 3-6.1 7.7-6.1 12.6V883c0 6.7 7.7 10.4 12.9 6.3l450.8-352.1a31.96 31.96 0 000-50.4z"></path></svg></span></button></li>
|
||||
<!---->
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
<ul unselectable="on" class="ant-pagination ant-table-pagination">
|
||||
<!---->
|
||||
<li title="Previous Page" class="ant-pagination-prev ant-pagination-disabled" aria-disabled="true"><button class="ant-pagination-item-link" type="button" tabindex="-1" disabled=""><span role="img" aria-label="left" class="anticon anticon-left"><svg focusable="false" class="" data-icon="left" width="1em" height="1em" fill="currentColor" aria-hidden="true" viewBox="64 64 896 896"><path d="M724 218.3V141c0-6.7-7.7-10.4-12.9-6.3L260.3 486.8a31.86 31.86 0 000 50.3l450.8 352.1c5.3 4.1 12.9.4 12.9-6.3v-77.3c0-4.9-2.3-9.6-6.1-12.6l-360-281 360-281.1c3.8-3 6.1-7.7 6.1-12.6z"></path></svg></span></button></li>
|
||||
<li title="1" tabindex="0" class="ant-pagination-item ant-pagination-item-1 ant-pagination-item-active"><a rel="nofollow">1</a></li>
|
||||
<li title="Next Page" class="ant-pagination-next ant-pagination-disabled" aria-disabled="true"><button class="ant-pagination-item-link" type="button" tabindex="-1" disabled=""><span role="img" aria-label="right" class="anticon anticon-right"><svg focusable="false" class="" data-icon="right" width="1em" height="1em" fill="currentColor" aria-hidden="true" viewBox="64 64 896 896"><path d="M765.7 486.8L314.9 134.7A7.97 7.97 0 00302 141v77.3c0 4.9 2.3 9.6 6.1 12.6l360 281.1-360 281.1c-3.9 3-6.1 7.7-6.1 12.6V883c0 6.7 7.7 10.4 12.9 6.3l450.8-352.1a31.96 31.96 0 000-50.4z"></path></svg></span></button></li>
|
||||
<!---->
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
|
||||
|
@ -74,76 +67,64 @@ exports[`Table renders JSX correctly 1`] = `
|
|||
<div class="ant-spin-nested-loading">
|
||||
<!---->
|
||||
<div class="ant-spin-container">
|
||||
<div class="ant-table ant-table-default ant-table-scroll-position-left">
|
||||
<div class="ant-table">
|
||||
<!---->
|
||||
<div class="ant-table-content">
|
||||
<!---->
|
||||
<div class="ant-table-body">
|
||||
<table class="">
|
||||
<colgroup>
|
||||
<col data-key="firstName">
|
||||
<col data-key="lastName">
|
||||
<col data-key="age">
|
||||
</colgroup>
|
||||
<div class="ant-table-container">
|
||||
<div class="ant-table-content">
|
||||
<table style="table-layout: auto;">
|
||||
<colgroup></colgroup>
|
||||
<thead class="ant-table-thead">
|
||||
<tr>
|
||||
<th colstart="0" hassubcolumns="true" colspan="2" colend="1" class=""><span class="ant-table-header-column"><div><span class="ant-table-column-title">Name</span><span class="ant-table-column-sorter"><!----></span>
|
||||
</div></span>
|
||||
<!---->
|
||||
</th>
|
||||
<th colstart="2" rowspan="2" colspan="1" colend="2" class=""><span class="ant-table-header-column"><div><span class="ant-table-column-title">Age</span><span class="ant-table-column-sorter"><!----></span>
|
||||
</div></span>
|
||||
<th colspan="2" class="ant-table-cell" colstart="0" hassubcolumns="true" colend="1">
|
||||
<!---->Name
|
||||
</th>
|
||||
<th rowspan="2" class="ant-table-cell" colstart="2" colend="2">
|
||||
<!---->Age
|
||||
</th>
|
||||
</tr>
|
||||
<tr>
|
||||
<th class="ant-table-cell" colstart="0" colend="0">
|
||||
<!---->First Name
|
||||
</th>
|
||||
<th class="ant-table-cell" colstart="1" colend="1">
|
||||
<!---->Last Name
|
||||
</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody class="ant-table-tbody">
|
||||
<!---->
|
||||
<tr data-row-key="1" class="ant-table-row ant-table-row-level-0">
|
||||
<td class="ant-table-cell ant-table-cell-with-append">
|
||||
<!---->John
|
||||
</td>
|
||||
<td class="ant-table-cell ant-table-cell-with-append">
|
||||
<!---->Brown
|
||||
</td>
|
||||
<td class="ant-table-cell ant-table-cell-with-append">
|
||||
<!---->32
|
||||
</td>
|
||||
</tr>
|
||||
<!---->
|
||||
<tr data-row-key="2" class="ant-table-row ant-table-row-level-0">
|
||||
<td class="ant-table-cell ant-table-cell-with-append">
|
||||
<!---->Jim
|
||||
</td>
|
||||
<td class="ant-table-cell ant-table-cell-with-append">
|
||||
<!---->Green
|
||||
</td>
|
||||
<td class="ant-table-cell ant-table-cell-with-append">
|
||||
<!---->42
|
||||
</td>
|
||||
</tr>
|
||||
<!---->
|
||||
</tbody>
|
||||
<!---->
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
<!---->
|
||||
</th>
|
||||
</tr>
|
||||
<tr>
|
||||
<th colstart="0" colspan="1" colend="0" rowspan="1" class=""><span class="ant-table-header-column"><div><span class="ant-table-column-title">First Name</span><span class="ant-table-column-sorter"><!----></span>
|
||||
</div></span>
|
||||
<!---->
|
||||
</th>
|
||||
<th colstart="1" colspan="1" colend="1" rowspan="1" class=""><span class="ant-table-header-column"><div><span class="ant-table-column-title">Last Name</span><span class="ant-table-column-sorter"><!----></span>
|
||||
</div></span>
|
||||
<!---->
|
||||
</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody class="ant-table-tbody">
|
||||
<tr class="ant-table-row ant-table-row-level-0" data-row-key="1">
|
||||
<td class="">
|
||||
<!---->
|
||||
<!---->John
|
||||
</td>
|
||||
<td class="">
|
||||
<!---->
|
||||
<!---->Brown
|
||||
</td>
|
||||
<td class="">
|
||||
<!---->
|
||||
<!---->32
|
||||
</td>
|
||||
</tr>
|
||||
<tr class="ant-table-row ant-table-row-level-0" data-row-key="2">
|
||||
<td class="">
|
||||
<!---->
|
||||
<!---->Jim
|
||||
</td>
|
||||
<td class="">
|
||||
<!---->
|
||||
<!---->Green
|
||||
</td>
|
||||
<td class="">
|
||||
<!---->
|
||||
<!---->42
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!---->
|
||||
<!---->
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -5,80 +5,57 @@ exports[`Table renders empty table 1`] = `
|
|||
<div class="ant-spin-nested-loading">
|
||||
<!---->
|
||||
<div class="ant-spin-container">
|
||||
<div class="ant-table ant-table-default ant-table-empty ant-table-scroll-position-left">
|
||||
<div class="ant-table ant-table-empty">
|
||||
<!---->
|
||||
<div class="ant-table-content">
|
||||
<!---->
|
||||
<div class="ant-table-body">
|
||||
<table class="">
|
||||
<colgroup>
|
||||
<col data-key="1">
|
||||
<col data-key="2">
|
||||
<col data-key="3">
|
||||
<col data-key="4">
|
||||
<col data-key="5">
|
||||
<col data-key="6">
|
||||
<col data-key="7">
|
||||
<col data-key="8">
|
||||
</colgroup>
|
||||
<div class="ant-table-container">
|
||||
<div class="ant-table-content">
|
||||
<table style="table-layout: auto;">
|
||||
<colgroup></colgroup>
|
||||
<thead class="ant-table-thead">
|
||||
<tr>
|
||||
<th colstart="0" colspan="1" colend="0" rowspan="1" class=""><span class="ant-table-header-column"><div><span class="ant-table-column-title">Column 1</span><span class="ant-table-column-sorter"><!----></span>
|
||||
</div></span>
|
||||
<!---->
|
||||
</th>
|
||||
<th colstart="1" colspan="1" colend="1" rowspan="1" class=""><span class="ant-table-header-column"><div><span class="ant-table-column-title">Column 2</span><span class="ant-table-column-sorter"><!----></span>
|
||||
</div></span>
|
||||
<th class="ant-table-cell" colstart="0" colend="0">
|
||||
<!---->Column 1
|
||||
</th>
|
||||
<th class="ant-table-cell" colstart="1" colend="1">
|
||||
<!---->Column 2
|
||||
</th>
|
||||
<th class="ant-table-cell" colstart="2" colend="2">
|
||||
<!---->Column 3
|
||||
</th>
|
||||
<th class="ant-table-cell" colstart="3" colend="3">
|
||||
<!---->Column 4
|
||||
</th>
|
||||
<th class="ant-table-cell" colstart="4" colend="4">
|
||||
<!---->Column 5
|
||||
</th>
|
||||
<th class="ant-table-cell" colstart="5" colend="5">
|
||||
<!---->Column 6
|
||||
</th>
|
||||
<th class="ant-table-cell" colstart="6" colend="6">
|
||||
<!---->Column 7
|
||||
</th>
|
||||
<th class="ant-table-cell" colstart="7" colend="7">
|
||||
<!---->Column 8
|
||||
</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody class="ant-table-tbody">
|
||||
<!---->
|
||||
<tr class="ant-table-placeholder">
|
||||
<td colspan="8" class="ant-table-cell">
|
||||
<!---->No data
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
<!---->
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
<!---->
|
||||
</th>
|
||||
<th colstart="2" colspan="1" colend="2" rowspan="1" class=""><span class="ant-table-header-column"><div><span class="ant-table-column-title">Column 3</span><span class="ant-table-column-sorter"><!----></span>
|
||||
</div></span>
|
||||
<!---->
|
||||
</th>
|
||||
<th colstart="3" colspan="1" colend="3" rowspan="1" class=""><span class="ant-table-header-column"><div><span class="ant-table-column-title">Column 4</span><span class="ant-table-column-sorter"><!----></span>
|
||||
</div></span>
|
||||
<!---->
|
||||
</th>
|
||||
<th colstart="4" colspan="1" colend="4" rowspan="1" class=""><span class="ant-table-header-column"><div><span class="ant-table-column-title">Column 5</span><span class="ant-table-column-sorter"><!----></span>
|
||||
</div></span>
|
||||
<!---->
|
||||
</th>
|
||||
<th colstart="5" colspan="1" colend="5" rowspan="1" class=""><span class="ant-table-header-column"><div><span class="ant-table-column-title">Column 6</span><span class="ant-table-column-sorter"><!----></span>
|
||||
</div></span>
|
||||
<!---->
|
||||
</th>
|
||||
<th colstart="6" colspan="1" colend="6" rowspan="1" class=""><span class="ant-table-header-column"><div><span class="ant-table-column-title">Column 7</span><span class="ant-table-column-sorter"><!----></span></div></span>
|
||||
<!---->
|
||||
</th>
|
||||
<th colstart="7" colspan="1" colend="7" rowspan="1" class=""><span class="ant-table-header-column"><div><span class="ant-table-column-title">Column 8</span><span class="ant-table-column-sorter"><!----></span></div></span>
|
||||
<!---->
|
||||
</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody class="ant-table-tbody"></tbody>
|
||||
</table>
|
||||
</div>
|
||||
<div class="ant-table-placeholder">
|
||||
<div class="ant-empty ant-empty-normal">
|
||||
<div class="ant-empty-image"><svg class="ant-empty-img-simple" width="64" height="41" viewBox="0 0 64 41">
|
||||
<g transform="translate(0 1)" fill="none" fill-rule="evenodd">
|
||||
<ellipse class="ant-empty-img-simple-ellipse" fill="#F5F5F5" cx="32" cy="33" rx="32" ry="7"></ellipse>
|
||||
<g class="ant-empty-img-simple-g" fill-rule="nonzero" stroke="#D9D9D9">
|
||||
<path d="M55 12.76L44.854 1.258C44.367.474 43.656 0 42.907 0H21.093c-.749 0-1.46.474-1.947 1.257L9 12.761V22h46v-9.24z"></path>
|
||||
<path d="M41.613 15.931c0-1.605.994-2.93 2.227-2.931H55v18.137C55 33.26 53.68 35 52.05 35h-40.1C10.32 35 9 33.259 9 31.137V13h11.16c1.233 0 2.227 1.323 2.227 2.928v.022c0 1.605 1.005 2.901 2.237 2.901h14.752c1.232 0 2.237-1.308 2.237-2.913v-.007z" fill="#FAFAFA" class="ant-empty-img-simple-path"></path>
|
||||
</g>
|
||||
</g>
|
||||
</svg></div>
|
||||
<p class="ant-empty-description">No Data</p>
|
||||
<!---->
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!---->
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
|
||||
exports[`Table renders empty table with custom emptyText 1`] = `
|
||||
|
@ -86,65 +63,56 @@ exports[`Table renders empty table with custom emptyText 1`] = `
|
|||
<div class="ant-spin-nested-loading">
|
||||
<!---->
|
||||
<div class="ant-spin-container">
|
||||
<div class="ant-table ant-table-default ant-table-empty ant-table-scroll-position-left">
|
||||
<div class="ant-table ant-table-empty">
|
||||
<!---->
|
||||
<div class="ant-table-content">
|
||||
<!---->
|
||||
<div class="ant-table-body">
|
||||
<table class="">
|
||||
<colgroup>
|
||||
<col data-key="1">
|
||||
<col data-key="2">
|
||||
<col data-key="3">
|
||||
<col data-key="4">
|
||||
<col data-key="5">
|
||||
<col data-key="6">
|
||||
<col data-key="7">
|
||||
<col data-key="8">
|
||||
</colgroup>
|
||||
<div class="ant-table-container">
|
||||
<div class="ant-table-content">
|
||||
<table style="table-layout: auto;">
|
||||
<colgroup></colgroup>
|
||||
<thead class="ant-table-thead">
|
||||
<tr>
|
||||
<th colstart="0" colspan="1" colend="0" rowspan="1" class=""><span class="ant-table-header-column"><div><span class="ant-table-column-title">Column 1</span><span class="ant-table-column-sorter"><!----></span>
|
||||
</div></span>
|
||||
<!---->
|
||||
</th>
|
||||
<th colstart="1" colspan="1" colend="1" rowspan="1" class=""><span class="ant-table-header-column"><div><span class="ant-table-column-title">Column 2</span><span class="ant-table-column-sorter"><!----></span>
|
||||
</div></span>
|
||||
<th class="ant-table-cell" colstart="0" colend="0">
|
||||
<!---->Column 1
|
||||
</th>
|
||||
<th class="ant-table-cell" colstart="1" colend="1">
|
||||
<!---->Column 2
|
||||
</th>
|
||||
<th class="ant-table-cell" colstart="2" colend="2">
|
||||
<!---->Column 3
|
||||
</th>
|
||||
<th class="ant-table-cell" colstart="3" colend="3">
|
||||
<!---->Column 4
|
||||
</th>
|
||||
<th class="ant-table-cell" colstart="4" colend="4">
|
||||
<!---->Column 5
|
||||
</th>
|
||||
<th class="ant-table-cell" colstart="5" colend="5">
|
||||
<!---->Column 6
|
||||
</th>
|
||||
<th class="ant-table-cell" colstart="6" colend="6">
|
||||
<!---->Column 7
|
||||
</th>
|
||||
<th class="ant-table-cell" colstart="7" colend="7">
|
||||
<!---->Column 8
|
||||
</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody class="ant-table-tbody">
|
||||
<!---->
|
||||
<tr class="ant-table-placeholder">
|
||||
<td colspan="8" class="ant-table-cell">
|
||||
<!---->custom empty text
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
<!---->
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
<!---->
|
||||
</th>
|
||||
<th colstart="2" colspan="1" colend="2" rowspan="1" class=""><span class="ant-table-header-column"><div><span class="ant-table-column-title">Column 3</span><span class="ant-table-column-sorter"><!----></span>
|
||||
</div></span>
|
||||
<!---->
|
||||
</th>
|
||||
<th colstart="3" colspan="1" colend="3" rowspan="1" class=""><span class="ant-table-header-column"><div><span class="ant-table-column-title">Column 4</span><span class="ant-table-column-sorter"><!----></span>
|
||||
</div></span>
|
||||
<!---->
|
||||
</th>
|
||||
<th colstart="4" colspan="1" colend="4" rowspan="1" class=""><span class="ant-table-header-column"><div><span class="ant-table-column-title">Column 5</span><span class="ant-table-column-sorter"><!----></span>
|
||||
</div></span>
|
||||
<!---->
|
||||
</th>
|
||||
<th colstart="5" colspan="1" colend="5" rowspan="1" class=""><span class="ant-table-header-column"><div><span class="ant-table-column-title">Column 6</span><span class="ant-table-column-sorter"><!----></span>
|
||||
</div></span>
|
||||
<!---->
|
||||
</th>
|
||||
<th colstart="6" colspan="1" colend="6" rowspan="1" class=""><span class="ant-table-header-column"><div><span class="ant-table-column-title">Column 7</span><span class="ant-table-column-sorter"><!----></span></div></span>
|
||||
<!---->
|
||||
</th>
|
||||
<th colstart="7" colspan="1" colend="7" rowspan="1" class=""><span class="ant-table-header-column"><div><span class="ant-table-column-title">Column 8</span><span class="ant-table-column-sorter"><!----></span></div></span>
|
||||
<!---->
|
||||
</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody class="ant-table-tbody"></tbody>
|
||||
</table>
|
||||
</div>
|
||||
<div class="ant-table-placeholder">custom empty text </div>
|
||||
<!---->
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
|
||||
|
@ -153,178 +121,138 @@ exports[`Table renders empty table with fixed columns 1`] = `
|
|||
<div class="ant-spin-nested-loading">
|
||||
<!---->
|
||||
<div class="ant-spin-container">
|
||||
<div class="ant-table ant-table-default ant-table-empty ant-table-scroll-position-left ant-table-scroll-position-right">
|
||||
<div class="ant-table ant-table-has-fix-left ant-table-has-fix-right ant-table-empty">
|
||||
<!---->
|
||||
<div class="ant-table-content">
|
||||
<div class="ant-table-scroll">
|
||||
<!---->
|
||||
<div class="ant-table-body">
|
||||
<table class="">
|
||||
<colgroup>
|
||||
<col data-key="name" style="width: 100px; min-width: 100px;">
|
||||
<col data-key="age" style="width: 100px; min-width: 100px;">
|
||||
<col data-key="1">
|
||||
<col data-key="2">
|
||||
<col data-key="3">
|
||||
<col data-key="4">
|
||||
<col data-key="5">
|
||||
<col data-key="6">
|
||||
<col data-key="7">
|
||||
<col data-key="8">
|
||||
<col data-key="address" style="width: 100px; min-width: 100px;">
|
||||
</colgroup>
|
||||
<thead class="ant-table-thead">
|
||||
<tr>
|
||||
<th colstart="0" colspan="1" colend="0" rowspan="1" class="ant-table-row-cell-break-word"><span class="ant-table-header-column"><div><span class="ant-table-column-title">Full Name</span><span class="ant-table-column-sorter"><!----></span>
|
||||
</div></span>
|
||||
<!---->
|
||||
</th>
|
||||
<th colstart="1" colspan="1" colend="1" rowspan="1" class="ant-table-row-cell-break-word"><span class="ant-table-header-column"><div><span class="ant-table-column-title">Age</span><span class="ant-table-column-sorter"><!----></span>
|
||||
</div></span>
|
||||
<!---->
|
||||
</th>
|
||||
<th colstart="2" colspan="1" colend="2" rowspan="1" class=""><span class="ant-table-header-column"><div><span class="ant-table-column-title">Column 1</span><span class="ant-table-column-sorter"><!----></span>
|
||||
</div></span>
|
||||
<div class="ant-table-container">
|
||||
<div class="ant-table-content">
|
||||
<table style="table-layout: auto;">
|
||||
<colgroup>
|
||||
<col style="width: 100px;">
|
||||
<col style="width: 100px;">
|
||||
<col>
|
||||
<col>
|
||||
<col>
|
||||
<col>
|
||||
<col>
|
||||
<col>
|
||||
<col>
|
||||
<col>
|
||||
<col style="width: 100px;">
|
||||
</colgroup>
|
||||
<thead class="ant-table-thead">
|
||||
<tr>
|
||||
<th class="ant-table-cell ant-table-cell-fix-left" style="position: sticky; left: 0px;" colstart="0" colend="0">
|
||||
<!---->Full Name
|
||||
</th>
|
||||
<th class="ant-table-cell ant-table-cell-fix-left ant-table-cell-fix-left-last" style="position: sticky; left: 0px;" colstart="1" colend="1">
|
||||
<!---->Age
|
||||
</th>
|
||||
<th class="ant-table-cell" colstart="2" colend="2">
|
||||
<!---->Column 1
|
||||
</th>
|
||||
<th class="ant-table-cell" colstart="3" colend="3">
|
||||
<!---->Column 2
|
||||
</th>
|
||||
<th class="ant-table-cell" colstart="4" colend="4">
|
||||
<!---->Column 3
|
||||
</th>
|
||||
<th class="ant-table-cell" colstart="5" colend="5">
|
||||
<!---->Column 4
|
||||
</th>
|
||||
<th class="ant-table-cell" colstart="6" colend="6">
|
||||
<!---->Column 5
|
||||
</th>
|
||||
<th class="ant-table-cell" colstart="7" colend="7">
|
||||
<!---->Column 6
|
||||
</th>
|
||||
<th class="ant-table-cell" colstart="8" colend="8">
|
||||
<!---->Column 7
|
||||
</th>
|
||||
<th class="ant-table-cell" colstart="9" colend="9">
|
||||
<!---->Column 8
|
||||
</th>
|
||||
<th class="ant-table-cell ant-table-cell-fix-right ant-table-cell-fix-right-first" style="position: sticky; right: 0px;" colstart="10" colend="10">
|
||||
<!---->Action
|
||||
</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody class="ant-table-tbody">
|
||||
<!---->
|
||||
<tr class="ant-table-placeholder">
|
||||
<td colspan="11" class="ant-table-cell">
|
||||
<!---->No data
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
<!---->
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
<!---->
|
||||
</th>
|
||||
<th colstart="3" colspan="1" colend="3" rowspan="1" class=""><span class="ant-table-header-column"><div><span class="ant-table-column-title">Column 2</span><span class="ant-table-column-sorter"><!----></span>
|
||||
</div></span>
|
||||
<!---->
|
||||
</th>
|
||||
<th colstart="4" colspan="1" colend="4" rowspan="1" class=""><span class="ant-table-header-column"><div><span class="ant-table-column-title">Column 3</span><span class="ant-table-column-sorter"><!----></span>
|
||||
</div></span>
|
||||
<!---->
|
||||
</th>
|
||||
<th colstart="5" colspan="1" colend="5" rowspan="1" class=""><span class="ant-table-header-column"><div><span class="ant-table-column-title">Column 4</span><span class="ant-table-column-sorter"><!----></span>
|
||||
</div></span>
|
||||
<!---->
|
||||
</th>
|
||||
<th colstart="6" colspan="1" colend="6" rowspan="1" class=""><span class="ant-table-header-column"><div><span class="ant-table-column-title">Column 5</span><span class="ant-table-column-sorter"><!----></span>
|
||||
</div></span>
|
||||
<!---->
|
||||
</th>
|
||||
<th colstart="7" colspan="1" colend="7" rowspan="1" class=""><span class="ant-table-header-column"><div><span class="ant-table-column-title">Column 6</span><span class="ant-table-column-sorter"><!----></span></div></span>
|
||||
<!---->
|
||||
</th>
|
||||
<th colstart="8" colspan="1" colend="8" rowspan="1" class=""><span class="ant-table-header-column"><div><span class="ant-table-column-title">Column 7</span><span class="ant-table-column-sorter"><!----></span></div></span>
|
||||
<!---->
|
||||
</th>
|
||||
<th colstart="9" colspan="1" colend="9" rowspan="1" class=""><span class="ant-table-header-column"><div><span class="ant-table-column-title">Column 8</span><span class="ant-table-column-sorter"><!----></span></div></span>
|
||||
<!---->
|
||||
</th>
|
||||
<th colstart="10" colspan="1" colend="10" rowspan="1" class="ant-table-row-cell-break-word"><span class="ant-table-header-column"><div><span class="ant-table-column-title">Action</span><span class="ant-table-column-sorter"><!----></span></div></span>
|
||||
<!---->
|
||||
</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody class="ant-table-tbody"></tbody>
|
||||
</table>
|
||||
</div>
|
||||
<div class="ant-table-placeholder">
|
||||
<div class="ant-empty ant-empty-normal">
|
||||
<div class="ant-empty-image"><svg class="ant-empty-img-simple" width="64" height="41" viewBox="0 0 64 41">
|
||||
<g transform="translate(0 1)" fill="none" fill-rule="evenodd">
|
||||
<ellipse class="ant-empty-img-simple-ellipse" fill="#F5F5F5" cx="32" cy="33" rx="32" ry="7"></ellipse>
|
||||
<g class="ant-empty-img-simple-g" fill-rule="nonzero" stroke="#D9D9D9">
|
||||
<path d="M55 12.76L44.854 1.258C44.367.474 43.656 0 42.907 0H21.093c-.749 0-1.46.474-1.947 1.257L9 12.761V22h46v-9.24z"></path>
|
||||
<path d="M41.613 15.931c0-1.605.994-2.93 2.227-2.931H55v18.137C55 33.26 53.68 35 52.05 35h-40.1C10.32 35 9 33.259 9 31.137V13h11.16c1.233 0 2.227 1.323 2.227 2.928v.022c0 1.605 1.005 2.901 2.237 2.901h14.752c1.232 0 2.237-1.308 2.237-2.913v-.007z" fill="#FAFAFA" class="ant-empty-img-simple-path"></path>
|
||||
</g>
|
||||
</g>
|
||||
</svg></div>
|
||||
<p class="ant-empty-description">No Data</p>
|
||||
<!---->
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!---->
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
|
||||
exports[`Table renders empty table without emptyText when loading 1`] = `
|
||||
<div class="ant-table-wrapper">
|
||||
<div class="ant-spin-nested-loading">
|
||||
<div>
|
||||
<div class="ant-spin ant-spin-spinning ant-table-without-pagination ant-table-spin-holder"><span class="ant-spin-dot ant-spin-dot-spin"><i class="ant-spin-dot-item"></i><i class="ant-spin-dot-item"></i><i class="ant-spin-dot-item"></i><i class="ant-spin-dot-item"></i></span>
|
||||
<div class="ant-spin ant-spin-spinning"><span class="ant-spin-dot ant-spin-dot-spin"><i class="ant-spin-dot-item"></i><i class="ant-spin-dot-item"></i><i class="ant-spin-dot-item"></i><i class="ant-spin-dot-item"></i></span>
|
||||
<!---->
|
||||
</div>
|
||||
</div>
|
||||
<div class="ant-spin-container ant-spin-blur">
|
||||
<div class="ant-table ant-table-default ant-table-empty ant-table-scroll-position-left">
|
||||
<div class="ant-table ant-table-empty">
|
||||
<!---->
|
||||
<div class="ant-table-content">
|
||||
<!---->
|
||||
<div class="ant-table-body">
|
||||
<table class="">
|
||||
<colgroup>
|
||||
<col data-key="1">
|
||||
<col data-key="2">
|
||||
<col data-key="3">
|
||||
<col data-key="4">
|
||||
<col data-key="5">
|
||||
<col data-key="6">
|
||||
<col data-key="7">
|
||||
<col data-key="8">
|
||||
</colgroup>
|
||||
<div class="ant-table-container">
|
||||
<div class="ant-table-content">
|
||||
<table style="table-layout: auto;">
|
||||
<colgroup></colgroup>
|
||||
<thead class="ant-table-thead">
|
||||
<tr>
|
||||
<th colstart="0" colspan="1" colend="0" rowspan="1" class=""><span class="ant-table-header-column"><div><span class="ant-table-column-title">Column 1</span><span class="ant-table-column-sorter"><!----></span>
|
||||
</div></span>
|
||||
<!---->
|
||||
</th>
|
||||
<th colstart="1" colspan="1" colend="1" rowspan="1" class=""><span class="ant-table-header-column"><div><span class="ant-table-column-title">Column 2</span><span class="ant-table-column-sorter"><!----></span>
|
||||
</div></span>
|
||||
<th class="ant-table-cell" colstart="0" colend="0">
|
||||
<!---->Column 1
|
||||
</th>
|
||||
<th class="ant-table-cell" colstart="1" colend="1">
|
||||
<!---->Column 2
|
||||
</th>
|
||||
<th class="ant-table-cell" colstart="2" colend="2">
|
||||
<!---->Column 3
|
||||
</th>
|
||||
<th class="ant-table-cell" colstart="3" colend="3">
|
||||
<!---->Column 4
|
||||
</th>
|
||||
<th class="ant-table-cell" colstart="4" colend="4">
|
||||
<!---->Column 5
|
||||
</th>
|
||||
<th class="ant-table-cell" colstart="5" colend="5">
|
||||
<!---->Column 6
|
||||
</th>
|
||||
<th class="ant-table-cell" colstart="6" colend="6">
|
||||
<!---->Column 7
|
||||
</th>
|
||||
<th class="ant-table-cell" colstart="7" colend="7">
|
||||
<!---->Column 8
|
||||
</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody class="ant-table-tbody">
|
||||
<!---->
|
||||
<tr class="ant-table-placeholder">
|
||||
<td colspan="8" class="ant-table-cell">
|
||||
<!---->No data
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
<!---->
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
<!---->
|
||||
</th>
|
||||
<th colstart="2" colspan="1" colend="2" rowspan="1" class=""><span class="ant-table-header-column"><div><span class="ant-table-column-title">Column 3</span><span class="ant-table-column-sorter"><!----></span>
|
||||
</div></span>
|
||||
<!---->
|
||||
</th>
|
||||
<th colstart="3" colspan="1" colend="3" rowspan="1" class=""><span class="ant-table-header-column"><div><span class="ant-table-column-title">Column 4</span><span class="ant-table-column-sorter"><!----></span>
|
||||
</div></span>
|
||||
<!---->
|
||||
</th>
|
||||
<th colstart="4" colspan="1" colend="4" rowspan="1" class=""><span class="ant-table-header-column"><div><span class="ant-table-column-title">Column 5</span><span class="ant-table-column-sorter"><!----></span>
|
||||
</div></span>
|
||||
<!---->
|
||||
</th>
|
||||
<th colstart="5" colspan="1" colend="5" rowspan="1" class=""><span class="ant-table-header-column"><div><span class="ant-table-column-title">Column 6</span><span class="ant-table-column-sorter"><!----></span>
|
||||
</div></span>
|
||||
<!---->
|
||||
</th>
|
||||
<th colstart="6" colspan="1" colend="6" rowspan="1" class=""><span class="ant-table-header-column"><div><span class="ant-table-column-title">Column 7</span><span class="ant-table-column-sorter"><!----></span></div></span>
|
||||
<!---->
|
||||
</th>
|
||||
<th colstart="7" colspan="1" colend="7" rowspan="1" class=""><span class="ant-table-header-column"><div><span class="ant-table-column-title">Column 8</span><span class="ant-table-column-sorter"><!----></span></div></span>
|
||||
<!---->
|
||||
</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody class="ant-table-tbody"></tbody>
|
||||
</table>
|
||||
</div>
|
||||
<div class="ant-table-placeholder">
|
||||
<div class="ant-empty ant-empty-normal">
|
||||
<div class="ant-empty-image"><svg class="ant-empty-img-simple" width="64" height="41" viewBox="0 0 64 41">
|
||||
<g transform="translate(0 1)" fill="none" fill-rule="evenodd">
|
||||
<ellipse class="ant-empty-img-simple-ellipse" fill="#F5F5F5" cx="32" cy="33" rx="32" ry="7"></ellipse>
|
||||
<g class="ant-empty-img-simple-g" fill-rule="nonzero" stroke="#D9D9D9">
|
||||
<path d="M55 12.76L44.854 1.258C44.367.474 43.656 0 42.907 0H21.093c-.749 0-1.46.474-1.947 1.257L9 12.761V22h46v-9.24z"></path>
|
||||
<path d="M41.613 15.931c0-1.605.994-2.93 2.227-2.931H55v18.137C55 33.26 53.68 35 52.05 35h-40.1C10.32 35 9 33.259 9 31.137V13h11.16c1.233 0 2.227 1.323 2.227 2.928v.022c0 1.605 1.005 2.901 2.237 2.901h14.752c1.232 0 2.237-1.308 2.237-2.913v-.007z" fill="#FAFAFA" class="ant-empty-img-simple-path"></path>
|
||||
</g>
|
||||
</g>
|
||||
</svg></div>
|
||||
<p class="ant-empty-description">No Data</p>
|
||||
<!---->
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!---->
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
|
|
|
@ -0,0 +1,29 @@
|
|||
import type { ComputedRef, InjectionKey } from 'vue';
|
||||
import { computed } from 'vue';
|
||||
import { inject, provide } from 'vue';
|
||||
|
||||
export type ContextSlots = {
|
||||
emptyText?: (...args: any[]) => void;
|
||||
expandIcon?: (...args: any[]) => void;
|
||||
title?: (...args: any[]) => void;
|
||||
footer?: (...args: any[]) => void;
|
||||
summary?: (...args: any[]) => void;
|
||||
bodyCell?: (...args: any[]) => void;
|
||||
headerCell?: (...args: any[]) => void;
|
||||
customFilterIcon?: (...args: any[]) => void;
|
||||
customFilterDropdown?: (...args: any[]) => void;
|
||||
// 兼容 2.x 的 columns slots 配置
|
||||
[key: string]: (...args: any[]) => void;
|
||||
};
|
||||
|
||||
export type ContextProps = ComputedRef<ContextSlots>;
|
||||
|
||||
export const ContextKey: InjectionKey<ContextProps> = Symbol('ContextProps');
|
||||
|
||||
export const useProvideSlots = (props: ContextProps) => {
|
||||
provide(ContextKey, props);
|
||||
};
|
||||
|
||||
export const useInjectSlots = () => {
|
||||
return inject(ContextKey, computed(() => ({})) as ContextProps);
|
||||
};
|
|
@ -1,43 +0,0 @@
|
|||
import PropTypes from '../_util/vue-types';
|
||||
import { computed, defineComponent } from 'vue';
|
||||
import { getSlot } from '../_util/props-util';
|
||||
import omit from 'omit.js';
|
||||
|
||||
const BodyRowProps = {
|
||||
store: PropTypes.object,
|
||||
rowKey: PropTypes.oneOfType([PropTypes.string, PropTypes.number]),
|
||||
prefixCls: PropTypes.string,
|
||||
};
|
||||
|
||||
export default function createBodyRow(Component = 'tr') {
|
||||
const BodyRow = defineComponent({
|
||||
name: 'BodyRow',
|
||||
inheritAttrs: false,
|
||||
props: BodyRowProps,
|
||||
setup(props) {
|
||||
return {
|
||||
selected: computed(() => props.store?.selectedRowKeys.indexOf(props.rowKey) >= 0),
|
||||
};
|
||||
},
|
||||
render() {
|
||||
const rowProps = omit({ ...this.$props, ...this.$attrs }, [
|
||||
'prefixCls',
|
||||
'rowKey',
|
||||
'store',
|
||||
'class',
|
||||
]);
|
||||
const className = {
|
||||
[`${this.prefixCls}-row-selected`]: this.selected,
|
||||
[this.$attrs.class as string]: !!this.$attrs.class,
|
||||
};
|
||||
|
||||
return (
|
||||
<Component class={className} {...rowProps}>
|
||||
{getSlot(this)}
|
||||
</Component>
|
||||
);
|
||||
},
|
||||
});
|
||||
|
||||
return BodyRow;
|
||||
}
|
|
@ -28,20 +28,23 @@ This example shows how to fetch and present data from a remote server, and how t
|
|||
:loading="loading"
|
||||
@change="handleTableChange"
|
||||
>
|
||||
<template #name="{ text }">{{ text.first }} {{ text.last }}</template>
|
||||
<template #bodyCell="{ column, text }">
|
||||
<template v-if="column.dataIndex === 'name'">{{ text.first }} {{ text.last }}</template>
|
||||
<template v-else>{{ text }}</template>
|
||||
</template>
|
||||
</a-table>
|
||||
</template>
|
||||
<script lang="ts">
|
||||
import { TableState, TableStateFilters } from 'ant-design-vue/es/table/interface';
|
||||
import type { TableProps } from 'ant-design-vue';
|
||||
import { usePagination } from 'vue-request';
|
||||
import { computed, defineComponent } from 'vue';
|
||||
import axios from 'axios';
|
||||
const columns = [
|
||||
{
|
||||
title: 'Name',
|
||||
dataIndex: 'name',
|
||||
sorter: true,
|
||||
width: '20%',
|
||||
slots: { customRender: 'name' },
|
||||
},
|
||||
{
|
||||
title: 'Gender',
|
||||
|
@ -58,7 +61,6 @@ const columns = [
|
|||
},
|
||||
];
|
||||
|
||||
type Pagination = TableState['pagination'];
|
||||
type APIParams = {
|
||||
results: number;
|
||||
page?: number;
|
||||
|
@ -75,21 +77,24 @@ type APIResult = {
|
|||
};
|
||||
|
||||
const queryData = (params: APIParams) => {
|
||||
return `https://randomuser.me/api?noinfo&${new URLSearchParams(params)}`;
|
||||
return axios.get<APIResult>('https://randomuser.me/api?noinfo', { params });
|
||||
};
|
||||
|
||||
export default defineComponent({
|
||||
setup() {
|
||||
const { data: dataSource, run, loading, current, pageSize } = usePagination<APIResult>(
|
||||
queryData,
|
||||
{
|
||||
formatResult: res => res.results,
|
||||
pagination: {
|
||||
currentKey: 'page',
|
||||
pageSizeKey: 'results',
|
||||
},
|
||||
const {
|
||||
data: dataSource,
|
||||
run,
|
||||
loading,
|
||||
current,
|
||||
pageSize,
|
||||
} = usePagination(queryData, {
|
||||
formatResult: res => res.data.results,
|
||||
pagination: {
|
||||
currentKey: 'page',
|
||||
pageSizeKey: 'results',
|
||||
},
|
||||
);
|
||||
});
|
||||
|
||||
const pagination = computed(() => ({
|
||||
total: 200,
|
||||
|
@ -97,9 +102,13 @@ export default defineComponent({
|
|||
pageSize: pageSize.value,
|
||||
}));
|
||||
|
||||
const handleTableChange = (pag: Pagination, filters: TableStateFilters, sorter: any) => {
|
||||
const handleTableChange: TableProps['onChange'] = (
|
||||
pag: { pageSize: number; current: number },
|
||||
filters: any,
|
||||
sorter: any,
|
||||
) => {
|
||||
run({
|
||||
results: pag!.pageSize!,
|
||||
results: pag.pageSize!,
|
||||
page: pag?.current,
|
||||
sortField: sorter.field,
|
||||
sortOrder: sorter.order,
|
||||
|
|
|
@ -17,37 +17,46 @@ Simple table with actions.
|
|||
|
||||
<template>
|
||||
<a-table :columns="columns" :data-source="data">
|
||||
<template #name="{ text }">
|
||||
<a>{{ text }}</a>
|
||||
<template #headerCell="{ title, column }">
|
||||
<template v-if="column.key === 'name'">
|
||||
<span>
|
||||
<smile-outlined />
|
||||
Name
|
||||
</span>
|
||||
</template>
|
||||
<template v-else>{{ title }}</template>
|
||||
</template>
|
||||
<template #customTitle>
|
||||
<span>
|
||||
<smile-outlined />
|
||||
Name
|
||||
</span>
|
||||
</template>
|
||||
<template #tags="{ text: tags }">
|
||||
<span>
|
||||
<a-tag
|
||||
v-for="tag in tags"
|
||||
:key="tag"
|
||||
:color="tag === 'loser' ? 'volcano' : tag.length > 5 ? 'geekblue' : 'green'"
|
||||
>
|
||||
{{ tag.toUpperCase() }}
|
||||
</a-tag>
|
||||
</span>
|
||||
</template>
|
||||
<template #action="{ record }">
|
||||
<span>
|
||||
<a>Invite 一 {{ record.name }}</a>
|
||||
<a-divider type="vertical" />
|
||||
<a>Delete</a>
|
||||
<a-divider type="vertical" />
|
||||
<a class="ant-dropdown-link">
|
||||
More actions
|
||||
<down-outlined />
|
||||
|
||||
<template #bodyCell="{ column, record }">
|
||||
<template v-if="column.key === 'name'">
|
||||
<a>
|
||||
{{ record.name }}
|
||||
</a>
|
||||
</span>
|
||||
</template>
|
||||
<template v-else-if="column.key === 'tags'">
|
||||
<span>
|
||||
<a-tag
|
||||
v-for="tag in record.tags"
|
||||
:key="tag"
|
||||
:color="tag === 'loser' ? 'volcano' : tag.length > 5 ? 'geekblue' : 'green'"
|
||||
>
|
||||
{{ tag.toUpperCase() }}
|
||||
</a-tag>
|
||||
</span>
|
||||
</template>
|
||||
<template v-else-if="column.key === 'action'">
|
||||
<span>
|
||||
<a>Invite 一 {{ record.name }}</a>
|
||||
<a-divider type="vertical" />
|
||||
<a>Delete</a>
|
||||
<a-divider type="vertical" />
|
||||
<a class="ant-dropdown-link">
|
||||
More actions
|
||||
<down-outlined />
|
||||
</a>
|
||||
</span>
|
||||
</template>
|
||||
<template v-else>{{ record.name }}</template>
|
||||
</template>
|
||||
</a-table>
|
||||
</template>
|
||||
|
@ -56,9 +65,9 @@ import { SmileOutlined, DownOutlined } from '@ant-design/icons-vue';
|
|||
import { defineComponent } from 'vue';
|
||||
const columns = [
|
||||
{
|
||||
name: 'Name',
|
||||
dataIndex: 'name',
|
||||
key: 'name',
|
||||
slots: { title: 'customTitle', customRender: 'name' },
|
||||
},
|
||||
{
|
||||
title: 'Age',
|
||||
|
@ -74,12 +83,10 @@ const columns = [
|
|||
title: 'Tags',
|
||||
key: 'tags',
|
||||
dataIndex: 'tags',
|
||||
slots: { customRender: 'tags' },
|
||||
},
|
||||
{
|
||||
title: 'Action',
|
||||
key: 'action',
|
||||
slots: { customRender: 'action' },
|
||||
},
|
||||
];
|
||||
|
||||
|
|
|
@ -17,8 +17,11 @@ Add border, title and footer for table.
|
|||
|
||||
<template>
|
||||
<a-table :columns="columns" :data-source="data" bordered>
|
||||
<template #name="{ text }">
|
||||
<a>{{ text }}</a>
|
||||
<template #bodyCell="{ column, text }">
|
||||
<template v-if="column.dataIndex === 'name'">
|
||||
<a>{{ text }}</a>
|
||||
</template>
|
||||
<template v-else>{{ text }}</template>
|
||||
</template>
|
||||
<template #title>Header</template>
|
||||
<template #footer>Footer</template>
|
||||
|
@ -31,7 +34,6 @@ const columns = [
|
|||
{
|
||||
title: 'Name',
|
||||
dataIndex: 'name',
|
||||
slots: { customRender: 'name' },
|
||||
},
|
||||
{
|
||||
title: 'Cash Assets',
|
||||
|
|
|
@ -20,19 +20,21 @@ Table cell supports `colSpan` and `rowSpan` that set in render return object. Wh
|
|||
|
||||
<template>
|
||||
<a-table :columns="columns" :data-source="data" bordered>
|
||||
<template #name="{ text }">
|
||||
<a>{{ text }}</a>
|
||||
<template #bodyCell="{ column, text }">
|
||||
<template v-if="column.dataIndex === 'name'">
|
||||
<a href="javascript:;">{{ text }}</a>
|
||||
</template>
|
||||
<template v-else>{{ text }}</template>
|
||||
</template>
|
||||
</a-table>
|
||||
</template>
|
||||
<script lang="ts">
|
||||
import { defineComponent, h } from 'vue';
|
||||
import { ColumnProps } from 'ant-design-vue/es/table/interface';
|
||||
import { defineComponent } from 'vue';
|
||||
import { TableColumnType } from 'ant-design-vue';
|
||||
// In the fifth row, other columns are merged into first column
|
||||
// by setting it's colSpan to be 0
|
||||
const renderContent = ({ text, index }: any) => {
|
||||
const renderContent = ({ index }: any) => {
|
||||
const obj = {
|
||||
children: text,
|
||||
props: {} as any,
|
||||
};
|
||||
if (index === 4) {
|
||||
|
@ -86,16 +88,15 @@ const data = [
|
|||
|
||||
export default defineComponent({
|
||||
setup() {
|
||||
const columns: ColumnProps[] = [
|
||||
const columns: TableColumnType[] = [
|
||||
{
|
||||
title: 'Name',
|
||||
dataIndex: 'name',
|
||||
customRender: ({ text, index }) => {
|
||||
customRender: ({ index }) => {
|
||||
if (index < 4) {
|
||||
return h('a', { href: 'javascript:;' }, text);
|
||||
return;
|
||||
}
|
||||
return {
|
||||
children: h('a', { href: 'javascript:;' }, text),
|
||||
props: {
|
||||
colSpan: 5,
|
||||
},
|
||||
|
@ -111,9 +112,8 @@ export default defineComponent({
|
|||
title: 'Home phone',
|
||||
colSpan: 2,
|
||||
dataIndex: 'tel',
|
||||
customRender: ({ text, index }) => {
|
||||
customRender: ({ index }) => {
|
||||
const obj = {
|
||||
children: text,
|
||||
props: {} as any,
|
||||
};
|
||||
if (index === 2) {
|
||||
|
|
|
@ -8,17 +8,25 @@ title:
|
|||
|
||||
## zh-CN
|
||||
|
||||
通过 `filterDropdown` 定义自定义的列筛选功能,并实现一个搜索列的示例。
|
||||
通过 `customFilterDropdown` 定义自定义的列筛选功能,并实现一个搜索列的示例。
|
||||
|
||||
## en-US
|
||||
|
||||
Implement a customized column search example via `filterDropdown`.
|
||||
Implement a customized column search example via `customFilterDropdown`.
|
||||
|
||||
</docs>
|
||||
|
||||
<template>
|
||||
<a-table :data-source="data" :columns="columns">
|
||||
<template #filterDropdown="{ setSelectedKeys, selectedKeys, confirm, clearFilters, column }">
|
||||
<template #headerCell="{ column }">
|
||||
<template v-if="column.key === 'name'">
|
||||
<span style="color: #1890ff">Name</span>
|
||||
</template>
|
||||
<template v-else>{{ column.title }}</template>
|
||||
</template>
|
||||
<template
|
||||
#customFilterDropdown="{ setSelectedKeys, selectedKeys, confirm, clearFilters, column }"
|
||||
>
|
||||
<div style="padding: 8px">
|
||||
<a-input
|
||||
ref="searchInput"
|
||||
|
@ -42,10 +50,10 @@ Implement a customized column search example via `filterDropdown`.
|
|||
</a-button>
|
||||
</div>
|
||||
</template>
|
||||
<template #filterIcon="filtered">
|
||||
<template #customFilterIcon="{ filtered }">
|
||||
<search-outlined :style="{ color: filtered ? '#108ee9' : undefined }" />
|
||||
</template>
|
||||
<template #customRender="{ text, column }">
|
||||
<template #bodyCell="{ text, column }">
|
||||
<span v-if="searchText && searchedColumn === column.dataIndex">
|
||||
<template
|
||||
v-for="(fragment, i) in text
|
||||
|
@ -71,7 +79,7 @@ Implement a customized column search example via `filterDropdown`.
|
|||
|
||||
<script>
|
||||
import { SearchOutlined } from '@ant-design/icons-vue';
|
||||
import { defineComponent, reactive, ref } from 'vue';
|
||||
import { defineComponent, reactive, ref, toRefs } from 'vue';
|
||||
const data = [
|
||||
{
|
||||
key: '1',
|
||||
|
@ -116,17 +124,12 @@ export default defineComponent({
|
|||
title: 'Name',
|
||||
dataIndex: 'name',
|
||||
key: 'name',
|
||||
slots: {
|
||||
filterDropdown: 'filterDropdown',
|
||||
filterIcon: 'filterIcon',
|
||||
customRender: 'customRender',
|
||||
},
|
||||
customFilterDropdown: true,
|
||||
onFilter: (value, record) =>
|
||||
record.name.toString().toLowerCase().includes(value.toLowerCase()),
|
||||
onFilterDropdownVisibleChange: visible => {
|
||||
if (visible) {
|
||||
setTimeout(() => {
|
||||
console.log(searchInput.value);
|
||||
searchInput.value.focus();
|
||||
}, 100);
|
||||
}
|
||||
|
@ -136,30 +139,12 @@ export default defineComponent({
|
|||
title: 'Age',
|
||||
dataIndex: 'age',
|
||||
key: 'age',
|
||||
slots: {
|
||||
filterDropdown: 'filterDropdown',
|
||||
filterIcon: 'filterIcon',
|
||||
customRender: 'customRender',
|
||||
},
|
||||
onFilter: (value, record) =>
|
||||
record.age.toString().toLowerCase().includes(value.toLowerCase()),
|
||||
onFilterDropdownVisibleChange: visible => {
|
||||
if (visible) {
|
||||
setTimeout(() => {
|
||||
searchInput.value.focus();
|
||||
}, 100);
|
||||
}
|
||||
},
|
||||
},
|
||||
{
|
||||
title: 'Address',
|
||||
dataIndex: 'address',
|
||||
key: 'address',
|
||||
slots: {
|
||||
filterDropdown: 'filterDropdown',
|
||||
filterIcon: 'filterIcon',
|
||||
customRender: 'customRender',
|
||||
},
|
||||
customFilterDropdown: true,
|
||||
onFilter: (value, record) =>
|
||||
record.address.toString().toLowerCase().includes(value.toLowerCase()),
|
||||
onFilterDropdownVisibleChange: visible => {
|
||||
|
@ -188,9 +173,8 @@ export default defineComponent({
|
|||
columns,
|
||||
handleSearch,
|
||||
handleReset,
|
||||
searchText: '',
|
||||
searchInput,
|
||||
searchedColumn: '',
|
||||
...toRefs(state),
|
||||
};
|
||||
},
|
||||
});
|
||||
|
|
|
@ -19,26 +19,29 @@ Table with editable cells.
|
|||
<template>
|
||||
<a-button class="editable-add-btn" style="margin-bottom: 8px" @click="handleAdd">Add</a-button>
|
||||
<a-table bordered :data-source="dataSource" :columns="columns">
|
||||
<template #name="{ text, record }">
|
||||
<div class="editable-cell">
|
||||
<div v-if="editableData[record.key]" class="editable-cell-input-wrapper">
|
||||
<a-input v-model:value="editableData[record.key].name" @pressEnter="save(record.key)" />
|
||||
<check-outlined class="editable-cell-icon-check" @click="save(record.key)" />
|
||||
<template #bodyCell="{ column, text, record }">
|
||||
<template v-if="column.dataIndex === 'name'">
|
||||
<div class="editable-cell">
|
||||
<div v-if="editableData[record.key]" class="editable-cell-input-wrapper">
|
||||
<a-input v-model:value="editableData[record.key].name" @pressEnter="save(record.key)" />
|
||||
<check-outlined class="editable-cell-icon-check" @click="save(record.key)" />
|
||||
</div>
|
||||
<div v-else class="editable-cell-text-wrapper">
|
||||
{{ text || ' ' }}
|
||||
<edit-outlined class="editable-cell-icon" @click="edit(record.key)" />
|
||||
</div>
|
||||
</div>
|
||||
<div v-else class="editable-cell-text-wrapper">
|
||||
{{ text || ' ' }}
|
||||
<edit-outlined class="editable-cell-icon" @click="edit(record.key)" />
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<template #operation="{ record }">
|
||||
<a-popconfirm
|
||||
v-if="dataSource.length"
|
||||
title="Sure to delete?"
|
||||
@confirm="onDelete(record.key)"
|
||||
>
|
||||
<a>Delete</a>
|
||||
</a-popconfirm>
|
||||
</template>
|
||||
<template v-else-if="column.dataIndex === 'operation'">
|
||||
<a-popconfirm
|
||||
v-if="dataSource.length"
|
||||
title="Sure to delete?"
|
||||
@confirm="onDelete(record.key)"
|
||||
>
|
||||
<a>Delete</a>
|
||||
</a-popconfirm>
|
||||
</template>
|
||||
<template v-else>{{ text }}</template>
|
||||
</template>
|
||||
</a-table>
|
||||
</template>
|
||||
|
@ -65,7 +68,6 @@ export default defineComponent({
|
|||
title: 'name',
|
||||
dataIndex: 'name',
|
||||
width: '30%',
|
||||
slots: { customRender: 'name' },
|
||||
},
|
||||
{
|
||||
title: 'age',
|
||||
|
@ -78,7 +80,6 @@ export default defineComponent({
|
|||
{
|
||||
title: 'operation',
|
||||
dataIndex: 'operation',
|
||||
slots: { customRender: 'operation' },
|
||||
},
|
||||
];
|
||||
const dataSource: Ref<DataItem[]> = ref([
|
||||
|
|
|
@ -17,30 +17,32 @@ Table with editable rows.
|
|||
|
||||
<template>
|
||||
<a-table :columns="columns" :data-source="dataSource" bordered>
|
||||
<template v-for="col in ['name', 'age', 'address']" #[col]="{ text, record }" :key="col">
|
||||
<div>
|
||||
<a-input
|
||||
v-if="editableData[record.key]"
|
||||
v-model:value="editableData[record.key][col]"
|
||||
style="margin: -5px 0"
|
||||
/>
|
||||
<template v-else>
|
||||
{{ text }}
|
||||
</template>
|
||||
</div>
|
||||
</template>
|
||||
<template #operation="{ record }">
|
||||
<div class="editable-row-operations">
|
||||
<span v-if="editableData[record.key]">
|
||||
<a @click="save(record.key)">Save</a>
|
||||
<a-popconfirm title="Sure to cancel?" @confirm="cancel(record.key)">
|
||||
<a>Cancel</a>
|
||||
</a-popconfirm>
|
||||
</span>
|
||||
<span v-else>
|
||||
<a @click="edit(record.key)">Edit</a>
|
||||
</span>
|
||||
</div>
|
||||
<template #bodyCell="{ column, text, record }">
|
||||
<template v-if="['name', 'age', 'address'].includes(column.dataIndex)">
|
||||
<div>
|
||||
<a-input
|
||||
v-if="editableData[record.key]"
|
||||
v-model:value="editableData[record.key][column.dataIndex]"
|
||||
style="margin: -5px 0"
|
||||
/>
|
||||
<template v-else>
|
||||
{{ text }}
|
||||
</template>
|
||||
</div>
|
||||
</template>
|
||||
<template v-else-if="column.dataIndex === 'operation'">
|
||||
<div class="editable-row-operations">
|
||||
<span v-if="editableData[record.key]">
|
||||
<a @click="save(record.key)">Save</a>
|
||||
<a-popconfirm title="Sure to cancel?" @confirm="cancel(record.key)">
|
||||
<a>Cancel</a>
|
||||
</a-popconfirm>
|
||||
</span>
|
||||
<span v-else>
|
||||
<a @click="edit(record.key)">Edit</a>
|
||||
</span>
|
||||
</div>
|
||||
</template>
|
||||
</template>
|
||||
</a-table>
|
||||
</template>
|
||||
|
@ -53,24 +55,20 @@ const columns = [
|
|||
title: 'name',
|
||||
dataIndex: 'name',
|
||||
width: '25%',
|
||||
slots: { customRender: 'name' },
|
||||
},
|
||||
{
|
||||
title: 'age',
|
||||
dataIndex: 'age',
|
||||
width: '15%',
|
||||
slots: { customRender: 'age' },
|
||||
},
|
||||
{
|
||||
title: 'address',
|
||||
dataIndex: 'address',
|
||||
width: '40%',
|
||||
slots: { customRender: 'address' },
|
||||
},
|
||||
{
|
||||
title: 'operation',
|
||||
dataIndex: 'operation',
|
||||
slots: { customRender: 'operation' },
|
||||
},
|
||||
];
|
||||
interface DataItem {
|
||||
|
|
|
@ -20,8 +20,11 @@ Ellipsis cell content via setting `column.ellipsis`.
|
|||
|
||||
<template>
|
||||
<a-table :columns="columns" :data-source="data">
|
||||
<template #name="{ text }">
|
||||
<a>{{ text }}</a>
|
||||
<template #bodyCell="{ column, text }">
|
||||
<template v-if="column.dataIndex === 'name'">
|
||||
<a>{{ text }}</a>
|
||||
</template>
|
||||
<template v-else>{{ text }}</template>
|
||||
</template>
|
||||
</a-table>
|
||||
</template>
|
||||
|
@ -32,7 +35,6 @@ const columns = [
|
|||
title: 'Name',
|
||||
dataIndex: 'name',
|
||||
key: 'name',
|
||||
slots: { customRender: 'name' },
|
||||
},
|
||||
{
|
||||
title: 'Age',
|
||||
|
|
|
@ -10,21 +10,23 @@ title:
|
|||
|
||||
表格支持树形数据的展示,当数据中有 `children` 字段时会自动展示为树形表格,如果不需要或配置为其他字段可以用 `childrenColumnName` 进行配置。
|
||||
可以通过设置 `indentSize` 以控制每一层的缩进宽度。
|
||||
> 注:暂不支持父子数据递归关联选择。
|
||||
|
||||
## en-US
|
||||
|
||||
Display tree structure data in Table when there is field key `children` in dataSource, try to customize `childrenColumnName` property to avoid tree table structure.
|
||||
You can control the indent width by setting `indentSize`.
|
||||
> Note, no support for recursive selection of tree structure data table yet.
|
||||
|
||||
</docs>
|
||||
|
||||
<template>
|
||||
<a-space align="center" style="margin-bottom: 16px">
|
||||
CheckStrictly:
|
||||
<a-switch v-model:checked="rowSelection.checkStrictly"></a-switch>
|
||||
</a-space>
|
||||
<a-table :columns="columns" :data-source="data" :row-selection="rowSelection" />
|
||||
</template>
|
||||
<script lang="ts">
|
||||
import { defineComponent } from 'vue';
|
||||
import { defineComponent, ref } from 'vue';
|
||||
const columns = [
|
||||
{
|
||||
title: 'Name',
|
||||
|
@ -118,7 +120,8 @@ const data: DataItem[] = [
|
|||
},
|
||||
];
|
||||
|
||||
const rowSelection = {
|
||||
const rowSelection = ref({
|
||||
checkStrictly: false,
|
||||
onChange: (selectedRowKeys: (string | number)[], selectedRows: DataItem[]) => {
|
||||
console.log(`selectedRowKeys: ${selectedRowKeys}`, 'selectedRows: ', selectedRows);
|
||||
},
|
||||
|
@ -128,7 +131,7 @@ const rowSelection = {
|
|||
onSelectAll: (selected: boolean, selectedRows: DataItem[], changeRows: DataItem[]) => {
|
||||
console.log(selected, selectedRows, changeRows);
|
||||
},
|
||||
};
|
||||
});
|
||||
|
||||
export default defineComponent({
|
||||
setup() {
|
||||
|
|
|
@ -18,8 +18,11 @@ When there's too much information to show and the table can't display all at onc
|
|||
|
||||
<template>
|
||||
<a-table :columns="columns" :data-source="data">
|
||||
<template #action>
|
||||
<a>Delete</a>
|
||||
<template #bodyCell="{ column, text }">
|
||||
<template v-if="column.key === 'action'">
|
||||
<a>Delete</a>
|
||||
</template>
|
||||
<template v-else>{{ text }}</template>
|
||||
</template>
|
||||
<template #expandedRowRender="{ record }">
|
||||
<p style="margin: 0">
|
||||
|
@ -34,7 +37,7 @@ const columns = [
|
|||
{ title: 'Name', dataIndex: 'name', key: 'name' },
|
||||
{ title: 'Age', dataIndex: 'age', key: 'age' },
|
||||
{ title: 'Address', dataIndex: 'address', key: 'address' },
|
||||
{ title: 'Action', dataIndex: '', key: 'x', slots: { customRender: 'action' } },
|
||||
{ title: 'Action', key: 'action' },
|
||||
];
|
||||
|
||||
const data = [
|
||||
|
|
|
@ -25,8 +25,11 @@ A Solution for displaying large amounts of data with long columns.
|
|||
|
||||
<template>
|
||||
<a-table :columns="columns" :data-source="data" :scroll="{ x: 1500, y: 300 }">
|
||||
<template #action>
|
||||
<a>action</a>
|
||||
<template #bodyCell="{ column, text }">
|
||||
<template v-if="column.key === 'operation'">
|
||||
<a>action</a>
|
||||
</template>
|
||||
<template v-else>{{ text }}</template>
|
||||
</template>
|
||||
</a-table>
|
||||
</template>
|
||||
|
@ -49,7 +52,6 @@ const columns = [
|
|||
key: 'operation',
|
||||
fixed: 'right',
|
||||
width: 100,
|
||||
slots: { customRender: 'action' },
|
||||
},
|
||||
];
|
||||
|
||||
|
|
|
@ -25,9 +25,12 @@ To fix some columns and scroll inside other columns, and you must set `scroll.x`
|
|||
</docs>
|
||||
|
||||
<template>
|
||||
<a-table :columns="columns" :data-source="data" :scroll="{ x: 1300 }">
|
||||
<template #action>
|
||||
<a>action</a>
|
||||
<a-table :columns="columns" :data-source="data" :scroll="{ x: 1300, y: 1000 }">
|
||||
<template #bodyCell="{ column, text }">
|
||||
<template v-if="column.key === 'operation'">
|
||||
<a>action</a>
|
||||
</template>
|
||||
<template v-else>{{ text }}</template>
|
||||
</template>
|
||||
</a-table>
|
||||
</template>
|
||||
|
@ -50,7 +53,6 @@ const columns = [
|
|||
key: 'operation',
|
||||
fixed: 'right',
|
||||
width: 100,
|
||||
slots: { customRender: 'action' },
|
||||
},
|
||||
];
|
||||
|
||||
|
|
|
@ -28,6 +28,7 @@ If a `sortOrder` or `defaultSortOrder` is specified with the value `ascend` or `
|
|||
<a-table :columns="columns" :data-source="data" @change="onChange" />
|
||||
</template>
|
||||
<script lang="ts">
|
||||
import type { TableColumnType, TableProps } from 'ant-design-vue';
|
||||
import { defineComponent } from 'vue';
|
||||
|
||||
type TableDataType = {
|
||||
|
@ -37,35 +38,7 @@ type TableDataType = {
|
|||
address: string;
|
||||
};
|
||||
|
||||
type PaginationType = {
|
||||
current: number;
|
||||
pageSize: number;
|
||||
};
|
||||
|
||||
type FilterType = {
|
||||
name: string;
|
||||
address: string;
|
||||
};
|
||||
|
||||
type ColumnType = {
|
||||
title: string;
|
||||
dataIndex: string;
|
||||
filters?: {
|
||||
text: string;
|
||||
value: string;
|
||||
children?: {
|
||||
text: string;
|
||||
value: string;
|
||||
}[];
|
||||
}[];
|
||||
onFilter?: (value: string, record: TableDataType) => boolean;
|
||||
sorter?: (a: TableDataType, b: TableDataType) => number;
|
||||
sortDirections?: string[];
|
||||
defaultSortOrder?: string;
|
||||
filterMultiple?: string[] | boolean;
|
||||
};
|
||||
|
||||
const columns: ColumnType[] = [
|
||||
const columns: TableColumnType<TableDataType>[] = [
|
||||
{
|
||||
title: 'Name',
|
||||
dataIndex: 'name',
|
||||
|
@ -153,7 +126,7 @@ const data: TableDataType[] = [
|
|||
];
|
||||
export default defineComponent({
|
||||
setup() {
|
||||
const onChange = (pagination: PaginationType, filters: FilterType[], sorter: ColumnType) => {
|
||||
const onChange: TableProps<TableDataType>['onChange'] = (pagination, filters, sorter) => {
|
||||
console.log('params', pagination, filters, sorter);
|
||||
};
|
||||
return {
|
||||
|
|
|
@ -15,6 +15,7 @@
|
|||
<FixedHeader />
|
||||
<GroupingColumns />
|
||||
<Head />
|
||||
<MultipleSorter />
|
||||
<NestedTable />
|
||||
<ResetFilter />
|
||||
<RowSelectionAndOperation />
|
||||
|
@ -22,6 +23,7 @@
|
|||
<RowSelection />
|
||||
<Size />
|
||||
<Stripe />
|
||||
<Summary />
|
||||
<TemplateCom />
|
||||
</demo-sort>
|
||||
</template>
|
||||
|
@ -51,6 +53,8 @@ import Size from './size.vue';
|
|||
import TemplateCom from './template.vue';
|
||||
import Ellipsis from './ellipsis.vue';
|
||||
import Stripe from './stripe.vue';
|
||||
import MultipleSorter from './multiple-sorter.vue';
|
||||
import Summary from './summary.vue';
|
||||
import CN from '../index.zh-CN.md';
|
||||
import US from '../index.en-US.md';
|
||||
|
||||
|
@ -81,6 +85,8 @@ export default {
|
|||
Size,
|
||||
TemplateCom,
|
||||
Stripe,
|
||||
MultipleSorter,
|
||||
Summary,
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
|
|
@ -0,0 +1,99 @@
|
|||
<docs>
|
||||
---
|
||||
order: 7
|
||||
title:
|
||||
en-US: Multiple sorter
|
||||
zh-CN: 多列排序
|
||||
---
|
||||
|
||||
## zh-CN
|
||||
|
||||
`column.sorter` 支持 `multiple` 字段以配置多列排序优先级。通过 `sorter.compare` 配置排序逻辑,你可以通过不设置该函数只启动多列排序的交互形式。
|
||||
|
||||
## en-US
|
||||
|
||||
`column.sorter` support `multiple` to config the priority of sort columns. Though `sorter.compare` to customize compare function. You can also leave it empty to use the interactive only.
|
||||
|
||||
</docs>
|
||||
|
||||
<template>
|
||||
<a-table :columns="columns" :data-source="data" @change="onChange" />
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { defineComponent } from 'vue';
|
||||
|
||||
const columns = [
|
||||
{
|
||||
title: 'Name',
|
||||
dataIndex: 'name',
|
||||
},
|
||||
{
|
||||
title: 'Chinese Score',
|
||||
dataIndex: 'chinese',
|
||||
sorter: {
|
||||
compare: (a, b) => a.chinese - b.chinese,
|
||||
multiple: 3,
|
||||
},
|
||||
},
|
||||
{
|
||||
title: 'Math Score',
|
||||
dataIndex: 'math',
|
||||
sorter: {
|
||||
compare: (a, b) => a.math - b.math,
|
||||
multiple: 2,
|
||||
},
|
||||
},
|
||||
{
|
||||
title: 'English Score',
|
||||
dataIndex: 'english',
|
||||
sorter: {
|
||||
compare: (a, b) => a.english - b.english,
|
||||
multiple: 1,
|
||||
},
|
||||
},
|
||||
];
|
||||
|
||||
const data = [
|
||||
{
|
||||
key: '1',
|
||||
name: 'John Brown',
|
||||
chinese: 98,
|
||||
math: 60,
|
||||
english: 70,
|
||||
},
|
||||
{
|
||||
key: '2',
|
||||
name: 'Jim Green',
|
||||
chinese: 98,
|
||||
math: 66,
|
||||
english: 89,
|
||||
},
|
||||
{
|
||||
key: '3',
|
||||
name: 'Joe Black',
|
||||
chinese: 98,
|
||||
math: 90,
|
||||
english: 70,
|
||||
},
|
||||
{
|
||||
key: '4',
|
||||
name: 'Jim Red',
|
||||
chinese: 88,
|
||||
math: 99,
|
||||
english: 89,
|
||||
},
|
||||
];
|
||||
|
||||
export default defineComponent({
|
||||
setup() {
|
||||
return {
|
||||
data,
|
||||
columns,
|
||||
onChange: (pagination, filters, sorter, extra) => {
|
||||
console.log('params', pagination, filters, sorter, extra);
|
||||
},
|
||||
};
|
||||
},
|
||||
});
|
||||
</script>
|
|
@ -19,34 +19,40 @@ Showing more detailed info of every row.
|
|||
|
||||
<template>
|
||||
<a-table :columns="columns" :data-source="data" class="components-table-demo-nested">
|
||||
<template #operation>
|
||||
<a>Publish</a>
|
||||
<template #bodyCell="{ column, text }">
|
||||
<template v-if="column.key === 'operation'">
|
||||
<a>Publish</a>
|
||||
</template>
|
||||
<template v-else>{{ text }}</template>
|
||||
</template>
|
||||
<template #expandedRowRender>
|
||||
<a-table :columns="innerColumns" :data-source="innerData" :pagination="false">
|
||||
<template #status>
|
||||
<span>
|
||||
<a-badge status="success" />
|
||||
Finished
|
||||
</span>
|
||||
</template>
|
||||
<template #operation>
|
||||
<span class="table-operation">
|
||||
<a>Pause</a>
|
||||
<a>Stop</a>
|
||||
<a-dropdown>
|
||||
<template #overlay>
|
||||
<a-menu>
|
||||
<a-menu-item>Action 1</a-menu-item>
|
||||
<a-menu-item>Action 2</a-menu-item>
|
||||
</a-menu>
|
||||
</template>
|
||||
<a>
|
||||
More
|
||||
<down-outlined />
|
||||
</a>
|
||||
</a-dropdown>
|
||||
</span>
|
||||
<template #bodyCell="{ column, text }">
|
||||
<template v-if="column.key === 'state'">
|
||||
<span>
|
||||
<a-badge status="success" />
|
||||
Finished
|
||||
</span>
|
||||
</template>
|
||||
<template v-else-if="column.key === 'operation'">
|
||||
<span class="table-operation">
|
||||
<a>Pause</a>
|
||||
<a>Stop</a>
|
||||
<a-dropdown>
|
||||
<template #overlay>
|
||||
<a-menu>
|
||||
<a-menu-item>Action 1</a-menu-item>
|
||||
<a-menu-item>Action 2</a-menu-item>
|
||||
</a-menu>
|
||||
</template>
|
||||
<a>
|
||||
More
|
||||
<down-outlined />
|
||||
</a>
|
||||
</a-dropdown>
|
||||
</span>
|
||||
</template>
|
||||
<template v-else>{{ text }}</template>
|
||||
</template>
|
||||
</a-table>
|
||||
</template>
|
||||
|
@ -63,7 +69,7 @@ const columns = [
|
|||
{ title: 'Upgraded', dataIndex: 'upgradeNum', key: 'upgradeNum' },
|
||||
{ title: 'Creator', dataIndex: 'creator', key: 'creator' },
|
||||
{ title: 'Date', dataIndex: 'createdAt', key: 'createdAt' },
|
||||
{ title: 'Action', key: 'operation', slots: { customRender: 'operation' } },
|
||||
{ title: 'Action', key: 'operation' },
|
||||
];
|
||||
|
||||
interface DataItem {
|
||||
|
@ -80,7 +86,7 @@ const data: DataItem[] = [];
|
|||
for (let i = 0; i < 3; ++i) {
|
||||
data.push({
|
||||
key: i,
|
||||
name: 'Screem',
|
||||
name: `Screem ${i + 1}`,
|
||||
platform: 'iOS',
|
||||
version: '10.3.4.5654',
|
||||
upgradeNum: 500,
|
||||
|
@ -92,13 +98,12 @@ for (let i = 0; i < 3; ++i) {
|
|||
const innerColumns = [
|
||||
{ title: 'Date', dataIndex: 'date', key: 'date' },
|
||||
{ title: 'Name', dataIndex: 'name', key: 'name' },
|
||||
{ title: 'Status', key: 'state', slots: { customRender: 'status' } },
|
||||
{ title: 'Status', key: 'state' },
|
||||
{ title: 'Upgrade Status', dataIndex: 'upgradeNum', key: 'upgradeNum' },
|
||||
{
|
||||
title: 'Action',
|
||||
dataIndex: 'operation',
|
||||
key: 'operation',
|
||||
slots: { customRender: 'operation' },
|
||||
},
|
||||
];
|
||||
|
||||
|
@ -114,7 +119,7 @@ for (let i = 0; i < 3; ++i) {
|
|||
innerData.push({
|
||||
key: i,
|
||||
date: '2014-12-24 23:12:00',
|
||||
name: 'This is production name',
|
||||
name: `This is production name ${i + 1}`,
|
||||
upgradeNum: 'Upgraded: 56',
|
||||
});
|
||||
}
|
||||
|
|
|
@ -35,9 +35,7 @@ Control filters and sorters by `filteredValue` and `sortOrder`.
|
|||
</template>
|
||||
<script lang="ts">
|
||||
import { computed, defineComponent, ref } from 'vue';
|
||||
import { TableState, TableStateFilters } from 'ant-design-vue/es/table/interface';
|
||||
|
||||
type Pagination = TableState['pagination'];
|
||||
import type { TableColumnType, TableProps } from 'ant-design-vue';
|
||||
|
||||
interface DataItem {
|
||||
key: string;
|
||||
|
@ -78,7 +76,7 @@ export default defineComponent({
|
|||
const filteredInfo = ref();
|
||||
const sortedInfo = ref();
|
||||
|
||||
const columns = computed(() => {
|
||||
const columns = computed<TableColumnType[]>(() => {
|
||||
const filtered = filteredInfo.value || {};
|
||||
const sorted = sortedInfo.value || {};
|
||||
return [
|
||||
|
@ -120,7 +118,7 @@ export default defineComponent({
|
|||
];
|
||||
});
|
||||
|
||||
const handleChange = (pagination: Pagination, filters: TableStateFilters, sorter: any) => {
|
||||
const handleChange: TableProps['onChange'] = (pagination, filters, sorter) => {
|
||||
console.log('Various parameters', pagination, filters, sorter);
|
||||
filteredInfo.value = filters;
|
||||
sortedInfo.value = sorter;
|
||||
|
|
|
@ -37,9 +37,8 @@ To perform operations and clear selections after selecting some rows, use `rowSe
|
|||
</template>
|
||||
<script lang="ts">
|
||||
import { computed, defineComponent, reactive, toRefs } from 'vue';
|
||||
import { ColumnProps } from 'ant-design-vue/es/table/interface';
|
||||
|
||||
type Key = ColumnProps['key'];
|
||||
type Key = string | number;
|
||||
|
||||
interface DataType {
|
||||
key: Key;
|
||||
|
|
|
@ -19,12 +19,10 @@ Use `rowSelection.selections` custom selections, default no select dropdown, sho
|
|||
</template>
|
||||
<script lang="ts">
|
||||
import { defineComponent, computed, ref, unref } from 'vue';
|
||||
import { ColumnProps } from 'ant-design-vue/es/table/interface';
|
||||
|
||||
type Key = ColumnProps['key'];
|
||||
import { Table } from 'ant-design-vue';
|
||||
|
||||
interface DataType {
|
||||
key: Key;
|
||||
key: string | number;
|
||||
name: string;
|
||||
age: number;
|
||||
address: string;
|
||||
|
@ -57,9 +55,9 @@ for (let i = 0; i < 46; i++) {
|
|||
|
||||
export default defineComponent({
|
||||
setup() {
|
||||
const selectedRowKeys = ref<Key[]>([]); // Check here to configure the default column
|
||||
const selectedRowKeys = ref<DataType['key'][]>([]); // Check here to configure the default column
|
||||
|
||||
const onSelectChange = (changableRowKeys: Key[]) => {
|
||||
const onSelectChange = (changableRowKeys: string[]) => {
|
||||
console.log('selectedRowKeys changed: ', changableRowKeys);
|
||||
selectedRowKeys.value = changableRowKeys;
|
||||
};
|
||||
|
@ -70,19 +68,15 @@ export default defineComponent({
|
|||
onChange: onSelectChange,
|
||||
hideDefaultSelections: true,
|
||||
selections: [
|
||||
{
|
||||
key: 'all-data',
|
||||
text: 'Select All Data',
|
||||
onSelect: () => {
|
||||
selectedRowKeys.value = [...Array(46).keys()]; // 0...45
|
||||
},
|
||||
},
|
||||
Table.SELECTION_ALL,
|
||||
Table.SELECTION_INVERT,
|
||||
Table.SELECTION_NONE,
|
||||
{
|
||||
key: 'odd',
|
||||
text: 'Select Odd Row',
|
||||
onSelect: (changableRowKeys: Key[]) => {
|
||||
onSelect: changableRowKeys => {
|
||||
let newSelectedRowKeys = [];
|
||||
newSelectedRowKeys = changableRowKeys.filter((key, index) => {
|
||||
newSelectedRowKeys = changableRowKeys.filter((_key, index) => {
|
||||
if (index % 2 !== 0) {
|
||||
return false;
|
||||
}
|
||||
|
@ -94,9 +88,9 @@ export default defineComponent({
|
|||
{
|
||||
key: 'even',
|
||||
text: 'Select Even Row',
|
||||
onSelect: (changableRowKeys: Key[]) => {
|
||||
onSelect: changableRowKeys => {
|
||||
let newSelectedRowKeys = [];
|
||||
newSelectedRowKeys = changableRowKeys.filter((key, index) => {
|
||||
newSelectedRowKeys = changableRowKeys.filter((_key, index) => {
|
||||
if (index % 2 !== 0) {
|
||||
return true;
|
||||
}
|
||||
|
|
|
@ -7,40 +7,40 @@ title:
|
|||
---
|
||||
|
||||
## zh-CN
|
||||
第一列是联动的选择框。
|
||||
第一列是联动的选择框。
|
||||
默认点击 checkbox 触发选择行为,需要 `点击行` 触发可参考例子:https://codesandbox.io/s/row-selection-on-click-tr58v
|
||||
|
||||
## en-US
|
||||
Rows can be selectable by making first column as a selectable column.
|
||||
Rows can be selectable by making first column as a selectable column.
|
||||
selection happens when clicking checkbox defaultly. You can see https://codesandbox.io/s/row-selection-on-click-tr58v if you need row-click selection behavior.
|
||||
|
||||
</docs>
|
||||
|
||||
<template>
|
||||
<a-table :row-selection="rowSelection" :columns="columns" :data-source="data">
|
||||
<template #name="{ text }">
|
||||
<a>{{ text }}</a>
|
||||
<template #bodyCell="{ column, text }">
|
||||
<template v-if="column.dataIndex === 'name'">
|
||||
<a>{{ text }}</a>
|
||||
</template>
|
||||
<template v-else>{{ text }}</template>
|
||||
</template>
|
||||
</a-table>
|
||||
</template>
|
||||
<script lang="ts">
|
||||
import { defineComponent } from 'vue';
|
||||
import { ColumnProps } from 'ant-design-vue/es/table/interface';
|
||||
|
||||
type Key = ColumnProps['key'];
|
||||
import type { TableProps, TableColumnType } from 'ant-design-vue';
|
||||
|
||||
interface DataType {
|
||||
key: Key;
|
||||
key: string;
|
||||
name: string;
|
||||
age: number;
|
||||
address: string;
|
||||
}
|
||||
|
||||
const columns = [
|
||||
const columns: TableColumnType<DataType>[] = [
|
||||
{
|
||||
title: 'Name',
|
||||
dataIndex: 'name',
|
||||
slots: { customRender: 'name' },
|
||||
},
|
||||
{
|
||||
title: 'Age',
|
||||
|
@ -80,8 +80,8 @@ const data: DataType[] = [
|
|||
|
||||
export default defineComponent({
|
||||
setup() {
|
||||
const rowSelection = {
|
||||
onChange: (selectedRowKeys: Key[], selectedRows: DataType[]) => {
|
||||
const rowSelection: TableProps['rowSelection'] = {
|
||||
onChange: (selectedRowKeys: string[], selectedRows: DataType[]) => {
|
||||
console.log(`selectedRowKeys: ${selectedRowKeys}`, 'selectedRows: ', selectedRows);
|
||||
},
|
||||
getCheckboxProps: (record: DataType) => ({
|
||||
|
|
|
@ -20,14 +20,14 @@ Use `rowClassName` Customize the table with Striped.
|
|||
size="middle"
|
||||
:columns="columns"
|
||||
:data-source="data"
|
||||
:row-class-name="(record, index) => (index % 2 === 1 ? 'table-striped' : null)"
|
||||
:row-class-name="(_record, index) => (index % 2 === 1 ? 'table-striped' : null)"
|
||||
/>
|
||||
<a-table
|
||||
class="ant-table-striped"
|
||||
size="middle"
|
||||
:columns="columns"
|
||||
:data-source="data"
|
||||
:row-class-name="(record, index) => (index % 2 === 1 ? 'table-striped' : null)"
|
||||
:row-class-name="(_record, index) => (index % 2 === 1 ? 'table-striped' : null)"
|
||||
bordered
|
||||
/>
|
||||
</template>
|
||||
|
|
|
@ -0,0 +1,159 @@
|
|||
<docs>
|
||||
---
|
||||
order: 29
|
||||
title:
|
||||
en-US: Summary
|
||||
zh-CN: 总结栏
|
||||
---
|
||||
|
||||
## zh-CN
|
||||
|
||||
通过 `summary` 设置总结栏。使用 `a-table-summary-cell` 同步 Column 的固定状态。你可以通过配置 `a-table-summary` 的 `fixed` 属性使其固定。
|
||||
|
||||
## en-US
|
||||
|
||||
Set summary content by `summary` prop. Sync column fixed status with `a-table-summary-cell`. You can fixed it by set `a-table-summary` `fixed` prop.
|
||||
|
||||
</docs>
|
||||
|
||||
<template>
|
||||
<a-table :columns="columns" :data-source="data" :pagination="false" bordered>
|
||||
<template #summary>
|
||||
<a-table-summary-row>
|
||||
<a-table-summary-cell>Total</a-table-summary-cell>
|
||||
<a-table-summary-cell>
|
||||
<a-typography-text type="danger">{{ totals.totalBorrow }}</a-typography-text>
|
||||
</a-table-summary-cell>
|
||||
<a-table-summary-cell>
|
||||
<a-typography-text>{{ totals.totalRepayment }}</a-typography-text>
|
||||
</a-table-summary-cell>
|
||||
</a-table-summary-row>
|
||||
<a-table-summary-row>
|
||||
<a-table-summary-cell>Balance</a-table-summary-cell>
|
||||
<a-table-summary-cell :col-span="2">
|
||||
<a-typography-text type="danger">
|
||||
{{ totals.totalBorrow - totals.totalRepayment }}
|
||||
</a-typography-text>
|
||||
</a-table-summary-cell>
|
||||
</a-table-summary-row>
|
||||
</template>
|
||||
</a-table>
|
||||
<br />
|
||||
<a-table
|
||||
:columns="fixedColumns"
|
||||
:data-source="fixedData"
|
||||
:pagination="false"
|
||||
:scroll="{ x: 2000, y: 500 }"
|
||||
bordered
|
||||
>
|
||||
<template #summary>
|
||||
<a-table-summary fixed>
|
||||
<a-table-summary-row>
|
||||
<a-table-summary-cell :index="0">Summary</a-table-summary-cell>
|
||||
<a-table-summary-cell :index="1">This is a summary content</a-table-summary-cell>
|
||||
</a-table-summary-row>
|
||||
</a-table-summary>
|
||||
</template>
|
||||
</a-table>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { computed, defineComponent, ref } from 'vue';
|
||||
|
||||
export default defineComponent({
|
||||
setup() {
|
||||
const columns = ref([
|
||||
{
|
||||
title: 'Name',
|
||||
dataIndex: 'name',
|
||||
},
|
||||
{
|
||||
title: 'Borrow',
|
||||
dataIndex: 'borrow',
|
||||
},
|
||||
{
|
||||
title: 'Repayment',
|
||||
dataIndex: 'repayment',
|
||||
},
|
||||
]);
|
||||
|
||||
const data = ref([
|
||||
{
|
||||
key: '1',
|
||||
name: 'John Brown',
|
||||
borrow: 10,
|
||||
repayment: 33,
|
||||
},
|
||||
{
|
||||
key: '2',
|
||||
name: 'Jim Green',
|
||||
borrow: 100,
|
||||
repayment: 0,
|
||||
},
|
||||
{
|
||||
key: '3',
|
||||
name: 'Joe Black',
|
||||
borrow: 10,
|
||||
repayment: 10,
|
||||
},
|
||||
{
|
||||
key: '4',
|
||||
name: 'Jim Red',
|
||||
borrow: 75,
|
||||
repayment: 45,
|
||||
},
|
||||
]);
|
||||
|
||||
const fixedColumns = ref([
|
||||
{
|
||||
title: 'Name',
|
||||
dataIndex: 'name',
|
||||
fixed: true,
|
||||
width: 100,
|
||||
},
|
||||
{
|
||||
title: 'Description',
|
||||
dataIndex: 'description',
|
||||
},
|
||||
]);
|
||||
|
||||
const fixedData = ref<{ key: number; name: string; description: string }[]>([]);
|
||||
for (let i = 0; i < 20; i += 1) {
|
||||
fixedData.value.push({
|
||||
key: i,
|
||||
name: ['Light', 'Bamboo', 'Little'][i % 3],
|
||||
description: 'Everything that has a beginning, has an end.',
|
||||
});
|
||||
}
|
||||
|
||||
const totals = computed(() => {
|
||||
let totalBorrow = 0;
|
||||
let totalRepayment = 0;
|
||||
|
||||
data.value.forEach(({ borrow, repayment }) => {
|
||||
totalBorrow += borrow;
|
||||
totalRepayment += repayment;
|
||||
});
|
||||
return { totalBorrow, totalRepayment };
|
||||
});
|
||||
return {
|
||||
data,
|
||||
columns,
|
||||
totals,
|
||||
fixedColumns,
|
||||
fixedData,
|
||||
};
|
||||
},
|
||||
});
|
||||
</script>
|
||||
|
||||
<style>
|
||||
#components-table-demo-summary tfoot th,
|
||||
#components-table-demo-summary tfoot td {
|
||||
background: #fafafa;
|
||||
}
|
||||
[data-theme='dark'] #components-table-demo-summary tfoot th,
|
||||
[data-theme='dark'] #components-table-demo-summary tfoot td {
|
||||
background: #1d1d1d;
|
||||
}
|
||||
</style>
|
|
@ -8,13 +8,17 @@ title:
|
|||
|
||||
## zh-CN
|
||||
|
||||
使用 template 风格的 API
|
||||
使用 template 风格的 API。
|
||||
|
||||
> 不推荐使用,会有一定的性能损耗。
|
||||
|
||||
> 这个只是一个描述 `columns` 的语法糖,所以你不能用其他组件去包裹 `Column` 和 `ColumnGroup`。
|
||||
|
||||
## en-US
|
||||
|
||||
Using template style API
|
||||
Using template style API.
|
||||
|
||||
> Not recommended, there will be a certain performance loss.
|
||||
|
||||
> Since this is just a syntax sugar for the prop `columns`, so that you can't compose `Column` and `ColumnGroup` with other Components.
|
||||
|
||||
|
|
|
@ -1,290 +0,0 @@
|
|||
import { reactive, defineComponent, nextTick, computed, watch } from 'vue';
|
||||
import FilterFilled from '@ant-design/icons-vue/FilterFilled';
|
||||
import Menu, { SubMenu, MenuItem } from '../menu';
|
||||
import classNames from '../_util/classNames';
|
||||
import shallowequal from '../_util/shallowequal';
|
||||
import Dropdown from '../dropdown';
|
||||
import Checkbox from '../checkbox';
|
||||
import Radio from '../radio';
|
||||
import FilterDropdownMenuWrapper from './FilterDropdownMenuWrapper';
|
||||
import { FilterMenuProps } from './interface';
|
||||
import { isValidElement } from '../_util/props-util';
|
||||
import initDefaultProps from '../_util/props-util/initDefaultProps';
|
||||
import { cloneElement } from '../_util/vnode';
|
||||
import BaseMixin2 from '../_util/BaseMixin2';
|
||||
import { generateValueMaps } from './util';
|
||||
import type { Key } from '../_util/type';
|
||||
|
||||
function stopPropagation(e) {
|
||||
e.stopPropagation();
|
||||
}
|
||||
|
||||
export default defineComponent({
|
||||
name: 'FilterMenu',
|
||||
mixins: [BaseMixin2],
|
||||
inheritAttrs: false,
|
||||
props: initDefaultProps(FilterMenuProps, {
|
||||
column: {},
|
||||
}),
|
||||
setup(props) {
|
||||
const sSelectedKeys = computed(() => props.selectedKeys);
|
||||
const sVisible = computed(() => {
|
||||
return 'filterDropdownVisible' in props.column ? props.column.filterDropdownVisible : false;
|
||||
});
|
||||
const sValueKeys = computed(() => generateValueMaps(props.column.filters));
|
||||
const state = reactive({
|
||||
neverShown: false,
|
||||
sSelectedKeys: sSelectedKeys.value,
|
||||
sKeyPathOfSelectedItem: {}, // 记录所有有选中子菜单的祖先菜单
|
||||
sVisible: sVisible.value,
|
||||
sValueKeys: sValueKeys.value,
|
||||
});
|
||||
watch(sSelectedKeys, () => {
|
||||
state.sSelectedKeys = sSelectedKeys.value;
|
||||
});
|
||||
watch(sVisible, () => {
|
||||
state.sVisible = sVisible.value;
|
||||
});
|
||||
watch(sValueKeys, () => {
|
||||
state.sValueKeys = sValueKeys.value;
|
||||
});
|
||||
// watchEffect(
|
||||
// () => {
|
||||
// const { column } = nextProps;
|
||||
// if (!shallowequal(preProps.selectedKeys, nextProps.selectedKeys)) {
|
||||
// state.sSelectedKeys = nextProps.selectedKeys;
|
||||
// }
|
||||
// if (!shallowequal((preProps.column || {}).filters, (nextProps.column || {}).filters)) {
|
||||
// state.sValueKeys = generateValueMaps(nextProps.column.filters);
|
||||
// }
|
||||
// if ('filterDropdownVisible' in column) {
|
||||
// state.sVisible = column.filterDropdownVisible;
|
||||
// }
|
||||
// preProps = { ...nextProps };
|
||||
// },
|
||||
// { flush: 'sync' },
|
||||
// );
|
||||
return state;
|
||||
},
|
||||
methods: {
|
||||
getDropdownVisible() {
|
||||
return !!this.sVisible;
|
||||
},
|
||||
|
||||
setSelectedKeys({ selectedKeys }) {
|
||||
this.setState({ sSelectedKeys: selectedKeys });
|
||||
},
|
||||
|
||||
setVisible(visible: boolean) {
|
||||
const { column } = this;
|
||||
if (!('filterDropdownVisible' in column)) {
|
||||
this.setState({ sVisible: visible });
|
||||
}
|
||||
if (column.onFilterDropdownVisibleChange) {
|
||||
column.onFilterDropdownVisibleChange(visible);
|
||||
}
|
||||
},
|
||||
|
||||
handleClearFilters() {
|
||||
this.setState(
|
||||
{
|
||||
sSelectedKeys: [],
|
||||
},
|
||||
this.handleConfirm,
|
||||
);
|
||||
},
|
||||
|
||||
handleConfirm() {
|
||||
this.setVisible(false);
|
||||
// Call `setSelectedKeys` & `confirm` in the same time will make filter data not up to date
|
||||
// https://github.com/ant-design/ant-design/issues/12284
|
||||
(this as any).$forceUpdate();
|
||||
nextTick(this.confirmFilter2);
|
||||
},
|
||||
|
||||
onVisibleChange(visible: boolean) {
|
||||
this.setVisible(visible);
|
||||
const { column } = this.$props;
|
||||
// https://github.com/ant-design/ant-design/issues/17833
|
||||
if (!visible && !(column.filterDropdown instanceof Function)) {
|
||||
this.confirmFilter2();
|
||||
}
|
||||
},
|
||||
handleMenuItemClick(info: { keyPath: Key[]; key: Key }) {
|
||||
const { sSelectedKeys: selectedKeys } = this;
|
||||
if (!info.keyPath || info.keyPath.length <= 1) {
|
||||
return;
|
||||
}
|
||||
const { sKeyPathOfSelectedItem: keyPathOfSelectedItem } = this;
|
||||
if (selectedKeys && selectedKeys.indexOf(info.key) >= 0) {
|
||||
// deselect SubMenu child
|
||||
delete keyPathOfSelectedItem[info.key];
|
||||
} else {
|
||||
// select SubMenu child
|
||||
keyPathOfSelectedItem[info.key] = info.keyPath;
|
||||
}
|
||||
this.setState({ sKeyPathOfSelectedItem: keyPathOfSelectedItem });
|
||||
},
|
||||
|
||||
hasSubMenu() {
|
||||
const {
|
||||
column: { filters = [] },
|
||||
} = this;
|
||||
return filters.some(item => !!(item.children && item.children.length > 0));
|
||||
},
|
||||
|
||||
confirmFilter2() {
|
||||
const { column, selectedKeys: propSelectedKeys, confirmFilter } = this.$props;
|
||||
const { sSelectedKeys: selectedKeys, sValueKeys: valueKeys } = this;
|
||||
const { filterDropdown } = column;
|
||||
|
||||
if (!shallowequal(selectedKeys, propSelectedKeys)) {
|
||||
confirmFilter(
|
||||
column,
|
||||
filterDropdown
|
||||
? selectedKeys
|
||||
: selectedKeys.map((key: any) => valueKeys[key]).filter(key => key !== undefined),
|
||||
);
|
||||
}
|
||||
},
|
||||
|
||||
renderMenus(items) {
|
||||
const { dropdownPrefixCls, prefixCls } = this.$props;
|
||||
return items.map(item => {
|
||||
if (item.children && item.children.length > 0) {
|
||||
const { sKeyPathOfSelectedItem } = this;
|
||||
const containSelected = Object.keys(sKeyPathOfSelectedItem).some(
|
||||
key => sKeyPathOfSelectedItem[key].indexOf(item.value) >= 0,
|
||||
);
|
||||
const subMenuCls = classNames(`${prefixCls}-dropdown-submenu`, {
|
||||
[`${dropdownPrefixCls}-submenu-contain-selected`]: containSelected,
|
||||
});
|
||||
return (
|
||||
<SubMenu title={item.text} popupClassName={subMenuCls} key={item.value}>
|
||||
{this.renderMenus(item.children)}
|
||||
</SubMenu>
|
||||
);
|
||||
}
|
||||
return this.renderMenuItem(item);
|
||||
});
|
||||
},
|
||||
|
||||
renderFilterIcon() {
|
||||
const { column, locale, prefixCls, selectedKeys } = this;
|
||||
const filtered = selectedKeys && selectedKeys.length > 0;
|
||||
let filterIcon = column.filterIcon;
|
||||
if (typeof filterIcon === 'function') {
|
||||
filterIcon = filterIcon({ filtered, column });
|
||||
}
|
||||
const dropdownIconClass = classNames({
|
||||
[`${prefixCls}-selected`]: 'filtered' in column ? column.filtered : filtered,
|
||||
[`${prefixCls}-open`]: this.getDropdownVisible(),
|
||||
});
|
||||
if (!filterIcon) {
|
||||
return (
|
||||
<FilterFilled
|
||||
title={locale.filterTitle}
|
||||
class={dropdownIconClass}
|
||||
onClick={stopPropagation}
|
||||
/>
|
||||
);
|
||||
}
|
||||
if (filterIcon.length === 1 && isValidElement(filterIcon[0])) {
|
||||
return cloneElement(filterIcon[0], {
|
||||
title: filterIcon.props?.title || locale.filterTitle,
|
||||
onClick: stopPropagation,
|
||||
class: classNames(`${prefixCls}-icon`, dropdownIconClass, filterIcon.props?.class),
|
||||
});
|
||||
}
|
||||
return (
|
||||
<span class={classNames(`${prefixCls}-icon`, dropdownIconClass)} onClick={stopPropagation}>
|
||||
{filterIcon}
|
||||
</span>
|
||||
);
|
||||
},
|
||||
|
||||
renderMenuItem(item) {
|
||||
const { column } = this;
|
||||
const { sSelectedKeys: selectedKeys } = this;
|
||||
const multiple = 'filterMultiple' in column ? column.filterMultiple : true;
|
||||
const input = multiple ? (
|
||||
<Checkbox checked={selectedKeys && selectedKeys.indexOf(item.value) >= 0} />
|
||||
) : (
|
||||
<Radio checked={selectedKeys && selectedKeys.indexOf(item.value) >= 0} />
|
||||
);
|
||||
|
||||
return (
|
||||
<MenuItem key={item.value}>
|
||||
{input}
|
||||
<span>{item.text}</span>
|
||||
</MenuItem>
|
||||
);
|
||||
},
|
||||
},
|
||||
|
||||
render() {
|
||||
const { sSelectedKeys: originSelectedKeys } = this as any;
|
||||
const { column, locale, prefixCls, dropdownPrefixCls, getPopupContainer } = this;
|
||||
// default multiple selection in filter dropdown
|
||||
const multiple = 'filterMultiple' in column ? column.filterMultiple : true;
|
||||
const dropdownMenuClass = classNames({
|
||||
[`${dropdownPrefixCls}-menu-without-submenu`]: !this.hasSubMenu(),
|
||||
});
|
||||
let { filterDropdown } = column;
|
||||
if (filterDropdown instanceof Function) {
|
||||
filterDropdown = filterDropdown({
|
||||
prefixCls: `${dropdownPrefixCls}-custom`,
|
||||
setSelectedKeys: selectedKeys => this.setSelectedKeys({ selectedKeys }),
|
||||
selectedKeys: originSelectedKeys,
|
||||
confirm: this.handleConfirm,
|
||||
clearFilters: this.handleClearFilters,
|
||||
filters: column.filters,
|
||||
visible: this.getDropdownVisible(),
|
||||
column,
|
||||
});
|
||||
}
|
||||
|
||||
const menus = filterDropdown ? (
|
||||
<FilterDropdownMenuWrapper class={`${prefixCls}-dropdown`}>
|
||||
{filterDropdown}
|
||||
</FilterDropdownMenuWrapper>
|
||||
) : (
|
||||
<FilterDropdownMenuWrapper class={`${prefixCls}-dropdown`}>
|
||||
<Menu
|
||||
multiple={multiple}
|
||||
onClick={this.handleMenuItemClick}
|
||||
prefixCls={`${dropdownPrefixCls}-menu`}
|
||||
class={dropdownMenuClass}
|
||||
onSelect={this.setSelectedKeys}
|
||||
onDeselect={this.setSelectedKeys}
|
||||
selectedKeys={originSelectedKeys}
|
||||
getPopupContainer={getPopupContainer}
|
||||
>
|
||||
{this.renderMenus(column.filters)}
|
||||
</Menu>
|
||||
<div class={`${prefixCls}-dropdown-btns`}>
|
||||
<a class={`${prefixCls}-dropdown-link confirm`} onClick={this.handleConfirm}>
|
||||
{locale.filterConfirm}
|
||||
</a>
|
||||
<a class={`${prefixCls}-dropdown-link clear`} onClick={this.handleClearFilters}>
|
||||
{locale.filterReset}
|
||||
</a>
|
||||
</div>
|
||||
</FilterDropdownMenuWrapper>
|
||||
);
|
||||
|
||||
return (
|
||||
<Dropdown
|
||||
trigger={['click']}
|
||||
placement="bottomRight"
|
||||
visible={this.getDropdownVisible()}
|
||||
onVisibleChange={this.onVisibleChange}
|
||||
getPopupContainer={getPopupContainer}
|
||||
forceRender
|
||||
overlay={menus}
|
||||
>
|
||||
{this.renderFilterIcon()}
|
||||
</Dropdown>
|
||||
);
|
||||
},
|
||||
});
|
|
@ -0,0 +1,45 @@
|
|||
import devWarning from '../../vc-util/devWarning';
|
||||
import type { Ref } from 'vue';
|
||||
import type { ContextSlots } from '../context';
|
||||
import type { TransformColumns, ColumnsType } from '../interface';
|
||||
|
||||
function fillSlots<RecordType>(columns: ColumnsType<RecordType>, contextSlots: Ref<ContextSlots>) {
|
||||
const $slots = contextSlots.value;
|
||||
return columns.map(column => {
|
||||
const cloneColumn = { ...column };
|
||||
const { slots = {} } = cloneColumn;
|
||||
cloneColumn.__originColumn__ = column;
|
||||
devWarning(
|
||||
!('slots' in cloneColumn),
|
||||
'Table',
|
||||
'`column.slots` is deprecated. Please use `v-slot:headerCell` `v-slot:bodyCell` instead.',
|
||||
);
|
||||
|
||||
Object.keys(slots).forEach(key => {
|
||||
const name = slots[key];
|
||||
if (cloneColumn[key] === undefined && $slots[name]) {
|
||||
cloneColumn[key] = $slots[name];
|
||||
}
|
||||
});
|
||||
|
||||
if (contextSlots.value.headerCell && !column.slots?.title) {
|
||||
cloneColumn.title = contextSlots.value.headerCell({
|
||||
title: column.title,
|
||||
column,
|
||||
});
|
||||
}
|
||||
if ('children' in cloneColumn) {
|
||||
cloneColumn.children = fillSlots(cloneColumn.children, contextSlots);
|
||||
}
|
||||
|
||||
return cloneColumn;
|
||||
});
|
||||
}
|
||||
|
||||
export default function useColumns<RecordType>(
|
||||
contextSlots: Ref<ContextSlots>,
|
||||
): [TransformColumns<RecordType>] {
|
||||
const filledColumns = (columns: ColumnsType<RecordType>) => fillSlots(columns, contextSlots);
|
||||
|
||||
return [filledColumns];
|
||||
}
|
|
@ -0,0 +1,366 @@
|
|||
import isEqual from 'lodash-es/isEqual';
|
||||
import FilterFilled from '@ant-design/icons-vue/FilterFilled';
|
||||
import Button from '../../../button';
|
||||
import Menu from '../../../menu';
|
||||
import Checkbox from '../../../checkbox';
|
||||
import Radio from '../../../radio';
|
||||
import Dropdown from '../../../dropdown';
|
||||
import Empty from '../../../empty';
|
||||
import type {
|
||||
ColumnType,
|
||||
ColumnFilterItem,
|
||||
Key,
|
||||
TableLocale,
|
||||
GetPopupContainer,
|
||||
} from '../../interface';
|
||||
import FilterDropdownMenuWrapper from './FilterWrapper';
|
||||
import type { FilterState } from '.';
|
||||
import { computed, defineComponent, onBeforeUnmount, ref, watch } from 'vue';
|
||||
import classNames from '../../../_util/classNames';
|
||||
import useConfigInject from '../../../_util/hooks/useConfigInject';
|
||||
import { useInjectSlots } from '../../context';
|
||||
|
||||
const { SubMenu, Item: MenuItem } = Menu;
|
||||
|
||||
function hasSubMenu(filters: ColumnFilterItem[]) {
|
||||
return filters.some(({ children }) => children && children.length > 0);
|
||||
}
|
||||
|
||||
function renderFilterItems({
|
||||
filters,
|
||||
prefixCls,
|
||||
filteredKeys,
|
||||
filterMultiple,
|
||||
locale,
|
||||
}: {
|
||||
filters: ColumnFilterItem[];
|
||||
prefixCls: string;
|
||||
filteredKeys: Key[];
|
||||
filterMultiple: boolean;
|
||||
locale: TableLocale;
|
||||
}) {
|
||||
if (filters.length === 0) {
|
||||
// wrapped with <div /> to avoid react warning
|
||||
// https://github.com/ant-design/ant-design/issues/25979
|
||||
return (
|
||||
<MenuItem key="empty">
|
||||
<div
|
||||
style={{
|
||||
margin: '16px 0',
|
||||
}}
|
||||
>
|
||||
<Empty
|
||||
image={Empty.PRESENTED_IMAGE_SIMPLE}
|
||||
description={locale.filterEmptyText}
|
||||
imageStyle={{
|
||||
height: 24,
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
</MenuItem>
|
||||
);
|
||||
}
|
||||
return filters.map((filter, index) => {
|
||||
const key = String(filter.value);
|
||||
|
||||
if (filter.children) {
|
||||
return (
|
||||
<SubMenu
|
||||
key={key || index}
|
||||
title={filter.text}
|
||||
popupClassName={`${prefixCls}-dropdown-submenu`}
|
||||
>
|
||||
{renderFilterItems({
|
||||
filters: filter.children,
|
||||
prefixCls,
|
||||
filteredKeys,
|
||||
filterMultiple,
|
||||
locale,
|
||||
})}
|
||||
</SubMenu>
|
||||
);
|
||||
}
|
||||
|
||||
const Component = filterMultiple ? Checkbox : Radio;
|
||||
|
||||
return (
|
||||
<MenuItem key={filter.value !== undefined ? key : index}>
|
||||
<Component checked={filteredKeys.includes(key)} />
|
||||
<span>{filter.text}</span>
|
||||
</MenuItem>
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
export interface FilterDropdownProps<RecordType> {
|
||||
tablePrefixCls: string;
|
||||
prefixCls: string;
|
||||
dropdownPrefixCls: string;
|
||||
column: ColumnType<RecordType>;
|
||||
filterState?: FilterState<RecordType>;
|
||||
filterMultiple: boolean;
|
||||
columnKey: Key;
|
||||
triggerFilter: (filterState: FilterState<RecordType>) => void;
|
||||
locale: TableLocale;
|
||||
getPopupContainer?: GetPopupContainer;
|
||||
}
|
||||
|
||||
export default defineComponent<FilterDropdownProps<any>>({
|
||||
name: 'FilterDropdown',
|
||||
props: [
|
||||
'tablePrefixCls',
|
||||
'prefixCls',
|
||||
'dropdownPrefixCls',
|
||||
'column',
|
||||
'filterState',
|
||||
'filterMultiple',
|
||||
'columnKey',
|
||||
'triggerFilter',
|
||||
'locale',
|
||||
'getPopupContainer',
|
||||
] as any,
|
||||
setup(props, { slots }) {
|
||||
const contextSlots = useInjectSlots();
|
||||
const filterDropdownVisible = computed(() => props.column.filterDropdownVisible);
|
||||
const visible = ref(false);
|
||||
const filtered = computed(
|
||||
() =>
|
||||
!!(
|
||||
props.filterState &&
|
||||
(props.filterState.filteredKeys?.length || props.filterState.forceFiltered)
|
||||
),
|
||||
);
|
||||
|
||||
const filterDropdownRef = computed(() => {
|
||||
const { filterDropdown, slots = {}, customFilterDropdown } = props.column;
|
||||
return (
|
||||
filterDropdown ||
|
||||
(slots.filterDropdown && contextSlots.value[slots.filterDropdown]) ||
|
||||
(customFilterDropdown && contextSlots.value.customFilterDropdown)
|
||||
);
|
||||
});
|
||||
|
||||
const filterIconRef = computed(() => {
|
||||
const { filterIcon, slots = {} } = props.column;
|
||||
return (
|
||||
filterIcon ||
|
||||
(slots.filterIcon && contextSlots.value[slots.filterIcon]) ||
|
||||
contextSlots.value.customFilterIcon
|
||||
);
|
||||
});
|
||||
|
||||
const triggerVisible = (newVisible: boolean) => {
|
||||
visible.value = newVisible;
|
||||
props.column.onFilterDropdownVisibleChange?.(newVisible);
|
||||
};
|
||||
|
||||
const mergedVisible = computed(() =>
|
||||
typeof filterDropdownVisible.value === 'boolean'
|
||||
? filterDropdownVisible.value
|
||||
: visible.value,
|
||||
);
|
||||
|
||||
const propFilteredKeys = computed(() => props.filterState?.filteredKeys);
|
||||
|
||||
const filteredKeys = ref([]);
|
||||
|
||||
const onSelectKeys = ({ selectedKeys }: { selectedKeys?: Key[] }) => {
|
||||
filteredKeys.value = selectedKeys;
|
||||
};
|
||||
|
||||
watch(
|
||||
propFilteredKeys,
|
||||
() => {
|
||||
onSelectKeys({ selectedKeys: propFilteredKeys.value || [] });
|
||||
},
|
||||
{ immediate: true },
|
||||
);
|
||||
|
||||
const openKeys = ref([]);
|
||||
|
||||
const openRef = ref();
|
||||
|
||||
const onOpenChange = (keys: string[]) => {
|
||||
openRef.value = window.setTimeout(() => {
|
||||
openKeys.value = keys;
|
||||
});
|
||||
};
|
||||
const onMenuClick = () => {
|
||||
window.clearTimeout(openRef.value);
|
||||
};
|
||||
|
||||
onBeforeUnmount(() => {
|
||||
window.clearTimeout(openRef.value);
|
||||
});
|
||||
|
||||
// ======================= Submit ========================
|
||||
const internalTriggerFilter = (keys: Key[] | undefined | null) => {
|
||||
const { column, columnKey, filterState } = props;
|
||||
const mergedKeys = keys && keys.length ? keys : null;
|
||||
if (mergedKeys === null && (!filterState || !filterState.filteredKeys)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (isEqual(mergedKeys, filterState?.filteredKeys)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
props.triggerFilter({
|
||||
column,
|
||||
key: columnKey,
|
||||
filteredKeys: mergedKeys,
|
||||
});
|
||||
};
|
||||
|
||||
const onConfirm = () => {
|
||||
triggerVisible(false);
|
||||
internalTriggerFilter(filteredKeys.value);
|
||||
};
|
||||
|
||||
const onReset = () => {
|
||||
filteredKeys.value = [];
|
||||
triggerVisible(false);
|
||||
internalTriggerFilter([]);
|
||||
};
|
||||
|
||||
const doFilter = ({ closeDropdown } = { closeDropdown: true }) => {
|
||||
if (closeDropdown) {
|
||||
triggerVisible(false);
|
||||
}
|
||||
internalTriggerFilter(filteredKeys.value);
|
||||
};
|
||||
|
||||
const onVisibleChange = (newVisible: boolean) => {
|
||||
if (newVisible && propFilteredKeys.value !== undefined) {
|
||||
// Sync filteredKeys on appear in controlled mode (propFilteredKeys.value !== undefiend)
|
||||
filteredKeys.value = propFilteredKeys.value || [];
|
||||
}
|
||||
triggerVisible(newVisible);
|
||||
|
||||
// Default will filter when closed
|
||||
if (!newVisible && !filterDropdownRef.value) {
|
||||
onConfirm();
|
||||
}
|
||||
};
|
||||
|
||||
const { direction } = useConfigInject('', props);
|
||||
return () => {
|
||||
const {
|
||||
tablePrefixCls,
|
||||
prefixCls,
|
||||
column,
|
||||
dropdownPrefixCls,
|
||||
filterMultiple,
|
||||
locale,
|
||||
getPopupContainer,
|
||||
} = props;
|
||||
// ======================== Style ========================
|
||||
const dropdownMenuClass = classNames({
|
||||
[`${dropdownPrefixCls}-menu-without-submenu`]: !hasSubMenu(column.filters || []),
|
||||
});
|
||||
|
||||
let dropdownContent;
|
||||
|
||||
if (typeof filterDropdownRef.value === 'function') {
|
||||
dropdownContent = filterDropdownRef.value({
|
||||
prefixCls: `${dropdownPrefixCls}-custom`,
|
||||
setSelectedKeys: (selectedKeys: Key[]) => onSelectKeys({ selectedKeys }),
|
||||
selectedKeys: filteredKeys.value,
|
||||
confirm: doFilter,
|
||||
clearFilters: onReset,
|
||||
filters: column.filters,
|
||||
visible: mergedVisible.value,
|
||||
column: column.__originColumn__,
|
||||
});
|
||||
} else if (filterDropdownRef.value) {
|
||||
dropdownContent = filterDropdownRef.value;
|
||||
} else {
|
||||
const selectedKeys = filteredKeys.value as any;
|
||||
dropdownContent = (
|
||||
<>
|
||||
<Menu
|
||||
multiple={filterMultiple}
|
||||
prefixCls={`${dropdownPrefixCls}-menu`}
|
||||
class={dropdownMenuClass}
|
||||
onClick={onMenuClick}
|
||||
onSelect={onSelectKeys}
|
||||
onDeselect={onSelectKeys}
|
||||
selectedKeys={selectedKeys}
|
||||
getPopupContainer={getPopupContainer}
|
||||
openKeys={openKeys.value}
|
||||
onOpenChange={onOpenChange}
|
||||
v-slots={{
|
||||
default: () =>
|
||||
renderFilterItems({
|
||||
filters: column.filters || [],
|
||||
prefixCls,
|
||||
filteredKeys: filteredKeys.value,
|
||||
filterMultiple,
|
||||
locale,
|
||||
}),
|
||||
}}
|
||||
></Menu>
|
||||
<div class={`${prefixCls}-dropdown-btns`}>
|
||||
<Button
|
||||
type="link"
|
||||
size="small"
|
||||
disabled={selectedKeys.length === 0}
|
||||
onClick={onReset}
|
||||
>
|
||||
{locale.filterReset}
|
||||
</Button>
|
||||
<Button type="primary" size="small" onClick={onConfirm}>
|
||||
{locale.filterConfirm}
|
||||
</Button>
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
const menu = (
|
||||
<FilterDropdownMenuWrapper class={`${prefixCls}-dropdown`}>
|
||||
{dropdownContent}
|
||||
</FilterDropdownMenuWrapper>
|
||||
);
|
||||
|
||||
let filterIcon;
|
||||
if (typeof filterIconRef.value === 'function') {
|
||||
filterIcon = filterIconRef.value({
|
||||
filtered: filtered.value,
|
||||
column: column.__originColumn__,
|
||||
});
|
||||
} else if (filterIconRef.value) {
|
||||
filterIcon = filterIconRef.value;
|
||||
} else {
|
||||
filterIcon = <FilterFilled />;
|
||||
}
|
||||
|
||||
return (
|
||||
<div class={`${prefixCls}-column`}>
|
||||
<span class={`${tablePrefixCls}-column-title`}>{slots.default?.()}</span>
|
||||
<Dropdown
|
||||
overlay={menu}
|
||||
trigger={['click']}
|
||||
visible={mergedVisible.value}
|
||||
onVisibleChange={onVisibleChange}
|
||||
getPopupContainer={getPopupContainer}
|
||||
placement={direction.value === 'rtl' ? 'bottomLeft' : 'bottomRight'}
|
||||
>
|
||||
<span
|
||||
role="button"
|
||||
tabindex={-1}
|
||||
class={classNames(`${prefixCls}-trigger`, {
|
||||
active: filtered.value,
|
||||
})}
|
||||
onClick={e => {
|
||||
e.stopPropagation();
|
||||
}}
|
||||
>
|
||||
{filterIcon}
|
||||
</span>
|
||||
</Dropdown>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
},
|
||||
});
|
|
@ -0,0 +1,5 @@
|
|||
const FilterDropdownMenuWrapper = (_props, { slots }) => (
|
||||
<div onClick={e => e.stopPropagation()}>{slots.default?.()}</div>
|
||||
);
|
||||
|
||||
export default FilterDropdownMenuWrapper;
|
|
@ -0,0 +1,264 @@
|
|||
import type { DefaultRecordType } from '../../../vc-table/interface';
|
||||
import devWarning from '../../../vc-util/devWarning';
|
||||
import useState from '../../../_util/hooks/useState';
|
||||
import type { Ref } from 'vue';
|
||||
import { computed } from 'vue';
|
||||
import type {
|
||||
TransformColumns,
|
||||
ColumnsType,
|
||||
ColumnType,
|
||||
ColumnTitleProps,
|
||||
Key,
|
||||
TableLocale,
|
||||
FilterValue,
|
||||
FilterKey,
|
||||
GetPopupContainer,
|
||||
ColumnFilterItem,
|
||||
} from '../../interface';
|
||||
import { getColumnPos, renderColumnTitle, getColumnKey } from '../../util';
|
||||
import FilterDropdown from './FilterDropdown';
|
||||
|
||||
export interface FilterState<RecordType = DefaultRecordType> {
|
||||
column: ColumnType<RecordType>;
|
||||
key: Key;
|
||||
filteredKeys?: FilterKey;
|
||||
forceFiltered?: boolean;
|
||||
}
|
||||
|
||||
function collectFilterStates<RecordType>(
|
||||
columns: ColumnsType<RecordType>,
|
||||
init: boolean,
|
||||
pos?: string,
|
||||
): FilterState<RecordType>[] {
|
||||
let filterStates: FilterState<RecordType>[] = [];
|
||||
|
||||
(columns || []).forEach((column, index) => {
|
||||
const columnPos = getColumnPos(index, pos);
|
||||
const hasFilterDropdown =
|
||||
column.filterDropdown || column?.slots?.filterDropdown || column.customFilterDropdown;
|
||||
if (column.filters || hasFilterDropdown || 'onFilter' in column) {
|
||||
if ('filteredValue' in column) {
|
||||
// Controlled
|
||||
let filteredValues = column.filteredValue;
|
||||
if (!hasFilterDropdown) {
|
||||
filteredValues = filteredValues?.map(String) ?? filteredValues;
|
||||
}
|
||||
filterStates.push({
|
||||
column,
|
||||
key: getColumnKey(column, columnPos),
|
||||
filteredKeys: filteredValues as FilterKey,
|
||||
forceFiltered: column.filtered,
|
||||
});
|
||||
} else {
|
||||
// Uncontrolled
|
||||
filterStates.push({
|
||||
column,
|
||||
key: getColumnKey(column, columnPos),
|
||||
filteredKeys: (init && column.defaultFilteredValue
|
||||
? column.defaultFilteredValue!
|
||||
: undefined) as FilterKey,
|
||||
forceFiltered: column.filtered,
|
||||
});
|
||||
}
|
||||
}
|
||||
if ('children' in column) {
|
||||
filterStates = [...filterStates, ...collectFilterStates(column.children, init, columnPos)];
|
||||
}
|
||||
});
|
||||
|
||||
return filterStates;
|
||||
}
|
||||
|
||||
function injectFilter<RecordType>(
|
||||
prefixCls: string,
|
||||
dropdownPrefixCls: string,
|
||||
columns: ColumnsType<RecordType>,
|
||||
filterStates: FilterState<RecordType>[],
|
||||
triggerFilter: (filterState: FilterState<RecordType>) => void,
|
||||
getPopupContainer: GetPopupContainer | undefined,
|
||||
locale: TableLocale,
|
||||
pos?: string,
|
||||
): ColumnsType<RecordType> {
|
||||
return columns.map((column, index) => {
|
||||
const columnPos = getColumnPos(index, pos);
|
||||
const { filterMultiple = true } = column as ColumnType<RecordType>;
|
||||
|
||||
let newColumn: ColumnsType<RecordType>[number] = column;
|
||||
const hasFilterDropdown =
|
||||
column.filterDropdown || column?.slots?.filterDropdown || column.customFilterDropdown;
|
||||
if (newColumn.filters || hasFilterDropdown) {
|
||||
const columnKey = getColumnKey(newColumn, columnPos);
|
||||
const filterState = filterStates.find(({ key }) => columnKey === key);
|
||||
|
||||
newColumn = {
|
||||
...newColumn,
|
||||
title: (renderProps: ColumnTitleProps<RecordType>) => (
|
||||
<FilterDropdown
|
||||
tablePrefixCls={prefixCls}
|
||||
prefixCls={`${prefixCls}-filter`}
|
||||
dropdownPrefixCls={dropdownPrefixCls}
|
||||
column={newColumn}
|
||||
columnKey={columnKey}
|
||||
filterState={filterState}
|
||||
filterMultiple={filterMultiple}
|
||||
triggerFilter={triggerFilter}
|
||||
locale={locale}
|
||||
getPopupContainer={getPopupContainer}
|
||||
>
|
||||
{renderColumnTitle(column.title, renderProps)}
|
||||
</FilterDropdown>
|
||||
),
|
||||
};
|
||||
}
|
||||
|
||||
if ('children' in newColumn) {
|
||||
newColumn = {
|
||||
...newColumn,
|
||||
children: injectFilter(
|
||||
prefixCls,
|
||||
dropdownPrefixCls,
|
||||
newColumn.children,
|
||||
filterStates,
|
||||
triggerFilter,
|
||||
getPopupContainer,
|
||||
locale,
|
||||
columnPos,
|
||||
),
|
||||
};
|
||||
}
|
||||
|
||||
return newColumn;
|
||||
});
|
||||
}
|
||||
|
||||
function flattenKeys(filters?: ColumnFilterItem[]) {
|
||||
let keys: FilterValue = [];
|
||||
(filters || []).forEach(({ value, children }) => {
|
||||
keys.push(value);
|
||||
if (children) {
|
||||
keys = [...keys, ...flattenKeys(children)];
|
||||
}
|
||||
});
|
||||
return keys;
|
||||
}
|
||||
|
||||
function generateFilterInfo<RecordType>(filterStates: FilterState<RecordType>[]) {
|
||||
const currentFilters: Record<string, FilterValue | null> = {};
|
||||
|
||||
filterStates.forEach(({ key, filteredKeys, column }) => {
|
||||
const hasFilterDropdown =
|
||||
column.filterDropdown || column?.slots?.filterDropdown || column.customFilterDropdown;
|
||||
const { filters } = column;
|
||||
if (hasFilterDropdown) {
|
||||
currentFilters[key] = filteredKeys || null;
|
||||
} else if (Array.isArray(filteredKeys)) {
|
||||
const keys = flattenKeys(filters);
|
||||
currentFilters[key] = keys.filter(originKey => filteredKeys.includes(String(originKey)));
|
||||
} else {
|
||||
currentFilters[key] = null;
|
||||
}
|
||||
});
|
||||
|
||||
return currentFilters;
|
||||
}
|
||||
|
||||
export function getFilterData<RecordType>(
|
||||
data: RecordType[],
|
||||
filterStates: FilterState<RecordType>[],
|
||||
) {
|
||||
return filterStates.reduce((currentData, filterState) => {
|
||||
const {
|
||||
column: { onFilter, filters },
|
||||
filteredKeys,
|
||||
} = filterState;
|
||||
if (onFilter && filteredKeys && filteredKeys.length) {
|
||||
return currentData.filter(record =>
|
||||
filteredKeys.some(key => {
|
||||
const keys = flattenKeys(filters);
|
||||
const keyIndex = keys.findIndex(k => String(k) === String(key));
|
||||
const realKey = keyIndex !== -1 ? keys[keyIndex] : key;
|
||||
return onFilter(realKey, record);
|
||||
}),
|
||||
);
|
||||
}
|
||||
return currentData;
|
||||
}, data);
|
||||
}
|
||||
|
||||
interface FilterConfig<RecordType> {
|
||||
prefixCls: Ref<string>;
|
||||
dropdownPrefixCls: Ref<string>;
|
||||
mergedColumns: Ref<ColumnsType<RecordType>>;
|
||||
locale: Ref<TableLocale>;
|
||||
onFilterChange: (
|
||||
filters: Record<string, FilterValue | null>,
|
||||
filterStates: FilterState<RecordType>[],
|
||||
) => void;
|
||||
getPopupContainer?: Ref<GetPopupContainer>;
|
||||
}
|
||||
|
||||
function useFilter<RecordType>({
|
||||
prefixCls,
|
||||
dropdownPrefixCls,
|
||||
mergedColumns,
|
||||
locale,
|
||||
onFilterChange,
|
||||
getPopupContainer,
|
||||
}: FilterConfig<RecordType>): [
|
||||
TransformColumns<RecordType>,
|
||||
Ref<FilterState<RecordType>[]>,
|
||||
Ref<Record<string, FilterValue | null>>,
|
||||
] {
|
||||
const [filterStates, setFilterStates] = useState<FilterState<RecordType>[]>(
|
||||
collectFilterStates(mergedColumns.value, true),
|
||||
);
|
||||
|
||||
const mergedFilterStates = computed(() => {
|
||||
const collectedStates = collectFilterStates(mergedColumns.value, false);
|
||||
|
||||
const filteredKeysIsNotControlled = collectedStates.every(
|
||||
({ filteredKeys }) => filteredKeys === undefined,
|
||||
);
|
||||
|
||||
// Return if not controlled
|
||||
if (filteredKeysIsNotControlled) {
|
||||
return filterStates.value;
|
||||
}
|
||||
|
||||
const filteredKeysIsAllControlled = collectedStates.every(
|
||||
({ filteredKeys }) => filteredKeys !== undefined,
|
||||
);
|
||||
|
||||
devWarning(
|
||||
filteredKeysIsNotControlled || filteredKeysIsAllControlled,
|
||||
'Table',
|
||||
'`FilteredKeys` should all be controlled or not controlled.',
|
||||
);
|
||||
|
||||
return collectedStates;
|
||||
});
|
||||
|
||||
const filters = computed(() => generateFilterInfo(mergedFilterStates.value));
|
||||
|
||||
const triggerFilter = (filterState: FilterState<RecordType>) => {
|
||||
const newFilterStates = mergedFilterStates.value.filter(({ key }) => key !== filterState.key);
|
||||
newFilterStates.push(filterState);
|
||||
setFilterStates(newFilterStates);
|
||||
onFilterChange(generateFilterInfo(newFilterStates), newFilterStates);
|
||||
};
|
||||
|
||||
const transformColumns = (innerColumns: ColumnsType<RecordType>) => {
|
||||
return injectFilter(
|
||||
prefixCls.value,
|
||||
dropdownPrefixCls.value,
|
||||
innerColumns,
|
||||
mergedFilterStates.value,
|
||||
triggerFilter,
|
||||
getPopupContainer.value,
|
||||
locale.value,
|
||||
);
|
||||
};
|
||||
return [transformColumns, mergedFilterStates, filters];
|
||||
}
|
||||
|
||||
export default useFilter;
|
|
@ -0,0 +1,52 @@
|
|||
import type { Ref } from 'vue';
|
||||
import { watch } from 'vue';
|
||||
import { ref } from 'vue';
|
||||
import type { Key, GetRowKey } from '../interface';
|
||||
|
||||
interface MapCache<RecordType> {
|
||||
kvMap?: Map<Key, RecordType>;
|
||||
}
|
||||
|
||||
export default function useLazyKVMap<RecordType>(
|
||||
dataRef: Ref<readonly RecordType[]>,
|
||||
childrenColumnNameRef: Ref<string>,
|
||||
getRowKeyRef: Ref<GetRowKey<RecordType>>,
|
||||
) {
|
||||
const mapCacheRef = ref<MapCache<RecordType>>({});
|
||||
|
||||
watch(
|
||||
[dataRef, childrenColumnNameRef, getRowKeyRef],
|
||||
() => {
|
||||
const kvMap = new Map<Key, RecordType>();
|
||||
const getRowKey = getRowKeyRef.value;
|
||||
const childrenColumnName = childrenColumnNameRef.value;
|
||||
/* eslint-disable no-inner-declarations */
|
||||
function dig(records: readonly RecordType[]) {
|
||||
records.forEach((record, index) => {
|
||||
const rowKey = getRowKey(record, index);
|
||||
kvMap.set(rowKey, record);
|
||||
|
||||
if (record && typeof record === 'object' && childrenColumnName in record) {
|
||||
dig((record as any)[childrenColumnName] || []);
|
||||
}
|
||||
});
|
||||
}
|
||||
/* eslint-enable */
|
||||
|
||||
dig(dataRef.value);
|
||||
|
||||
mapCacheRef.value = {
|
||||
kvMap,
|
||||
};
|
||||
},
|
||||
{
|
||||
deep: false,
|
||||
immediate: true,
|
||||
},
|
||||
);
|
||||
function getRecordByKey(key: Key): RecordType {
|
||||
return mapCacheRef.value.kvMap!.get(key)!;
|
||||
}
|
||||
|
||||
return [getRecordByKey];
|
||||
}
|
|
@ -0,0 +1,107 @@
|
|||
import useState from '../../_util/hooks/useState';
|
||||
import type { Ref } from 'vue';
|
||||
import { computed } from 'vue';
|
||||
import type { PaginationProps } from '../../pagination';
|
||||
import type { TablePaginationConfig } from '../interface';
|
||||
|
||||
export const DEFAULT_PAGE_SIZE = 10;
|
||||
|
||||
export function getPaginationParam(
|
||||
pagination: TablePaginationConfig | boolean | undefined,
|
||||
mergedPagination: TablePaginationConfig,
|
||||
) {
|
||||
const param: any = {
|
||||
current: mergedPagination.current,
|
||||
pageSize: mergedPagination.pageSize,
|
||||
};
|
||||
const paginationObj = pagination && typeof pagination === 'object' ? pagination : {};
|
||||
|
||||
Object.keys(paginationObj).forEach(pageProp => {
|
||||
const value = (mergedPagination as any)[pageProp];
|
||||
|
||||
if (typeof value !== 'function') {
|
||||
param[pageProp] = value;
|
||||
}
|
||||
});
|
||||
|
||||
return param;
|
||||
}
|
||||
|
||||
function extendsObject<T extends Object>(...list: T[]) {
|
||||
const result: T = {} as T;
|
||||
|
||||
list.forEach(obj => {
|
||||
if (obj) {
|
||||
Object.keys(obj).forEach(key => {
|
||||
const val = (obj as any)[key];
|
||||
if (val !== undefined) {
|
||||
(result as any)[key] = val;
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
export default function usePagination(
|
||||
totalRef: Ref<number>,
|
||||
paginationRef: Ref<TablePaginationConfig | false | undefined>,
|
||||
onChange: (current: number, pageSize: number) => void,
|
||||
): [Ref<TablePaginationConfig>, () => void] {
|
||||
const pagination = computed(() =>
|
||||
paginationRef.value && typeof paginationRef.value === 'object' ? paginationRef.value : {},
|
||||
);
|
||||
const paginationTotal = computed(() => pagination.value.total || 0);
|
||||
const [innerPagination, setInnerPagination] = useState<{
|
||||
current?: number;
|
||||
pageSize?: number;
|
||||
}>(() => ({
|
||||
current: 'defaultCurrent' in pagination.value ? pagination.value.defaultCurrent : 1,
|
||||
pageSize:
|
||||
'defaultPageSize' in pagination.value ? pagination.value.defaultPageSize : DEFAULT_PAGE_SIZE,
|
||||
}));
|
||||
|
||||
// ============ Basic Pagination Config ============
|
||||
const mergedPagination = computed(() =>
|
||||
extendsObject<Partial<TablePaginationConfig>>(innerPagination.value, pagination.value, {
|
||||
total: paginationTotal.value > 0 ? paginationTotal.value : totalRef.value,
|
||||
}),
|
||||
);
|
||||
|
||||
// Reset `current` if data length or pageSize changed
|
||||
const maxPage = Math.ceil(
|
||||
(paginationTotal.value || totalRef.value) / mergedPagination.value.pageSize!,
|
||||
);
|
||||
if (mergedPagination.value.current! > maxPage) {
|
||||
// Prevent a maximum page count of 0
|
||||
mergedPagination.value.current = maxPage || 1;
|
||||
}
|
||||
|
||||
const refreshPagination = (current = 1, pageSize?: number) => {
|
||||
setInnerPagination({
|
||||
current,
|
||||
pageSize: pageSize || mergedPagination.value.pageSize,
|
||||
});
|
||||
};
|
||||
|
||||
const onInternalChange: PaginationProps['onChange'] = (current, pageSize) => {
|
||||
if (pagination.value) {
|
||||
pagination.value.onChange?.(current, pageSize);
|
||||
}
|
||||
refreshPagination(current, pageSize);
|
||||
onChange(current, pageSize || mergedPagination.value.pageSize);
|
||||
};
|
||||
|
||||
if (pagination.value === false) {
|
||||
return [computed(() => ({})), () => {}];
|
||||
}
|
||||
|
||||
return [
|
||||
computed(() => ({
|
||||
...mergedPagination.value,
|
||||
onChange: onInternalChange,
|
||||
})),
|
||||
refreshPagination,
|
||||
];
|
||||
}
|
|
@ -0,0 +1,611 @@
|
|||
import DownOutlined from '@ant-design/icons-vue/DownOutlined';
|
||||
import type { DataNode } from '../../tree';
|
||||
import { INTERNAL_COL_DEFINE } from '../../vc-table';
|
||||
import type { ColumnType, FixedType } from '../../vc-table/interface';
|
||||
import type { GetCheckDisabled } from '../../vc-tree/interface';
|
||||
import { arrAdd, arrDel } from '../../vc-tree/util';
|
||||
import { conductCheck } from '../../vc-tree/utils/conductUtil';
|
||||
import { convertDataToEntities } from '../../vc-tree/utils/treeUtil';
|
||||
import devWarning from '../../vc-util/devWarning';
|
||||
import useMergedState from '../../_util/hooks/useMergedState';
|
||||
import useState from '../../_util/hooks/useState';
|
||||
import type { Ref } from 'vue';
|
||||
import { computed, ref, watchEffect } from 'vue';
|
||||
import type { CheckboxProps } from '../../checkbox';
|
||||
import Checkbox from '../../checkbox';
|
||||
import Dropdown from '../../dropdown';
|
||||
import Menu from '../../menu';
|
||||
import Radio from '../../radio';
|
||||
import type {
|
||||
TableRowSelection,
|
||||
Key,
|
||||
ColumnsType,
|
||||
GetRowKey,
|
||||
TableLocale,
|
||||
SelectionItem,
|
||||
TransformColumns,
|
||||
ExpandType,
|
||||
GetPopupContainer,
|
||||
} from '../interface';
|
||||
|
||||
// TODO: warning if use ajax!!!
|
||||
export const SELECTION_ALL = 'SELECT_ALL' as const;
|
||||
export const SELECTION_INVERT = 'SELECT_INVERT' as const;
|
||||
export const SELECTION_NONE = 'SELECT_NONE' as const;
|
||||
|
||||
function getFixedType<RecordType>(column: ColumnsType<RecordType>[number]): FixedType | undefined {
|
||||
return (column && column.fixed) as FixedType;
|
||||
}
|
||||
|
||||
interface UseSelectionConfig<RecordType> {
|
||||
prefixCls: Ref<string>;
|
||||
pageData: Ref<RecordType[]>;
|
||||
data: Ref<RecordType[]>;
|
||||
getRowKey: Ref<GetRowKey<RecordType>>;
|
||||
getRecordByKey: (key: Key) => RecordType;
|
||||
expandType: Ref<ExpandType>;
|
||||
childrenColumnName: Ref<string>;
|
||||
expandIconColumnIndex?: Ref<number>;
|
||||
locale: Ref<TableLocale>;
|
||||
getPopupContainer?: Ref<GetPopupContainer>;
|
||||
}
|
||||
|
||||
export type INTERNAL_SELECTION_ITEM =
|
||||
| SelectionItem
|
||||
| typeof SELECTION_ALL
|
||||
| typeof SELECTION_INVERT
|
||||
| typeof SELECTION_NONE;
|
||||
|
||||
function flattenData<RecordType>(
|
||||
data: RecordType[] | undefined,
|
||||
childrenColumnName: string,
|
||||
): RecordType[] {
|
||||
let list: RecordType[] = [];
|
||||
(data || []).forEach(record => {
|
||||
list.push(record);
|
||||
|
||||
if (record && typeof record === 'object' && childrenColumnName in record) {
|
||||
list = [
|
||||
...list,
|
||||
...flattenData<RecordType>((record as any)[childrenColumnName], childrenColumnName),
|
||||
];
|
||||
}
|
||||
});
|
||||
|
||||
return list;
|
||||
}
|
||||
|
||||
export default function useSelection<RecordType>(
|
||||
rowSelectionRef: Ref<TableRowSelection<RecordType> | undefined>,
|
||||
configRef: UseSelectionConfig<RecordType>,
|
||||
): [TransformColumns<RecordType>, Ref<Set<Key>>] {
|
||||
// ======================== Caches ========================
|
||||
const preserveRecordsRef = ref(new Map<Key, RecordType>());
|
||||
const mergedRowSelection = computed(() => {
|
||||
const temp = rowSelectionRef.value || {};
|
||||
const { checkStrictly = true } = temp;
|
||||
return { ...temp, checkStrictly };
|
||||
});
|
||||
// ========================= Keys =========================
|
||||
const [mergedSelectedKeys, setMergedSelectedKeys] = useMergedState(
|
||||
mergedRowSelection.value.selectedRowKeys ||
|
||||
mergedRowSelection.value.defaultSelectedRowKeys ||
|
||||
[],
|
||||
{
|
||||
value: computed(() => mergedRowSelection.value.selectedRowKeys),
|
||||
},
|
||||
);
|
||||
|
||||
const keyEntities = computed(() =>
|
||||
mergedRowSelection.value.checkStrictly
|
||||
? { keyEntities: null }
|
||||
: convertDataToEntities(configRef.data.value as unknown as DataNode[], {
|
||||
externalGetKey: configRef.getRowKey.value as any,
|
||||
childrenPropName: configRef.childrenColumnName.value,
|
||||
}).keyEntities,
|
||||
);
|
||||
|
||||
// Get flatten data
|
||||
const flattedData = computed(() =>
|
||||
flattenData(configRef.pageData.value, configRef.childrenColumnName.value),
|
||||
);
|
||||
|
||||
// Get all checkbox props
|
||||
const checkboxPropsMap = computed(() => {
|
||||
const map = new Map<Key, Partial<CheckboxProps>>();
|
||||
const getRowKey = configRef.getRowKey.value;
|
||||
const getCheckboxProps = mergedRowSelection.value.getCheckboxProps;
|
||||
flattedData.value.forEach((record, index) => {
|
||||
const key = getRowKey(record, index);
|
||||
const checkboxProps = (getCheckboxProps ? getCheckboxProps(record) : null) || {};
|
||||
map.set(key, checkboxProps);
|
||||
|
||||
if (
|
||||
process.env.NODE_ENV !== 'production' &&
|
||||
('checked' in checkboxProps || 'defaultChecked' in checkboxProps)
|
||||
) {
|
||||
devWarning(
|
||||
false,
|
||||
'Table',
|
||||
'Do not set `checked` or `defaultChecked` in `getCheckboxProps`. Please use `selectedRowKeys` instead.',
|
||||
);
|
||||
}
|
||||
});
|
||||
return map;
|
||||
});
|
||||
|
||||
const isCheckboxDisabled: GetCheckDisabled<RecordType> = (r: RecordType) =>
|
||||
!!checkboxPropsMap.value.get(configRef.getRowKey.value(r))?.disabled;
|
||||
|
||||
const selectKeysState = computed(() => {
|
||||
if (mergedRowSelection.value.checkStrictly) {
|
||||
return [mergedSelectedKeys.value || [], []];
|
||||
}
|
||||
const { checkedKeys, halfCheckedKeys } = conductCheck(
|
||||
mergedSelectedKeys.value,
|
||||
true,
|
||||
keyEntities.value,
|
||||
isCheckboxDisabled as any,
|
||||
);
|
||||
return [checkedKeys || [], halfCheckedKeys];
|
||||
});
|
||||
|
||||
const derivedSelectedKeys = computed(() => selectKeysState.value[0]);
|
||||
const derivedHalfSelectedKeys = computed(() => selectKeysState.value[1]);
|
||||
|
||||
const derivedSelectedKeySet = computed<Set<Key>>(() => {
|
||||
const keys =
|
||||
mergedRowSelection.value.type === 'radio'
|
||||
? derivedSelectedKeys.value.slice(0, 1)
|
||||
: derivedSelectedKeys.value;
|
||||
return new Set(keys);
|
||||
});
|
||||
const derivedHalfSelectedKeySet = computed(() =>
|
||||
mergedRowSelection.value.type === 'radio' ? new Set() : new Set(derivedHalfSelectedKeys.value),
|
||||
);
|
||||
|
||||
// Save last selected key to enable range selection
|
||||
const [lastSelectedKey, setLastSelectedKey] = useState<Key | null>(null);
|
||||
|
||||
// Reset if rowSelection reset
|
||||
watchEffect(() => {
|
||||
if (!rowSelectionRef.value) {
|
||||
setMergedSelectedKeys([]);
|
||||
}
|
||||
});
|
||||
|
||||
const setSelectedKeys = (keys: Key[]) => {
|
||||
let availableKeys: Key[];
|
||||
let records: RecordType[];
|
||||
const { preserveSelectedRowKeys, onChange: onSelectionChange } = mergedRowSelection.value;
|
||||
const { getRecordByKey } = configRef;
|
||||
if (preserveSelectedRowKeys) {
|
||||
// Keep key if mark as preserveSelectedRowKeys
|
||||
const newCache = new Map<Key, RecordType>();
|
||||
availableKeys = keys;
|
||||
records = keys.map(key => {
|
||||
let record = getRecordByKey(key);
|
||||
|
||||
if (!record && preserveRecordsRef.value.has(key)) {
|
||||
record = preserveRecordsRef.value.get(key)!;
|
||||
}
|
||||
|
||||
newCache.set(key, record);
|
||||
|
||||
return record;
|
||||
});
|
||||
|
||||
// Refresh to new cache
|
||||
preserveRecordsRef.value = newCache;
|
||||
} else {
|
||||
// Filter key which not exist in the `dataSource`
|
||||
availableKeys = [];
|
||||
records = [];
|
||||
|
||||
keys.forEach(key => {
|
||||
const record = getRecordByKey(key);
|
||||
if (record !== undefined) {
|
||||
availableKeys.push(key);
|
||||
records.push(record);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
setMergedSelectedKeys(availableKeys);
|
||||
|
||||
onSelectionChange?.(availableKeys, records);
|
||||
};
|
||||
|
||||
// ====================== Selections ======================
|
||||
// Trigger single `onSelect` event
|
||||
const triggerSingleSelection = (key: Key, selected: boolean, keys: Key[], event: Event) => {
|
||||
const { onSelect } = mergedRowSelection.value;
|
||||
const { getRecordByKey } = configRef || {};
|
||||
if (onSelect) {
|
||||
const rows = keys.map(k => getRecordByKey(k));
|
||||
onSelect(getRecordByKey(key), selected, rows, event);
|
||||
}
|
||||
|
||||
setSelectedKeys(keys);
|
||||
};
|
||||
|
||||
const mergedSelections = computed(() => {
|
||||
const { onSelectInvert, onSelectNone, selections, hideSelectAll } = mergedRowSelection.value;
|
||||
|
||||
const { data, pageData, getRowKey, locale: tableLocale } = configRef;
|
||||
|
||||
if (!selections || hideSelectAll) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const selectionList: INTERNAL_SELECTION_ITEM[] =
|
||||
selections === true ? [SELECTION_ALL, SELECTION_INVERT, SELECTION_NONE] : selections;
|
||||
|
||||
return selectionList.map((selection: INTERNAL_SELECTION_ITEM) => {
|
||||
if (selection === SELECTION_ALL) {
|
||||
return {
|
||||
key: 'all',
|
||||
text: tableLocale.value.selectionAll,
|
||||
onSelect() {
|
||||
setSelectedKeys(data.value.map((record, index) => getRowKey.value(record, index)));
|
||||
},
|
||||
};
|
||||
}
|
||||
if (selection === SELECTION_INVERT) {
|
||||
return {
|
||||
key: 'invert',
|
||||
text: tableLocale.value.selectInvert,
|
||||
onSelect() {
|
||||
const keySet = new Set(derivedSelectedKeySet.value);
|
||||
pageData.value.forEach((record, index) => {
|
||||
const key = getRowKey.value(record, index);
|
||||
|
||||
if (keySet.has(key)) {
|
||||
keySet.delete(key);
|
||||
} else {
|
||||
keySet.add(key);
|
||||
}
|
||||
});
|
||||
|
||||
const keys = Array.from(keySet);
|
||||
if (onSelectInvert) {
|
||||
devWarning(
|
||||
false,
|
||||
'Table',
|
||||
'`onSelectInvert` will be removed in future. Please use `onChange` instead.',
|
||||
);
|
||||
onSelectInvert(keys);
|
||||
}
|
||||
|
||||
setSelectedKeys(keys);
|
||||
},
|
||||
};
|
||||
}
|
||||
if (selection === SELECTION_NONE) {
|
||||
return {
|
||||
key: 'none',
|
||||
text: tableLocale.value.selectNone,
|
||||
onSelect() {
|
||||
onSelectNone?.();
|
||||
setSelectedKeys([]);
|
||||
},
|
||||
};
|
||||
}
|
||||
return selection as SelectionItem;
|
||||
});
|
||||
});
|
||||
const flattedDataLength = computed(() => flattedData.value.length);
|
||||
// ======================= Columns ========================
|
||||
const transformColumns = (columns: ColumnsType<RecordType>): ColumnsType<RecordType> => {
|
||||
const {
|
||||
onSelectAll,
|
||||
onSelectMultiple,
|
||||
columnWidth: selectionColWidth,
|
||||
type: selectionType,
|
||||
fixed,
|
||||
renderCell: customizeRenderCell,
|
||||
hideSelectAll,
|
||||
checkStrictly,
|
||||
} = mergedRowSelection.value;
|
||||
|
||||
const {
|
||||
prefixCls,
|
||||
getRecordByKey,
|
||||
getRowKey,
|
||||
expandType,
|
||||
expandIconColumnIndex,
|
||||
getPopupContainer,
|
||||
} = configRef;
|
||||
if (!rowSelectionRef.value) {
|
||||
return columns;
|
||||
}
|
||||
|
||||
// Support selection
|
||||
const keySet = new Set(derivedSelectedKeySet.value);
|
||||
|
||||
// Record key only need check with enabled
|
||||
const recordKeys = flattedData.value
|
||||
.map(getRowKey.value)
|
||||
.filter(key => !checkboxPropsMap.value.get(key)!.disabled);
|
||||
const checkedCurrentAll = recordKeys.every(key => keySet.has(key));
|
||||
const checkedCurrentSome = recordKeys.some(key => keySet.has(key));
|
||||
|
||||
const onSelectAllChange = () => {
|
||||
const changeKeys: Key[] = [];
|
||||
|
||||
if (checkedCurrentAll) {
|
||||
recordKeys.forEach(key => {
|
||||
keySet.delete(key);
|
||||
changeKeys.push(key);
|
||||
});
|
||||
} else {
|
||||
recordKeys.forEach(key => {
|
||||
if (!keySet.has(key)) {
|
||||
keySet.add(key);
|
||||
changeKeys.push(key);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
const keys = Array.from(keySet);
|
||||
|
||||
onSelectAll?.(
|
||||
!checkedCurrentAll,
|
||||
keys.map(k => getRecordByKey(k)),
|
||||
changeKeys.map(k => getRecordByKey(k)),
|
||||
);
|
||||
|
||||
setSelectedKeys(keys);
|
||||
};
|
||||
|
||||
// ===================== Render =====================
|
||||
// Title Cell
|
||||
let title;
|
||||
if (selectionType !== 'radio') {
|
||||
let customizeSelections;
|
||||
if (mergedSelections.value) {
|
||||
const menu = (
|
||||
<Menu getPopupContainer={getPopupContainer.value}>
|
||||
{mergedSelections.value.map((selection, index) => {
|
||||
const { key, text, onSelect: onSelectionClick } = selection;
|
||||
return (
|
||||
<Menu.Item
|
||||
key={key || index}
|
||||
onClick={() => {
|
||||
onSelectionClick?.(recordKeys);
|
||||
}}
|
||||
>
|
||||
{text}
|
||||
</Menu.Item>
|
||||
);
|
||||
})}
|
||||
</Menu>
|
||||
);
|
||||
customizeSelections = (
|
||||
<div class={`${prefixCls.value}-selection-extra`}>
|
||||
<Dropdown overlay={menu} getPopupContainer={getPopupContainer.value}>
|
||||
<span>
|
||||
<DownOutlined />
|
||||
</span>
|
||||
</Dropdown>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
const allDisabledData = flattedData.value
|
||||
.map((record, index) => {
|
||||
const key = getRowKey.value(record, index);
|
||||
const checkboxProps = checkboxPropsMap.value.get(key) || {};
|
||||
return { checked: keySet.has(key), ...checkboxProps };
|
||||
})
|
||||
.filter(({ disabled }) => disabled);
|
||||
|
||||
const allDisabled =
|
||||
!!allDisabledData.length && allDisabledData.length === flattedDataLength.value;
|
||||
|
||||
const allDisabledAndChecked = allDisabled && allDisabledData.every(({ checked }) => checked);
|
||||
const allDisabledSomeChecked = allDisabled && allDisabledData.some(({ checked }) => checked);
|
||||
|
||||
title = !hideSelectAll && (
|
||||
<div class={`${prefixCls.value}-selection`}>
|
||||
<Checkbox
|
||||
checked={
|
||||
!allDisabled ? !!flattedDataLength.value && checkedCurrentAll : allDisabledAndChecked
|
||||
}
|
||||
indeterminate={
|
||||
!allDisabled
|
||||
? !checkedCurrentAll && checkedCurrentSome
|
||||
: !allDisabledAndChecked && allDisabledSomeChecked
|
||||
}
|
||||
onChange={onSelectAllChange}
|
||||
disabled={flattedDataLength.value === 0 || allDisabled}
|
||||
skipGroup
|
||||
/>
|
||||
{customizeSelections}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
// Body Cell
|
||||
let renderCell: ({ record, index }: { record: RecordType; index: number }) => {
|
||||
node: any;
|
||||
checked: boolean;
|
||||
};
|
||||
if (selectionType === 'radio') {
|
||||
renderCell = ({ record, index }) => {
|
||||
const key = getRowKey.value(record, index);
|
||||
const checked = keySet.has(key);
|
||||
|
||||
return {
|
||||
node: (
|
||||
<Radio
|
||||
{...checkboxPropsMap.value.get(key)}
|
||||
checked={checked}
|
||||
onClick={e => e.stopPropagation()}
|
||||
onChange={event => {
|
||||
if (!keySet.has(key)) {
|
||||
triggerSingleSelection(key, true, [key], event.nativeEvent);
|
||||
}
|
||||
}}
|
||||
/>
|
||||
),
|
||||
checked,
|
||||
};
|
||||
};
|
||||
} else {
|
||||
renderCell = ({ record, index }) => {
|
||||
const key = getRowKey.value(record, index);
|
||||
const checked = keySet.has(key);
|
||||
const indeterminate = derivedHalfSelectedKeySet.value.has(key);
|
||||
const checkboxProps = checkboxPropsMap.value.get(key);
|
||||
let mergedIndeterminate: boolean;
|
||||
if (expandType.value === 'nest') {
|
||||
mergedIndeterminate = indeterminate;
|
||||
devWarning(
|
||||
typeof checkboxProps?.indeterminate !== 'boolean',
|
||||
'Table',
|
||||
'set `indeterminate` using `rowSelection.getCheckboxProps` is not allowed with tree structured dataSource.',
|
||||
);
|
||||
} else {
|
||||
mergedIndeterminate = checkboxProps?.indeterminate ?? indeterminate;
|
||||
}
|
||||
// Record checked
|
||||
return {
|
||||
node: (
|
||||
<Checkbox
|
||||
{...checkboxProps}
|
||||
indeterminate={mergedIndeterminate}
|
||||
checked={checked}
|
||||
skipGroup
|
||||
onClick={e => e.stopPropagation()}
|
||||
onChange={({ nativeEvent }) => {
|
||||
const { shiftKey } = nativeEvent;
|
||||
|
||||
let startIndex = -1;
|
||||
let endIndex = -1;
|
||||
|
||||
// Get range of this
|
||||
if (shiftKey && checkStrictly) {
|
||||
const pointKeys = new Set([lastSelectedKey.value, key]);
|
||||
|
||||
recordKeys.some((recordKey, recordIndex) => {
|
||||
if (pointKeys.has(recordKey)) {
|
||||
if (startIndex === -1) {
|
||||
startIndex = recordIndex;
|
||||
} else {
|
||||
endIndex = recordIndex;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
});
|
||||
}
|
||||
|
||||
if (endIndex !== -1 && startIndex !== endIndex && checkStrictly) {
|
||||
// Batch update selections
|
||||
const rangeKeys = recordKeys.slice(startIndex, endIndex + 1);
|
||||
const changedKeys: Key[] = [];
|
||||
|
||||
if (checked) {
|
||||
rangeKeys.forEach(recordKey => {
|
||||
if (keySet.has(recordKey)) {
|
||||
changedKeys.push(recordKey);
|
||||
keySet.delete(recordKey);
|
||||
}
|
||||
});
|
||||
} else {
|
||||
rangeKeys.forEach(recordKey => {
|
||||
if (!keySet.has(recordKey)) {
|
||||
changedKeys.push(recordKey);
|
||||
keySet.add(recordKey);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
const keys = Array.from(keySet);
|
||||
onSelectMultiple?.(
|
||||
!checked,
|
||||
keys.map(recordKey => getRecordByKey(recordKey)),
|
||||
changedKeys.map(recordKey => getRecordByKey(recordKey)),
|
||||
);
|
||||
|
||||
setSelectedKeys(keys);
|
||||
} else {
|
||||
// Single record selected
|
||||
const originCheckedKeys = derivedSelectedKeys.value;
|
||||
if (checkStrictly) {
|
||||
const checkedKeys = checked
|
||||
? arrDel(originCheckedKeys, key)
|
||||
: arrAdd(originCheckedKeys, key);
|
||||
triggerSingleSelection(key, !checked, checkedKeys, nativeEvent);
|
||||
} else {
|
||||
// Always fill first
|
||||
const result = conductCheck(
|
||||
[...originCheckedKeys, key],
|
||||
true,
|
||||
keyEntities.value,
|
||||
isCheckboxDisabled as any,
|
||||
);
|
||||
const { checkedKeys, halfCheckedKeys } = result;
|
||||
let nextCheckedKeys = checkedKeys;
|
||||
|
||||
// If remove, we do it again to correction
|
||||
if (checked) {
|
||||
const tempKeySet = new Set(checkedKeys);
|
||||
tempKeySet.delete(key);
|
||||
nextCheckedKeys = conductCheck(
|
||||
Array.from(tempKeySet),
|
||||
{ checked: false, halfCheckedKeys },
|
||||
keyEntities.value,
|
||||
isCheckboxDisabled as any,
|
||||
).checkedKeys;
|
||||
}
|
||||
|
||||
triggerSingleSelection(key, !checked, nextCheckedKeys, nativeEvent);
|
||||
}
|
||||
}
|
||||
|
||||
setLastSelectedKey(key);
|
||||
}}
|
||||
/>
|
||||
),
|
||||
checked,
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
const renderSelectionCell: ColumnType<RecordType>['customRender'] = ({ record, index }) => {
|
||||
const { node, checked } = renderCell({ record, index });
|
||||
|
||||
if (customizeRenderCell) {
|
||||
return customizeRenderCell(checked, record, index, node);
|
||||
}
|
||||
|
||||
return node;
|
||||
};
|
||||
|
||||
// Columns
|
||||
const selectionColumn = {
|
||||
width: selectionColWidth,
|
||||
className: `${prefixCls.value}-selection-column`,
|
||||
title: mergedRowSelection.value.columnTitle || title,
|
||||
customRender: renderSelectionCell,
|
||||
[INTERNAL_COL_DEFINE]: {
|
||||
class: `${prefixCls.value}-selection-col`,
|
||||
},
|
||||
};
|
||||
|
||||
if (expandType.value === 'row' && columns.length && !expandIconColumnIndex.value) {
|
||||
const [expandColumn, ...restColumns] = columns;
|
||||
const selectionFixed = fixed || getFixedType(restColumns[0]);
|
||||
if (selectionFixed) {
|
||||
expandColumn.fixed = selectionFixed;
|
||||
}
|
||||
return [expandColumn, { ...selectionColumn, fixed: selectionFixed }, ...restColumns];
|
||||
}
|
||||
return [{ ...selectionColumn, fixed: fixed || getFixedType(columns[0]) }, ...columns];
|
||||
};
|
||||
|
||||
return [transformColumns, derivedSelectedKeySet];
|
||||
}
|
|
@ -0,0 +1,428 @@
|
|||
import CaretDownOutlined from '@ant-design/icons-vue/CaretDownOutlined';
|
||||
import CaretUpOutlined from '@ant-design/icons-vue/CaretUpOutlined';
|
||||
import type {
|
||||
TransformColumns,
|
||||
ColumnsType,
|
||||
Key,
|
||||
ColumnType,
|
||||
SortOrder,
|
||||
CompareFn,
|
||||
ColumnTitleProps,
|
||||
SorterResult,
|
||||
ColumnGroupType,
|
||||
TableLocale,
|
||||
} from '../interface';
|
||||
import type { TooltipProps } from '../../tooltip';
|
||||
import Tooltip from '../../tooltip';
|
||||
import { getColumnKey, getColumnPos, renderColumnTitle } from '../util';
|
||||
import classNames from '../../_util/classNames';
|
||||
import type { Ref } from 'vue';
|
||||
import { computed } from 'vue';
|
||||
import useState from '../../_util/hooks/useState';
|
||||
import type { DefaultRecordType } from '../../vc-table/interface';
|
||||
|
||||
const ASCEND = 'ascend';
|
||||
const DESCEND = 'descend';
|
||||
|
||||
function getMultiplePriority<RecordType>(column: ColumnType<RecordType>): number | false {
|
||||
if (typeof column.sorter === 'object' && typeof column.sorter.multiple === 'number') {
|
||||
return column.sorter.multiple;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
function getSortFunction<RecordType>(
|
||||
sorter: ColumnType<RecordType>['sorter'],
|
||||
): CompareFn<RecordType> | false {
|
||||
if (typeof sorter === 'function') {
|
||||
return sorter;
|
||||
}
|
||||
if (sorter && typeof sorter === 'object' && sorter.compare) {
|
||||
return sorter.compare;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
function nextSortDirection(sortDirections: SortOrder[], current: SortOrder | null) {
|
||||
if (!current) {
|
||||
return sortDirections[0];
|
||||
}
|
||||
|
||||
return sortDirections[sortDirections.indexOf(current) + 1];
|
||||
}
|
||||
|
||||
export interface SortState<RecordType = DefaultRecordType> {
|
||||
column: ColumnType<RecordType>;
|
||||
key: Key;
|
||||
sortOrder: SortOrder | null;
|
||||
multiplePriority: number | false;
|
||||
}
|
||||
|
||||
function collectSortStates<RecordType>(
|
||||
columns: ColumnsType<RecordType>,
|
||||
init: boolean,
|
||||
pos?: string,
|
||||
): SortState<RecordType>[] {
|
||||
let sortStates: SortState<RecordType>[] = [];
|
||||
|
||||
function pushState(column: ColumnsType<RecordType>[number], columnPos: string) {
|
||||
sortStates.push({
|
||||
column,
|
||||
key: getColumnKey(column, columnPos),
|
||||
multiplePriority: getMultiplePriority(column),
|
||||
sortOrder: column.sortOrder!,
|
||||
});
|
||||
}
|
||||
|
||||
(columns || []).forEach((column, index) => {
|
||||
const columnPos = getColumnPos(index, pos);
|
||||
|
||||
if ((column as ColumnGroupType<RecordType>).children) {
|
||||
if ('sortOrder' in column) {
|
||||
// Controlled
|
||||
pushState(column, columnPos);
|
||||
}
|
||||
sortStates = [
|
||||
...sortStates,
|
||||
...collectSortStates((column as ColumnGroupType<RecordType>).children, init, columnPos),
|
||||
];
|
||||
} else if (column.sorter) {
|
||||
if ('sortOrder' in column) {
|
||||
// Controlled
|
||||
pushState(column, columnPos);
|
||||
} else if (init && column.defaultSortOrder) {
|
||||
// Default sorter
|
||||
sortStates.push({
|
||||
column,
|
||||
key: getColumnKey(column, columnPos),
|
||||
multiplePriority: getMultiplePriority(column),
|
||||
sortOrder: column.defaultSortOrder!,
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
return sortStates;
|
||||
}
|
||||
|
||||
function injectSorter<RecordType>(
|
||||
prefixCls: string,
|
||||
columns: ColumnsType<RecordType>,
|
||||
sorterSates: SortState<RecordType>[],
|
||||
triggerSorter: (sorterSates: SortState<RecordType>) => void,
|
||||
defaultSortDirections: SortOrder[],
|
||||
tableLocale?: TableLocale,
|
||||
tableShowSorterTooltip?: boolean | TooltipProps,
|
||||
pos?: string,
|
||||
): ColumnsType<RecordType> {
|
||||
return (columns || []).map((column, index) => {
|
||||
const columnPos = getColumnPos(index, pos);
|
||||
let newColumn: ColumnsType<RecordType>[number] = column;
|
||||
|
||||
if (newColumn.sorter) {
|
||||
const sortDirections: SortOrder[] = newColumn.sortDirections || defaultSortDirections;
|
||||
const showSorterTooltip =
|
||||
newColumn.showSorterTooltip === undefined
|
||||
? tableShowSorterTooltip
|
||||
: newColumn.showSorterTooltip;
|
||||
const columnKey = getColumnKey(newColumn, columnPos);
|
||||
const sorterState = sorterSates.find(({ key }) => key === columnKey);
|
||||
const sorterOrder = sorterState ? sorterState.sortOrder : null;
|
||||
const nextSortOrder = nextSortDirection(sortDirections, sorterOrder);
|
||||
const upNode = sortDirections.includes(ASCEND) && (
|
||||
<CaretUpOutlined
|
||||
class={classNames(`${prefixCls}-column-sorter-up`, {
|
||||
active: sorterOrder === ASCEND,
|
||||
})}
|
||||
/>
|
||||
);
|
||||
const downNode = sortDirections.includes(DESCEND) && (
|
||||
<CaretDownOutlined
|
||||
class={classNames(`${prefixCls}-column-sorter-down`, {
|
||||
active: sorterOrder === DESCEND,
|
||||
})}
|
||||
/>
|
||||
);
|
||||
const { cancelSort, triggerAsc, triggerDesc } = tableLocale || {};
|
||||
let sortTip: string | undefined = cancelSort;
|
||||
if (nextSortOrder === DESCEND) {
|
||||
sortTip = triggerDesc;
|
||||
} else if (nextSortOrder === ASCEND) {
|
||||
sortTip = triggerAsc;
|
||||
}
|
||||
const tooltipProps: TooltipProps =
|
||||
typeof showSorterTooltip === 'object' ? showSorterTooltip : { title: sortTip };
|
||||
newColumn = {
|
||||
...newColumn,
|
||||
className: classNames(newColumn.className, { [`${prefixCls}-column-sort`]: sorterOrder }),
|
||||
title: (renderProps: ColumnTitleProps<RecordType>) => {
|
||||
const renderSortTitle = (
|
||||
<div class={`${prefixCls}-column-sorters`}>
|
||||
<span class={`${prefixCls}-column-title`}>
|
||||
{renderColumnTitle(column.title, renderProps)}
|
||||
</span>
|
||||
<span
|
||||
class={classNames(`${prefixCls}-column-sorter`, {
|
||||
[`${prefixCls}-column-sorter-full`]: !!(upNode && downNode),
|
||||
})}
|
||||
>
|
||||
<span class={`${prefixCls}-column-sorter-inner`}>
|
||||
{upNode}
|
||||
{downNode}
|
||||
</span>
|
||||
</span>
|
||||
</div>
|
||||
);
|
||||
return showSorterTooltip ? (
|
||||
<Tooltip {...tooltipProps}>{renderSortTitle}</Tooltip>
|
||||
) : (
|
||||
renderSortTitle
|
||||
);
|
||||
},
|
||||
customHeaderCell: col => {
|
||||
const cell = (column.customHeaderCell && column.customHeaderCell(col)) || {};
|
||||
const originOnClick = cell.onClick;
|
||||
cell.onClick = (event: MouseEvent) => {
|
||||
triggerSorter({
|
||||
column,
|
||||
key: columnKey,
|
||||
sortOrder: nextSortOrder,
|
||||
multiplePriority: getMultiplePriority(column),
|
||||
});
|
||||
|
||||
if (originOnClick) {
|
||||
originOnClick(event);
|
||||
}
|
||||
};
|
||||
|
||||
cell.class = classNames(cell.class, `${prefixCls}-column-has-sorters`);
|
||||
|
||||
return cell;
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
if ('children' in newColumn) {
|
||||
newColumn = {
|
||||
...newColumn,
|
||||
children: injectSorter(
|
||||
prefixCls,
|
||||
newColumn.children,
|
||||
sorterSates,
|
||||
triggerSorter,
|
||||
defaultSortDirections,
|
||||
tableLocale,
|
||||
tableShowSorterTooltip,
|
||||
columnPos,
|
||||
),
|
||||
};
|
||||
}
|
||||
|
||||
return newColumn;
|
||||
});
|
||||
}
|
||||
|
||||
function stateToInfo<RecordType>(sorterStates: SortState<RecordType>) {
|
||||
const { column, sortOrder } = sorterStates;
|
||||
return { column, order: sortOrder, field: column.dataIndex, columnKey: column.key };
|
||||
}
|
||||
|
||||
function generateSorterInfo<RecordType>(
|
||||
sorterStates: SortState<RecordType>[],
|
||||
): SorterResult<RecordType> | SorterResult<RecordType>[] {
|
||||
const list = sorterStates.filter(({ sortOrder }) => sortOrder).map(stateToInfo);
|
||||
|
||||
// =========== Legacy compatible support ===========
|
||||
// https://github.com/ant-design/ant-design/pull/19226
|
||||
if (list.length === 0 && sorterStates.length) {
|
||||
return {
|
||||
...stateToInfo(sorterStates[sorterStates.length - 1]),
|
||||
column: undefined,
|
||||
};
|
||||
}
|
||||
|
||||
if (list.length <= 1) {
|
||||
return list[0] || {};
|
||||
}
|
||||
|
||||
return list;
|
||||
}
|
||||
|
||||
export function getSortData<RecordType>(
|
||||
data: readonly RecordType[],
|
||||
sortStates: SortState<RecordType>[],
|
||||
childrenColumnName: string,
|
||||
): RecordType[] {
|
||||
const innerSorterStates = sortStates
|
||||
.slice()
|
||||
.sort((a, b) => (b.multiplePriority as number) - (a.multiplePriority as number));
|
||||
|
||||
const cloneData = data.slice();
|
||||
|
||||
const runningSorters = innerSorterStates.filter(
|
||||
({ column: { sorter }, sortOrder }) => getSortFunction(sorter) && sortOrder,
|
||||
);
|
||||
|
||||
// Skip if no sorter needed
|
||||
if (!runningSorters.length) {
|
||||
return cloneData;
|
||||
}
|
||||
|
||||
return cloneData
|
||||
.sort((record1, record2) => {
|
||||
for (let i = 0; i < runningSorters.length; i += 1) {
|
||||
const sorterState = runningSorters[i];
|
||||
const {
|
||||
column: { sorter },
|
||||
sortOrder,
|
||||
} = sorterState;
|
||||
|
||||
const compareFn = getSortFunction(sorter);
|
||||
|
||||
if (compareFn && sortOrder) {
|
||||
const compareResult = compareFn(record1, record2, sortOrder);
|
||||
|
||||
if (compareResult !== 0) {
|
||||
return sortOrder === ASCEND ? compareResult : -compareResult;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
})
|
||||
.map<RecordType>(record => {
|
||||
const subRecords = (record as any)[childrenColumnName];
|
||||
if (subRecords) {
|
||||
return {
|
||||
...record,
|
||||
[childrenColumnName]: getSortData(subRecords, sortStates, childrenColumnName),
|
||||
};
|
||||
}
|
||||
return record;
|
||||
});
|
||||
}
|
||||
|
||||
interface SorterConfig<RecordType> {
|
||||
prefixCls: Ref<string>;
|
||||
mergedColumns: Ref<ColumnsType<RecordType>>;
|
||||
onSorterChange: (
|
||||
sorterResult: SorterResult<RecordType> | SorterResult<RecordType>[],
|
||||
sortStates: SortState<RecordType>[],
|
||||
) => void;
|
||||
sortDirections: Ref<SortOrder[]>;
|
||||
tableLocale?: Ref<TableLocale>;
|
||||
showSorterTooltip?: Ref<boolean | TooltipProps>;
|
||||
}
|
||||
|
||||
export default function useFilterSorter<RecordType>({
|
||||
prefixCls,
|
||||
mergedColumns,
|
||||
onSorterChange,
|
||||
sortDirections,
|
||||
tableLocale,
|
||||
showSorterTooltip,
|
||||
}: SorterConfig<RecordType>): [
|
||||
TransformColumns<RecordType>,
|
||||
Ref<SortState<RecordType>[]>,
|
||||
Ref<ColumnTitleProps<RecordType>>,
|
||||
Ref<SorterResult<RecordType> | SorterResult<RecordType>[]>,
|
||||
] {
|
||||
const [sortStates, setSortStates] = useState<SortState<RecordType>[]>(
|
||||
collectSortStates(mergedColumns.value, true),
|
||||
);
|
||||
|
||||
const mergedSorterStates = computed(() => {
|
||||
let validate = true;
|
||||
const collectedStates = collectSortStates(mergedColumns.value, false);
|
||||
|
||||
// Return if not controlled
|
||||
if (!collectedStates.length) {
|
||||
return sortStates.value;
|
||||
}
|
||||
|
||||
const validateStates: SortState<RecordType>[] = [];
|
||||
|
||||
function patchStates(state: SortState<RecordType>) {
|
||||
if (validate) {
|
||||
validateStates.push(state);
|
||||
} else {
|
||||
validateStates.push({
|
||||
...state,
|
||||
sortOrder: null,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
let multipleMode: boolean | null = null;
|
||||
collectedStates.forEach(state => {
|
||||
if (multipleMode === null) {
|
||||
patchStates(state);
|
||||
|
||||
if (state.sortOrder) {
|
||||
if (state.multiplePriority === false) {
|
||||
validate = false;
|
||||
} else {
|
||||
multipleMode = true;
|
||||
}
|
||||
}
|
||||
} else if (multipleMode && state.multiplePriority !== false) {
|
||||
patchStates(state);
|
||||
} else {
|
||||
validate = false;
|
||||
patchStates(state);
|
||||
}
|
||||
});
|
||||
|
||||
return validateStates;
|
||||
});
|
||||
|
||||
// Get render columns title required props
|
||||
const columnTitleSorterProps = computed<ColumnTitleProps<RecordType>>(() => {
|
||||
const sortColumns = mergedSorterStates.value.map(({ column, sortOrder }) => ({
|
||||
column,
|
||||
order: sortOrder,
|
||||
}));
|
||||
|
||||
return {
|
||||
sortColumns,
|
||||
// Legacy
|
||||
sortColumn: sortColumns[0] && sortColumns[0].column,
|
||||
sortOrder: (sortColumns[0] && sortColumns[0].order) as SortOrder,
|
||||
};
|
||||
});
|
||||
|
||||
function triggerSorter(sortState: SortState<RecordType>) {
|
||||
let newSorterStates;
|
||||
|
||||
if (
|
||||
sortState.multiplePriority === false ||
|
||||
!mergedSorterStates.value.length ||
|
||||
mergedSorterStates.value[0].multiplePriority === false
|
||||
) {
|
||||
newSorterStates = [sortState];
|
||||
} else {
|
||||
newSorterStates = [
|
||||
...mergedSorterStates.value.filter(({ key }) => key !== sortState.key),
|
||||
sortState,
|
||||
];
|
||||
}
|
||||
|
||||
setSortStates(newSorterStates);
|
||||
onSorterChange(generateSorterInfo(newSorterStates), newSorterStates);
|
||||
}
|
||||
|
||||
const transformColumns = (innerColumns: ColumnsType<RecordType>) =>
|
||||
injectSorter(
|
||||
prefixCls.value,
|
||||
innerColumns,
|
||||
mergedSorterStates.value,
|
||||
triggerSorter,
|
||||
sortDirections.value,
|
||||
tableLocale.value,
|
||||
showSorterTooltip.value,
|
||||
);
|
||||
|
||||
const sorters = computed(() => generateSorterInfo(mergedSorterStates.value));
|
||||
|
||||
return [transformColumns, mergedSorterStates, columnTitleSorterProps, sorters];
|
||||
}
|
|
@ -0,0 +1,29 @@
|
|||
import type { Ref } from 'vue';
|
||||
import type { TransformColumns, ColumnTitleProps, ColumnsType } from '../interface';
|
||||
import { renderColumnTitle } from '../util';
|
||||
|
||||
function fillTitle<RecordType>(
|
||||
columns: ColumnsType<RecordType>,
|
||||
columnTitleProps: ColumnTitleProps<RecordType>,
|
||||
) {
|
||||
return columns.map(column => {
|
||||
const cloneColumn = { ...column };
|
||||
|
||||
cloneColumn.title = renderColumnTitle(cloneColumn.title, columnTitleProps);
|
||||
|
||||
if ('children' in cloneColumn) {
|
||||
cloneColumn.children = fillTitle(cloneColumn.children, columnTitleProps);
|
||||
}
|
||||
|
||||
return cloneColumn;
|
||||
});
|
||||
}
|
||||
|
||||
export default function useTitleColumns<RecordType>(
|
||||
columnTitleProps: Ref<ColumnTitleProps<RecordType>>,
|
||||
): [TransformColumns<RecordType>] {
|
||||
const filledColumns = (columns: ColumnsType<RecordType>) =>
|
||||
fillTitle(columns, columnTitleProps.value);
|
||||
|
||||
return [filledColumns];
|
||||
}
|
|
@ -79,10 +79,11 @@ Specify `dataSource` of Table as an array of data.
|
|||
| defaultExpandedRowKeys | Initial expanded row keys | string\[] | - | |
|
||||
| expandedRowKeys | Current expanded row keys | string\[] | - | |
|
||||
| expandedRowRender | Expanded container render for each row | Function({record, index, indent, expanded}):VNode\|v-slot | - | |
|
||||
| expandIcon | Customize row expand Icon. | Function(props):VNode \| #expandIcon="props" | - | |
|
||||
| expandFixed | Set column to be fixed: `true`(same as left) `'left'` `'right'` | boolean \| string | false | 3.0 |
|
||||
| expandIcon | Customize row expand Icon. | Function(props):VNode \| v-slot:expandIcon="props" | - | |
|
||||
| expandRowByClick | Whether to expand row by clicking anywhere in the whole row | boolean | `false` | |
|
||||
| expandIconColumnIndex | The index of `expandIcon` which column will be inserted when `expandIconAsCell` is false | 0 | |
|
||||
| footer | Table footer renderer | Function(currentPageData)\| v-slot | |
|
||||
| expandIconColumnIndex | Customize expand icon column index. Not render when `-1` | 0 | |
|
||||
| footer | Table footer renderer | Function(currentPageData)\| v-slot:footer="currentPageData" | |
|
||||
| indentSize | Indent size in pixels of tree data | number | 15 | |
|
||||
| loading | Loading status of table | boolean\|[object](/components/spin) | `false` |
|
||||
| locale | i18n text including filter, sort, empty text, etc | object | filterConfirm: 'Ok' <br /> filterReset: 'Reset' <br /> emptyText: 'No Data' | |
|
||||
|
@ -90,14 +91,27 @@ Specify `dataSource` of Table as an array of data.
|
|||
| rowClassName | Row's className | Function(record, index):string | - | |
|
||||
| rowKey | Row's unique key, could be a string or function that returns a string | string\|Function(record, index):string | `key` | |
|
||||
| rowSelection | Row selection [config](#rowSelection) | object | null | |
|
||||
| scroll | Set horizontal or vertical scrolling, can also be used to specify the width and height of the scroll area. It is recommended to set a number for `x`, if you want to set it to `true`, you need to add style `.ant-table td { white-space: nowrap; }`. | { x: number \| true, y: number } | - | |
|
||||
| scroll | Whether the table can be scrollable, [config](#scroll) | object | - | |
|
||||
| showHeader | Whether to show table header | boolean | `true` | |
|
||||
| sortDirections | Supported sort way, could be `ascend`, `descend` | Array | \[`ascend`, `descend`] | 3.0 |
|
||||
| showSorterTooltip | The header show next sorter direction tooltip. It will be set as the property of Tooltip if its type is object | boolean \| [Tooltip props](/components/tooltip/#API) | true | 3.0 |
|
||||
| size | Size of table | `default` \| `middle` \| `small` \| `large` | `default` |
|
||||
| title | Table title renderer | Function(currentPageData)\| v-slot | | |
|
||||
| sticky | Set sticky header and scroll bar | boolean \| `{offsetHeader?: number, offsetScroll?: number, getContainer?: () => HTMLElement}` | - | 3.0 |
|
||||
| title | Table title renderer | Function(currentPageData)\| v-slot:title="currentPageData" | | |
|
||||
| customHeaderRow | Set props on per header row | Function(column, index) | - | |
|
||||
| customRow | Set props on per row | Function(record, index) | - | |
|
||||
| getPopupContainer | the render container of dropdowns in table | (triggerNode) => HTMLElement | `() => TableHtmlElement` | 1.5.0 |
|
||||
| transformCellText | Data can be changed again before rendering. The default configuration of general user empty data. You can configured globally through [ConfigProvider](/components/config-provider-cn/) | Function({ text, column, record, index }) => any | - | 1.5.4 | |
|
||||
| headerCell | custom head cell by slot | v-slot:headerCell="{title, column}" | - | 3.0 |
|
||||
| bodyCell | custom body cell by slot | v-slot:bodyCell="{text, record, index, column}" | - | 3.0 |
|
||||
| customFilterDropdown | Customized filter overlay,need set `column.customFilterDropdown` | v-slot:customFilterDropdown="[FilterDropdownProps](#FilterDropdownProps)" | - | 3.0 |
|
||||
| customFilterIcon | Customized filter icon | v-slot:customFilterIcon="{filtered, column}" | - | 3.0 |
|
||||
| emptyText | Customize the display content when empty data | v-slot:emptyText | - | 3.0 |
|
||||
| summary | Summary content | v-slot:summary | - | 3.0 |
|
||||
| transformCellText | The data can be changed again before rendering, generally used for the default configuration of empty data. You can configured globally through [ConfigProvider](/components/config-provider-cn/) | Function({ text, column, record, index }) => any, The `text` here is the data processed by other defined cell api, and it may be of type VNode \| string \| number | - | 1.5.4 | |
|
||||
|
||||
- `expandFixed`
|
||||
- When set to true or `left` and `expandIconColumnIndex` is not set or is 0, enable fixed
|
||||
- When set to true or `right` and `expandIconColumnIndex` is set to the number of table columns, enable fixed
|
||||
|
||||
### Events
|
||||
|
||||
|
@ -140,29 +154,37 @@ One of the Table `columns` prop for describing the table's columns, Column has t
|
|||
| align | specify how content is aligned | 'left' \| 'right' \| 'center' | 'left' | |
|
||||
| ellipsis | ellipsize cell content, not working with sorter and filters for now.<br />tableLayout would be `fixed` when `ellipsis` is true. | boolean | false | 1.5.0 |
|
||||
| colSpan | Span of this column's title | number | | |
|
||||
| dataIndex | Display field of the data record, could be set like `a.b.c` | string | - | |
|
||||
| dataIndex | Display field of the data record, support nest path by string array | string \| string\[] | - | |
|
||||
| defaultFilteredValue | Default filtered values | string\[] | - | 1.5.0 |
|
||||
| defaultSortOrder | Default order of sorted values: `'ascend'` `'descend'` `null` | string | - | |
|
||||
| filterDropdown | Customized filter overlay | slot | - | |
|
||||
| ellipsis | The ellipsis cell content, not working with sorter and filters for now.<br />tableLayout would be `fixed` when `ellipsis` is `true` or `{ showTitle?: boolean }` | boolean \| {showTitle?: boolean } | false | 3.0 |
|
||||
| filterDropdown | Customized filter overlay | VNode | - | |
|
||||
| customFilterDropdown | use v-slot:customFilterDropdown,Priority is lower than filterDropdown | boolean | false | 3.0 |
|
||||
| filterDropdownVisible | Whether `filterDropdown` is visible | boolean | - | |
|
||||
| filtered | Whether the `dataSource` is filtered | boolean | `false` | |
|
||||
| filteredValue | Controlled filtered value, filter icon will highlight | string\[] | - | |
|
||||
| filterIcon | Customized filter icon | slot \| ({filtered: boolean, column: Column}) | `false` | |
|
||||
| filterIcon | Customized filter icon | ({filtered: boolean, column: Column}) | `false` | |
|
||||
| filterMultiple | Whether multiple filters can be selected | boolean | `true` | |
|
||||
| filters | Filter menu config | object\[] | - | |
|
||||
| fixed | Set column to be fixed: `true`(same as left) `'left'` `'right'` | boolean\|string | `false` | |
|
||||
| key | Unique key of this column, you can ignore this prop if you've set a unique `dataIndex` | string | - | |
|
||||
| customRender | Renderer of the table cell. The return value should be a VNode, or an object for colSpan/rowSpan config | Function({text, record, index}) {}\|v-slot | - | |
|
||||
| customRender | Renderer of the table cell. The return value should be a VNode, or an object for colSpan/rowSpan config | Function({text, record, index}) {} | - | |
|
||||
| responsive | The list of breakpoints at which to display this column. Always visible if not set. | [Breakpoint](#Breakpoint)\[] | - | 3.0 |
|
||||
| sorter | Sort function for local sort, see [Array.sort](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/sort)'s compareFunction. If you need sort buttons only, set to `true` | Function\|boolean | - | |
|
||||
| sortOrder | Order of sorted values: `'ascend'` `'descend'` `false` | boolean\|string | - | |
|
||||
| sortDirections | supported sort way, could be `'ascend'`, `'descend'` | Array | `['ascend', 'descend']` | 1.5.0 |
|
||||
| title | Title of this column | string\|slot | - | |
|
||||
| title | Title of this column | string | - | |
|
||||
| width | Width of this column | string\|number | - | |
|
||||
| customCell | Set props on per cell | Function(record, rowIndex) | - | |
|
||||
| customHeaderCell | Set props on per header cell | Function(column) | - | |
|
||||
| onFilter | Callback executed when the confirm filter button is clicked, Use as a `filter` event when using template or jsx | Function | - | |
|
||||
| onFilterDropdownVisibleChange | Callback executed when `filterDropdownVisible` is changed, Use as a `filterDropdownVisible` event when using template or jsx | function(visible) {} | - | |
|
||||
| slots | When using columns, you can use this property to configure the properties that support the slot, such as `slots: { filterIcon: 'XXX'}` | object | - | |
|
||||
|
||||
#### Breakpoint
|
||||
|
||||
```ts
|
||||
type Breakpoint = 'xxl' | 'xl' | 'lg' | 'md' | 'sm' | 'xs';
|
||||
```
|
||||
|
||||
### ColumnGroup
|
||||
|
||||
|
@ -175,9 +197,9 @@ One of the Table `columns` prop for describing the table's columns, Column has t
|
|||
|
||||
Properties for pagination.
|
||||
|
||||
| Property | Description | Type | Default |
|
||||
| -------- | ------------------------------------ | --------------------------- | -------- |
|
||||
| position | specify the position of `Pagination` | 'top' \| 'bottom' \| 'both' | 'bottom' |
|
||||
| Property | Description | Type | Default |
|
||||
| --- | --- | --- | --- |
|
||||
| position | Specify the position of `Pagination`, could be`topLeft` \| `topCenter` \| `topRight` \|`bottomLeft` \| `bottomCenter` \| `bottomRight` | Array | \[`bottomRight`] |
|
||||
|
||||
More about pagination, please check [`Pagination`](/components/pagination/).
|
||||
|
||||
|
@ -185,28 +207,32 @@ More about pagination, please check [`Pagination`](/components/pagination/).
|
|||
|
||||
Properties for row selection.
|
||||
|
||||
| Property | Description | Type | Default |
|
||||
| --- | --- | --- | --- |
|
||||
| columnWidth | Set the width of the selection column | string\|number | - |
|
||||
| columnTitle | Set the title of the selection column | string\|VNode | - |
|
||||
| fixed | Fixed selection column on the left | boolean | - |
|
||||
| getCheckboxProps | Get Checkbox or Radio props | Function(record) | - |
|
||||
| hideDefaultSelections | Remove the default `Select All` and `Select Invert` selections | boolean | `false` |
|
||||
| selectedRowKeys | Controlled selected row keys | string\[] | \[] |
|
||||
| selections | Custom selection config, only displays default selections when set to `true` | object\[]\|boolean | - |
|
||||
| type | `checkbox` or `radio` | `checkbox` \| `radio` | `checkbox` |
|
||||
| onChange | Callback executed when selected rows change | Function(selectedRowKeys, selectedRows) | - |
|
||||
| onSelect | Callback executed when select/deselect one row | Function(record, selected, selectedRows, nativeEvent) | - |
|
||||
| onSelectAll | Callback executed when select/deselect all rows | Function(selected, selectedRows, changeRows) | - |
|
||||
| onSelectInvert | Callback executed when row selection is inverted | Function(selectedRows) | - |
|
||||
| Property | Description | Type | Default | Version |
|
||||
| --- | --- | --- | --- | --- |
|
||||
| checkStrictly | Check table row precisely; parent row and children rows are not associated | boolean | true | 3.0 |
|
||||
| columnWidth | Set the width of the selection column | string\|number | - | |
|
||||
| columnTitle | Set the title of the selection column | string\|VNode | - | |
|
||||
| fixed | Fixed selection column on the left | boolean | - | |
|
||||
| getCheckboxProps | Get Checkbox or Radio props | Function(record) | - | |
|
||||
| hideSelectAll | Hide the selectAll checkbox and custom selection | boolean | false | 3.0 |
|
||||
| preserveSelectedRowKeys | Keep selection `key` even when it removed from `dataSource` | boolean | - | 3.0 |
|
||||
| hideDefaultSelections | Remove the default `Select All` and `Select Invert` selections | boolean | `false` | |
|
||||
| selectedRowKeys | Controlled selected row keys | string\[] | \[] | |
|
||||
| selections | Custom selection [config](#rowSelection), only displays default selections when set to `true` | object\[] \| boolean | - | |
|
||||
| type | `checkbox` or `radio` | `checkbox` \| `radio` | `checkbox` | |
|
||||
| onChange | Callback executed when selected rows change | Function(selectedRowKeys, selectedRows) | - | |
|
||||
| onSelect | Callback executed when select/deselect one row | Function(record, selected, selectedRows, nativeEvent) | - | |
|
||||
| onSelectAll | Callback executed when select/deselect all rows | Function(selected, selectedRows, changeRows) | - | |
|
||||
| onSelectInvert | Callback executed when row selection is inverted | Function(selectedRows) | - | |
|
||||
| onSelectNone | Callback executed when row selection is cleared | function() | - | 3.0 |
|
||||
|
||||
### scroll
|
||||
|
||||
| Property | Description | Type | Default | Version |
|
||||
| --- | --- | --- | --- | --- |
|
||||
| x | Set horizontal scrolling, can also be used to specify the width and height of the scroll area, could be number, percent value, true and ['max-content'](https://developer.mozilla.org/zh-CN/docs/Web/CSS/width#max-content) | number \| true | - | |
|
||||
| y | Set vertical scrolling, can also be used to specify the width and height of the scroll area, could be number, percent value, true and ['max-content'](https://developer.mozilla.org/zh-CN/docs/Web/CSS/width#max-content) | number \| true | - | |
|
||||
| scrollToFirstRowOnChange | Whether to scroll to the top of the table when paging, sorting, filtering changes | boolean | - | 1.5.0 |
|
||||
| Property | Description | Type | Default |
|
||||
| --- | --- | --- | --- |
|
||||
| scrollToFirstRowOnChange | Whether to scroll to the top of the table when paging, sorting, filtering changes | boolean | - |
|
||||
| x | Set horizontal scrolling, can also be used to specify the width of the scroll area, could be number, percent value, true and ['max-content'](https://developer.mozilla.org/zh-CN/docs/Web/CSS/width#max-content) | string \| number \| true | - |
|
||||
| y | Set vertical scrolling, can also be used to specify the height of the scroll area, could be string or number | string \| number | - |
|
||||
|
||||
### selection
|
||||
|
||||
|
@ -218,6 +244,21 @@ Custom selection config
|
|||
| text | Display text of this selection | string\|VNode | - |
|
||||
| onSelect | Callback executed when this selection is clicked | Function(changeableRowKeys) | - |
|
||||
|
||||
### FilterDropdownProps
|
||||
|
||||
```ts
|
||||
interface FilterDropdownProps {
|
||||
prefixCls: string;
|
||||
setSelectedKeys: (selectedKeys: Key[]) => void;
|
||||
selectedKeys: Key[];
|
||||
confirm: (param?: FilterConfirmProps) => void;
|
||||
clearFilters?: () => void;
|
||||
filters?: ColumnFilterItem[];
|
||||
visible: boolean;
|
||||
column: ColumnType;
|
||||
}
|
||||
```
|
||||
|
||||
## Note
|
||||
|
||||
The values inside `dataSource` and `columns` should follow this in Table, and `dataSource[i].key` would be treated as key value default for `dataSource`.
|
||||
|
@ -230,3 +271,9 @@ return <Table rowKey="uid" />;
|
|||
// or
|
||||
return <Table rowKey={record => record.uid} />;
|
||||
```
|
||||
|
||||
## Migrate to v3
|
||||
|
||||
Table deprecated `column.slots`, added `v-slot:bodyCell`, `v-slot:headerCell`, custom cells, and added `column.customFilterDropdown` `v-slot:customFilterDropdown`, custom filtering Menu, added `v-slot:customFilterIcon` custom filter button, but `column.slots` is still available, we will remove it in the next major version.
|
||||
|
||||
Besides, the breaking change is changing `dataIndex` from nest string path like `user.age` to string array path like `['user', 'age']`. This help to resolve developer should additional work on the field which contains `.`.
|
||||
|
|
|
@ -1,111 +1,68 @@
|
|||
import type { App, Plugin } from 'vue';
|
||||
import Table, { tableProps } from './Table';
|
||||
import Column from './Column';
|
||||
import ColumnGroup from './ColumnGroup';
|
||||
import type { TableProps, TablePaginationConfig } from './Table';
|
||||
import { defineComponent } from 'vue';
|
||||
import T, { defaultTableProps } from './Table';
|
||||
import type Column from './Column';
|
||||
import type ColumnGroup from './ColumnGroup';
|
||||
import {
|
||||
getOptionProps,
|
||||
getKey,
|
||||
getPropsData,
|
||||
getSlot,
|
||||
flattenChildren,
|
||||
} from '../_util/props-util';
|
||||
import type { App } from 'vue';
|
||||
import { Summary, SummaryCell, SummaryRow } from '../vc-table';
|
||||
import { SELECTION_ALL, SELECTION_INVERT, SELECTION_NONE } from './hooks/useSelection';
|
||||
|
||||
const Table = defineComponent({
|
||||
name: 'ATable',
|
||||
Column: T.Column,
|
||||
ColumnGroup: T.ColumnGroup,
|
||||
inheritAttrs: false,
|
||||
props: defaultTableProps,
|
||||
methods: {
|
||||
normalize(elements = []) {
|
||||
const flattenElements = flattenChildren(elements);
|
||||
const columns = [];
|
||||
flattenElements.forEach(element => {
|
||||
if (!element) {
|
||||
return;
|
||||
}
|
||||
const key = getKey(element);
|
||||
const style = element.props?.style || {};
|
||||
const cls = element.props?.class || '';
|
||||
const props = getPropsData(element);
|
||||
const { default: children, ...restSlots } = element.children || {};
|
||||
const column = { ...restSlots, ...props, style, class: cls };
|
||||
if (key) {
|
||||
column.key = key;
|
||||
}
|
||||
if (element.type?.__ANT_TABLE_COLUMN_GROUP) {
|
||||
column.children = this.normalize(typeof children === 'function' ? children() : children);
|
||||
} else {
|
||||
const customRender = element.children?.default;
|
||||
column.customRender = column.customRender || customRender;
|
||||
}
|
||||
columns.push(column);
|
||||
});
|
||||
return columns;
|
||||
},
|
||||
updateColumns(cols = []) {
|
||||
const columns = [];
|
||||
const { $slots } = this;
|
||||
cols.forEach(col => {
|
||||
const { slots = {}, ...restProps } = col;
|
||||
const column = {
|
||||
...restProps,
|
||||
};
|
||||
Object.keys(slots).forEach(key => {
|
||||
const name = slots[key];
|
||||
if (column[key] === undefined && $slots[name]) {
|
||||
column[key] = $slots[name];
|
||||
}
|
||||
});
|
||||
// if (slotScopeName && $scopedSlots[slotScopeName]) {
|
||||
// column.customRender = column.customRender || $scopedSlots[slotScopeName]
|
||||
// }
|
||||
if (col.children) {
|
||||
column.children = this.updateColumns(column.children);
|
||||
}
|
||||
columns.push(column);
|
||||
});
|
||||
return columns;
|
||||
},
|
||||
},
|
||||
render() {
|
||||
const { normalize, $slots } = this;
|
||||
const props: any = { ...getOptionProps(this), ...this.$attrs };
|
||||
const columns = props.columns ? this.updateColumns(props.columns) : normalize(getSlot(this));
|
||||
let { title, footer } = props;
|
||||
const {
|
||||
title: slotTitle,
|
||||
footer: slotFooter,
|
||||
expandedRowRender = props.expandedRowRender,
|
||||
expandIcon,
|
||||
} = $slots;
|
||||
title = title || slotTitle;
|
||||
footer = footer || slotFooter;
|
||||
const tProps = {
|
||||
...props,
|
||||
columns,
|
||||
title,
|
||||
footer,
|
||||
expandedRowRender,
|
||||
expandIcon: this.$props.expandIcon || expandIcon,
|
||||
};
|
||||
return <T {...tProps} ref="table" />;
|
||||
},
|
||||
export type { ColumnProps } from './Column';
|
||||
export type { ColumnsType, ColumnType, ColumnGroupType } from './interface';
|
||||
export type { TableProps, TablePaginationConfig };
|
||||
|
||||
const TableSummaryRow = defineComponent({ ...SummaryRow, name: 'ATableSummaryRow' });
|
||||
const TableSummaryCell = defineComponent({ ...SummaryCell, name: 'ATableSummaryCell' });
|
||||
|
||||
const TempSummary = defineComponent({
|
||||
...Summary,
|
||||
name: 'ATableSummary',
|
||||
});
|
||||
|
||||
const TableSummary = TempSummary as typeof TempSummary & {
|
||||
Cell: typeof TableSummaryCell;
|
||||
Row: typeof TableSummaryRow;
|
||||
};
|
||||
TableSummary.Cell = TableSummaryCell;
|
||||
TableSummary.Row = TableSummaryRow;
|
||||
|
||||
const T = Table as typeof Table &
|
||||
Plugin & {
|
||||
Column: typeof Column;
|
||||
ColumnGroup: typeof ColumnGroup;
|
||||
Summary: typeof TableSummary;
|
||||
SELECTION_ALL: typeof SELECTION_ALL;
|
||||
SELECTION_INVERT: typeof SELECTION_INVERT;
|
||||
SELECTION_NONE: typeof SELECTION_NONE;
|
||||
};
|
||||
|
||||
T.SELECTION_ALL = SELECTION_ALL;
|
||||
T.SELECTION_INVERT = SELECTION_INVERT;
|
||||
T.SELECTION_NONE = SELECTION_NONE;
|
||||
|
||||
T.Column = Column;
|
||||
T.ColumnGroup = ColumnGroup;
|
||||
|
||||
T.Summary = TableSummary;
|
||||
|
||||
/* istanbul ignore next */
|
||||
Table.install = function (app: App) {
|
||||
app.component(Table.name, Table);
|
||||
app.component(Table.Column.name, Table.Column);
|
||||
app.component(Table.ColumnGroup.name, Table.ColumnGroup);
|
||||
T.install = function (app: App) {
|
||||
app.component(TableSummary.name, TableSummary);
|
||||
app.component(TableSummaryCell.name, TableSummaryCell);
|
||||
app.component(TableSummaryRow.name, TableSummaryRow);
|
||||
app.component(T.name, T);
|
||||
app.component(T.Column.name, Column);
|
||||
app.component(T.ColumnGroup.name, ColumnGroup);
|
||||
return app;
|
||||
};
|
||||
|
||||
export const TableColumn = Table.Column;
|
||||
export const TableColumnGroup = Table.ColumnGroup;
|
||||
export {
|
||||
tableProps,
|
||||
TableSummary,
|
||||
TableSummaryRow,
|
||||
TableSummaryCell,
|
||||
Column as TableColumn,
|
||||
ColumnGroup as TableColumnGroup,
|
||||
};
|
||||
|
||||
export default Table as typeof Table &
|
||||
Plugin & {
|
||||
readonly Column: typeof Column;
|
||||
readonly ColumnGroup: typeof ColumnGroup;
|
||||
};
|
||||
export default T;
|
||||
|
|
|
@ -74,35 +74,50 @@ cover: https://gw.alipayobjects.com/zos/alicdn/f-SbcX2Lx/Table.svg
|
|||
|
||||
| 参数 | 说明 | 类型 | 默认值 | 版本 |
|
||||
| --- | --- | --- | --- | --- |
|
||||
| tableLayout | 表格元素的 [table-layout](https://developer.mozilla.org/zh-CN/docs/Web/CSS/table-layout) 属性,设为 `fixed` 表示内容不会影响列的布局 | - \| 'auto' \| 'fixed' | 无<hr />固定表头/列或使用了 `column.ellipsis` 时,默认值为 `fixed` | 1.5.0 |
|
||||
| bordered | 是否展示外边框和列边框 | boolean | false | |
|
||||
| childrenColumnName | 指定树形结构的列名 | string | `children` | |
|
||||
| columns | 表格列的配置描述,具体项见[下表](#Column) | array | - | |
|
||||
| components | 覆盖默认的 table 元素 | object | - | |
|
||||
| dataSource | 数据数组 | any\[] | | |
|
||||
| childrenColumnName | 指定树形结构的列名 | string | `children` | |
|
||||
| dataSource | 数据数组 | object\[] | | |
|
||||
| defaultExpandAllRows | 初始时,是否展开所有行 | boolean | false | |
|
||||
| defaultExpandedRowKeys | 默认展开的行 | string\[] | - | |
|
||||
| expandedRowKeys | 展开的行,控制属性 | string\[] | - | |
|
||||
| expandedRowRender | 额外的展开行 | Function(record, index, indent, expanded):VNode \| #expandedRowRender="{record, index, indent, expanded}" | - | |
|
||||
| expandIcon | 自定义展开图标 | Function(props):VNode \| #expandIcon="props" | - | |
|
||||
| expandedRowRender | 额外的展开行 | Function(record, index, indent, expanded):VNode \| v-slot:expandedRowRender="{record, index, indent, expanded}" | - | |
|
||||
| expandFixed | 控制展开图标是否固定,可选 true `left` `right` | boolean \| string | false | 3.0 |
|
||||
| expandIcon | 自定义展开图标 | Function(props):VNode \| v-slot:expandIcon="props" | - | |
|
||||
| expandRowByClick | 通过点击行来展开子行 | boolean | `false` | |
|
||||
| expandIconColumnIndex | 展开的图标显示在哪一列,如果没有 `rowSelection`,默认显示在第一列,否则显示在选择框后面 | `number` | |
|
||||
| footer | 表格尾部 | Function(currentPageData)\|v-slot | | |
|
||||
| indentSize | 展示树形数据时,每层缩进的宽度,以 px 为单位 | number | 15 | |
|
||||
| expandIconColumnIndex | 自定义展开按钮的列顺序,`-1` 时不展示 | number | - | |
|
||||
| footer | 表格尾部 | Function(currentPageData)\|v-slot:footer="currentPageData" | | |
|
||||
| getPopupContainer | 设置表格内各类浮层的渲染节点,如筛选菜单 | (triggerNode) => HTMLElement | `() => TableHtmlElement` | 1.5.0 |
|
||||
| loading | 页面是否加载中 | boolean\|[object](/components/spin-cn) | false | |
|
||||
| locale | 默认文案设置,目前包括排序、过滤、空数据文案 | object | filterConfirm: '确定' <br /> filterReset: '重置' <br /> emptyText: '暂无数据' | |
|
||||
| locale | 默认文案设置,目前包括排序、过滤、空数据文案 | object | filterConfirm: `确定` <br> filterReset: `重置` <br> emptyText: `暂无数据` | |
|
||||
| pagination | 分页器,参考[配置项](#pagination)或 [pagination](/components/pagination-cn/)文档,设为 false 时不展示和进行分页 | object | | |
|
||||
| rowClassName | 表格行的类名 | Function(record, index):string | - | |
|
||||
| rowKey | 表格行 key 的取值,可以是字符串或一个函数 | string\|Function(record):string | 'key' | |
|
||||
| rowSelection | 列表项是否可选择,[配置项](#rowSelection) | object | null | |
|
||||
| scroll | 设置横向或纵向滚动,也可用于指定滚动区域的宽和高,建议为 `x` 设置一个数字,如果要设置为 `true`,需要配合样式 `.ant-table td { white-space: nowrap; }` | { x: number \| true, y: number } | - | |
|
||||
| scroll | 表格是否可滚动,也可以指定滚动区域的宽、高,[配置项](#scroll) | object | - | |
|
||||
| showHeader | 是否显示表头 | boolean | true | |
|
||||
| showSorterTooltip | 表头是否显示下一次排序的 tooltip 提示。当参数类型为对象时,将被设置为 Tooltip 的属性 | boolean \| [Tooltip props](/components/tooltip/) | true | 3.0 |
|
||||
| size | 表格大小 | default \| middle \| small | default | |
|
||||
| title | 表格标题 | Function(currentPageData)\|v-slot | | |
|
||||
| sortDirections | 支持的排序方式,取值为 `ascend` `descend` | Array | \[`ascend`, `descend`] | |
|
||||
| sticky | 设置粘性头部和滚动条 | boolean \| `{offsetHeader?: number, offsetScroll?: number, getContainer?: () => HTMLElement}` | - | 3.0 |
|
||||
| tableLayout | 表格元素的 [table-layout](https://developer.mozilla.org/zh-CN/docs/Web/CSS/table-layout) 属性,设为 `fixed` 表示内容不会影响列的布局 | - \| 'auto' \| 'fixed' | 无<hr />固定表头/列或使用了 `column.ellipsis` 时,默认值为 `fixed` | 1.5.0 |
|
||||
| title | 表格标题 | Function(currentPageData)\|v-slot:title="currentPageData" | | |
|
||||
| indentSize | 展示树形数据时,每层缩进的宽度,以 px 为单位 | number | 15 | |
|
||||
| rowExpandable | 设置是否允许行展开 | (record) => boolean | - | 3.0 |
|
||||
| customHeaderRow | 设置头部行属性 | Function(column, index) | - | |
|
||||
| customRow | 设置行属性 | Function(record, index) | - | |
|
||||
| getPopupContainer | 设置表格内各类浮层的渲染节点,如筛选菜单 | (triggerNode) => HTMLElement | `() => TableHtmlElement` | 1.5.0 |
|
||||
| transformCellText | 数据渲染前可以再次改变,一般用户空数据的默认配置,可以通过 [ConfigProvider](/components/config-provider-cn/) 全局统一配置 | Function({ text, column, record, index }) => any | - | 1.5.4 |
|
||||
| headerCell | 个性化头部单元格 | v-slot:headerCell="{title, column}" | - | 3.0 |
|
||||
| bodyCell | 个性化单元格 | v-slot:bodyCell="{text, record, index, column}" | - | 3.0 |
|
||||
| customFilterDropdown | 自定义筛选菜单,需要配合 `column.customFilterDropdown` 使用 | v-slot:customFilterDropdown="[FilterDropdownProps](#FilterDropdownProps)" | - | 3.0 |
|
||||
| customFilterIcon | 自定义筛选图标 | v-slot:customFilterIcon="{filtered, column}" | - | 3.0 |
|
||||
| emptyText | 自定义空数据时的显示内容 | v-slot:emptyText | - | 3.0 |
|
||||
| summary | 总结栏 | v-slot:summary | - | 3.0 |
|
||||
| transformCellText | 数据渲染前可以再次改变,一般用于空数据的默认配置,可以通过 [ConfigProvider](/components/config-provider-cn/) 全局统一配置 | Function({ text, column, record, index }) => any,此处的 text 是经过其它定义单元格 api 处理后的数据,有可能是 VNode \| string \| number 类型 | - | 1.5.4 |
|
||||
|
||||
- `expandFixed`
|
||||
- 当设置为 true 或 `left` 且 `expandIconColumnIndex` 未设置或为 0 时,开启固定
|
||||
- 当设置为 true 或 `right` 且 `expandIconColumnIndex` 设置为表格列数时,开启固定
|
||||
|
||||
### 事件
|
||||
|
||||
|
@ -142,46 +157,54 @@ cover: https://gw.alipayobjects.com/zos/alicdn/f-SbcX2Lx/Table.svg
|
|||
|
||||
| 参数 | 说明 | 类型 | 默认值 | 版本 |
|
||||
| --- | --- | --- | --- | --- |
|
||||
| align | 设置列内容的对齐方式 | 'left' \| 'right' \| 'center' | 'left' | |
|
||||
| ellipsis | 超过宽度将自动省略,暂不支持和排序筛选一起使用。<br />设置为 `true` 时,表格布局将变成 `tableLayout="fixed"`。 | boolean | false | 1.5.0 |
|
||||
| align | 设置列的对齐方式 | `left` \| `right` \| `center` | `left` | |
|
||||
| colSpan | 表头列合并,设置为 0 时,不渲染 | number | | |
|
||||
| dataIndex | 列数据在数据项中对应的 key,支持 `a.b.c` 的嵌套写法 | string | - | |
|
||||
| dataIndex | 列数据在数据项中对应的路径,支持通过数组查询嵌套路径 | string \| string\[] | - | |
|
||||
| defaultFilteredValue | 默认筛选值 | string\[] | - | 1.5.0 |
|
||||
| filterDropdown | 可以自定义筛选菜单,此函数只负责渲染图层,需要自行编写各种交互 | VNode \| v-slot | - | |
|
||||
| defaultSortOrder | 默认排序顺序 | `ascend` \| `descend` | - | |
|
||||
| ellipsis | 超过宽度将自动省略,暂不支持和排序筛选一起使用。<br />设置为 `true` 或 `{ showTitle?: boolean }` 时,表格布局将变成 `tableLayout="fixed"`。 | boolean \| { showTitle?: boolean } | false | 3.0 |
|
||||
| filterDropdown | 可以自定义筛选菜单,此函数只负责渲染图层,需要自行编写各种交互 | VNode | - | |
|
||||
| customFilterDropdown | 启用 v-slot:customFilterDropdown,优先级低于 filterDropdown | boolean | false | 3.0 |
|
||||
| filterDropdownVisible | 用于控制自定义筛选菜单是否可见 | boolean | - | |
|
||||
| filtered | 标识数据是否经过过滤,筛选图标会高亮 | boolean | false | |
|
||||
| filteredValue | 筛选的受控属性,外界可用此控制列的筛选状态,值为已筛选的 value 数组 | string\[] | - | |
|
||||
| filterIcon | 自定义 filter 图标。 | VNode \| ({filtered: boolean, column: Column}) => vNode \|slot | false | |
|
||||
| filterIcon | 自定义 filter 图标。 | VNode \| ({filtered: boolean, column: Column}) => vNode | false | |
|
||||
| filterMultiple | 是否多选 | boolean | true | |
|
||||
| filters | 表头的筛选菜单项 | object\[] | - | |
|
||||
| fixed | 列是否固定,可选 `true`(等效于 left) `'left'` `'right'` | boolean\|string | false | |
|
||||
| key | Vue 需要的 key,如果已经设置了唯一的 `dataIndex`,可以忽略这个属性 | string | - | |
|
||||
| customRender | 生成复杂数据的渲染函数,参数分别为当前行的值,当前行数据,行索引,@return 里面可以设置表格行/列合并,可参考 demo 表格行/列合并 | Function({text, record, index}) {}\|v-slot | - | |
|
||||
| customRender | 生成复杂数据的渲染函数,参数分别为当前行的值,当前行数据,行索引,@return 里面可以设置表格行/列合并,可参考 demo 表格行/列合并 | Function({text, record, index, column}) {} | - | |
|
||||
| responsive | 响应式 breakpoint 配置列表。未设置则始终可见。 | [Breakpoint](#Breakpoint)\[] | - | 3.0 |
|
||||
| showSorterTooltip | 表头显示下一次排序的 tooltip 提示, 覆盖 table 中 `showSorterTooltip` | boolean \| [Tooltip props](/components/tooltip/#API) | true | |
|
||||
| sorter | 排序函数,本地排序使用一个函数(参考 [Array.sort](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/sort) 的 compareFunction),需要服务端排序可设为 true | Function\|boolean | - | |
|
||||
| sortOrder | 排序的受控属性,外界可用此控制列的排序,可设置为 `'ascend'` `'descend'` `false` | boolean\|string | - | |
|
||||
| sortDirections | 支持的排序方式,取值为 `'ascend'` `'descend'` | Array | `['ascend', 'descend']` | 1.5.0 |
|
||||
| title | 列头显示文字 | string\|slot | - | |
|
||||
| title | 列头显示文字 | string | - | |
|
||||
| width | 列宽度 | string\|number | - | |
|
||||
| customCell | 设置单元格属性 | Function(record, rowIndex) | - | |
|
||||
| customHeaderCell | 设置头部单元格属性 | Function(column) | - | |
|
||||
| onFilter | 本地模式下,确定筛选的运行函数, 使用 template 或 jsx 时作为`filter`事件使用 | Function | - | |
|
||||
| onFilterDropdownVisibleChange | 自定义筛选菜单可见变化时调用,使用 template 或 jsx 时作为`filterDropdownVisibleChange`事件使用 | function(visible) {} | - | |
|
||||
| slots | 使用 columns 时,可以通过该属性配置支持 slot 的属性,如 `slots: { filterIcon: 'XXX'}` | object | - | |
|
||||
|
||||
#### Breakpoint
|
||||
|
||||
```ts
|
||||
type Breakpoint = 'xxl' | 'xl' | 'lg' | 'md' | 'sm' | 'xs';
|
||||
```
|
||||
|
||||
### ColumnGroup
|
||||
|
||||
| 参数 | 说明 | 类型 | 默认值 |
|
||||
| --- | --- | --- | --- |
|
||||
| title | 列头显示文字 | string\|slot | - |
|
||||
| slots | 使用 columns 时,可以通过该属性配置支持 slot 的属性,如 `slots: { title: 'XXX'}` | object | - |
|
||||
| 参数 | 说明 | 类型 | 默认值 |
|
||||
| ----- | ------------ | ------------ | ------ |
|
||||
| title | 列头显示文字 | string\|slot | - |
|
||||
|
||||
### pagination
|
||||
|
||||
分页的配置项。
|
||||
|
||||
| 参数 | 说明 | 类型 | 默认值 |
|
||||
| -------- | ------------------ | --------------------------- | -------- |
|
||||
| position | 指定分页显示的位置 | 'top' \| 'bottom' \| 'both' | 'bottom' |
|
||||
| 参数 | 说明 | 类型 | 默认值 |
|
||||
| --- | --- | --- | --- |
|
||||
| position | 指定分页显示的位置, 取值为`topLeft` \| `topCenter` \| `topRight` \|`bottomLeft` \| `bottomCenter` \| `bottomRight` | Array | \[`bottomRight`] |
|
||||
|
||||
更多配置项,请查看 [`Pagination`](/components/pagination/)。
|
||||
|
||||
|
@ -189,28 +212,32 @@ cover: https://gw.alipayobjects.com/zos/alicdn/f-SbcX2Lx/Table.svg
|
|||
|
||||
选择功能的配置。
|
||||
|
||||
| 参数 | 说明 | 类型 | 默认值 |
|
||||
| --- | --- | --- | --- |
|
||||
| columnWidth | 自定义列表选择框宽度 | string\|number | - |
|
||||
| columnTitle | 自定义列表选择框标题 | string\|VNode | - |
|
||||
| fixed | 把选择框列固定在左边 | boolean | - |
|
||||
| getCheckboxProps | 选择框的默认属性配置 | Function(record) | - |
|
||||
| 参数 | 说明 | 类型 | 默认值 | 版本 |
|
||||
| --- | --- | --- | --- | --- |
|
||||
| checkStrictly | checkable 状态下节点选择完全受控(父子数据选中状态不再关联) | boolean | true | 3.0 |
|
||||
| columnWidth | 自定义列表选择框宽度 | string\|number | - | |
|
||||
| columnTitle | 自定义列表选择框标题 | string\|VNode | - | |
|
||||
| fixed | 把选择框列固定在左边 | boolean | - | |
|
||||
| getCheckboxProps | 选择框的默认属性配置 | Function(record) | - | |
|
||||
| hideSelectAll | 隐藏全选勾选框与自定义选择项 | boolean | false | 3.0 |
|
||||
| preserveSelectedRowKeys | 当数据被删除时仍然保留选项的 `key` | boolean | - | 3.0 |
|
||||
| hideDefaultSelections | 去掉『全选』『反选』两个默认选项 | boolean | false |
|
||||
| selectedRowKeys | 指定选中项的 key 数组,需要和 onChange 进行配合 | string\[] | \[] |
|
||||
| selections | 自定义选择配置项, 设为 `true` 时使用默认选择项 | object\[]\|boolean | - |
|
||||
| selections | 自定义选择项 [配置项](#selection), 设为 `true` 时使用默认选择项 | object\[] \| boolean | true | |
|
||||
| type | 多选/单选,`checkbox` or `radio` | string | `checkbox` |
|
||||
| onChange | 选中项发生变化时的回调 | Function(selectedRowKeys, selectedRows) | - |
|
||||
| onSelect | 用户手动选择/取消选择某列的回调 | Function(record, selected, selectedRows, nativeEvent) | - |
|
||||
| onSelectAll | 用户手动选择/取消选择所有列的回调 | Function(selected, selectedRows, changeRows) | - |
|
||||
| onSelectInvert | 用户手动选择反选的回调 | Function(selectedRows) | - |
|
||||
| onSelectNone | 用户清空选择的回调 | function() | - | 3.0 |
|
||||
|
||||
### scroll
|
||||
|
||||
| 参数 | 说明 | 类型 | 默认值 | 版本 |
|
||||
| --- | --- | --- | --- | --- |
|
||||
| x | 设置横向滚动,也可用于指定滚动区域的宽和高,可以设置为像素值,百分比,true 和 ['max-content'](https://developer.mozilla.org/zh-CN/docs/Web/CSS/width#max-content) | number \| true | - | |
|
||||
| y | 设置纵向滚动,也可用于指定滚动区域的宽和高,可以设置为像素值,百分比,true 和 ['max-content'](https://developer.mozilla.org/zh-CN/docs/Web/CSS/width#max-content) | number \| true | - | |
|
||||
| scrollToFirstRowOnChange | 当分页、排序、筛选变化后是否滚动到表格顶部 | boolean | - | 1.5.0 |
|
||||
| 参数 | 说明 | 类型 | 默认值 |
|
||||
| --- | --- | --- | --- |
|
||||
| scrollToFirstRowOnChange | 当分页、排序、筛选变化后是否滚动到表格顶部 | boolean | - |
|
||||
| x | 设置横向滚动,也可用于指定滚动区域的宽,可以设置为像素值,百分比,true 和 ['max-content'](https://developer.mozilla.org/zh-CN/docs/Web/CSS/width#max-content) | string \| number \| true | - |
|
||||
| y | 设置纵向滚动,也可用于指定滚动区域的高,可以设置为像素值 | string \| number | - |
|
||||
|
||||
### selection
|
||||
|
||||
|
@ -222,6 +249,21 @@ cover: https://gw.alipayobjects.com/zos/alicdn/f-SbcX2Lx/Table.svg
|
|||
| text | 选择项显示的文字 | string\|VNode | - |
|
||||
| onSelect | 选择项点击回调 | Function(changeableRowKeys) | - |
|
||||
|
||||
### FilterDropdownProps
|
||||
|
||||
```ts
|
||||
interface FilterDropdownProps {
|
||||
prefixCls: string;
|
||||
setSelectedKeys: (selectedKeys: Key[]) => void;
|
||||
selectedKeys: Key[];
|
||||
confirm: (param?: FilterConfirmProps) => void;
|
||||
clearFilters?: () => void;
|
||||
filters?: ColumnFilterItem[];
|
||||
visible: boolean;
|
||||
column: ColumnType;
|
||||
}
|
||||
```
|
||||
|
||||
## 注意
|
||||
|
||||
在 Table 中,`dataSource` 和 `columns` 里的数据值都需要指定 `key` 值。对于 `dataSource` 默认将每列数据的 `key` 属性作为唯一的标识。
|
||||
|
@ -234,3 +276,9 @@ return <Table rowKey="uid" />;
|
|||
// 或
|
||||
return <Table rowKey={record => record.uid} />;
|
||||
```
|
||||
|
||||
## 从 v1 / v2 升级到 v3
|
||||
|
||||
Table 废弃了 `column.slots`, 新增 `v-slot:bodyCell`、`v-slot:headerCell`,自定义单元格,新增 `column.customFilterDropdown` `v-slot:customFilterDropdown`,自定义筛选菜单,新增了 `v-slot:customFilterIcon` 自定义筛选按钮,但 `column.slots` 还可用,我们会在下一个大版本时移除。
|
||||
|
||||
此外,比较重大的改动为 `dataIndex` 从支持路径嵌套如 `user.age` 改成了数组路径如 `['user', 'age']`。以解决过去属性名带 `.` 需要额外的数据转化问题。
|
||||
|
|
|
@ -1,261 +0,0 @@
|
|||
import type { ExtractPropTypes, PropType, UnwrapRef } from 'vue';
|
||||
import PropTypes, { withUndefined } from '../_util/vue-types';
|
||||
import { paginationProps as getPaginationProps, paginationConfig } from '../pagination';
|
||||
import { spinProps } from '../spin';
|
||||
import { tuple } from '../_util/type';
|
||||
|
||||
const PaginationProps = getPaginationProps();
|
||||
|
||||
export type CompareFn<T> = (a: T, b: T, sortOrder?: SortOrder) => number;
|
||||
export const ColumnFilterItem = PropTypes.shape({
|
||||
text: PropTypes.string,
|
||||
value: PropTypes.string,
|
||||
children: PropTypes.array,
|
||||
}).loose;
|
||||
|
||||
export const columnProps = {
|
||||
title: PropTypes.VNodeChild,
|
||||
key: PropTypes.oneOfType([PropTypes.string, PropTypes.number]),
|
||||
dataIndex: PropTypes.string,
|
||||
customRender: PropTypes.func,
|
||||
customCell: PropTypes.func,
|
||||
customHeaderCell: PropTypes.func,
|
||||
align: PropTypes.oneOf(tuple('left', 'right', 'center')),
|
||||
ellipsis: PropTypes.looseBool,
|
||||
filters: PropTypes.arrayOf(ColumnFilterItem),
|
||||
onFilter: {
|
||||
type: Function as PropType<(value: any, record: any) => boolean>,
|
||||
},
|
||||
filterMultiple: PropTypes.looseBool,
|
||||
filterDropdown: PropTypes.any,
|
||||
filterDropdownVisible: PropTypes.looseBool,
|
||||
onFilterDropdownVisibleChange: {
|
||||
type: Function as PropType<(visible: boolean) => void>,
|
||||
},
|
||||
sorter: PropTypes.oneOfType([PropTypes.looseBool, PropTypes.func]),
|
||||
defaultSortOrder: PropTypes.oneOf(tuple('ascend', 'descend')),
|
||||
colSpan: PropTypes.number,
|
||||
width: PropTypes.oneOfType([PropTypes.string, PropTypes.number]),
|
||||
className: PropTypes.string,
|
||||
fixed: withUndefined(
|
||||
PropTypes.oneOfType([PropTypes.looseBool, PropTypes.oneOf(tuple('left', 'right'))]),
|
||||
),
|
||||
filterIcon: PropTypes.any,
|
||||
filteredValue: PropTypes.array,
|
||||
filtered: PropTypes.looseBool,
|
||||
defaultFilteredValue: PropTypes.array,
|
||||
sortOrder: withUndefined(
|
||||
PropTypes.oneOfType([PropTypes.looseBool, PropTypes.oneOf(tuple('ascend', 'descend'))]),
|
||||
),
|
||||
sortDirections: PropTypes.array,
|
||||
// children?: ColumnProps<T>[];
|
||||
// onCellClick?: (record: T, event: any) => void;
|
||||
// onCell?: (record: T) => any;
|
||||
// onHeaderCell?: (props: ColumnProps<T>) => any;
|
||||
};
|
||||
|
||||
export type ColumnProps = Partial<ExtractPropTypes<typeof columnProps>> & {
|
||||
slots?: {
|
||||
title?: string;
|
||||
filterIcon?: string;
|
||||
filterDropdown?: string;
|
||||
customRender?: string;
|
||||
[key: string]: string | undefined;
|
||||
};
|
||||
};
|
||||
|
||||
export interface TableComponents {
|
||||
table?: any;
|
||||
header?: {
|
||||
wrapper?: any;
|
||||
row?: any;
|
||||
cell?: any;
|
||||
};
|
||||
body?: {
|
||||
wrapper?: any;
|
||||
row?: any;
|
||||
cell?: any;
|
||||
};
|
||||
}
|
||||
|
||||
export const TableLocale = PropTypes.shape({
|
||||
filterTitle: PropTypes.string,
|
||||
filterConfirm: PropTypes.any,
|
||||
filterReset: PropTypes.any,
|
||||
emptyText: PropTypes.any,
|
||||
selectAll: PropTypes.any,
|
||||
selectInvert: PropTypes.any,
|
||||
sortTitle: PropTypes.string,
|
||||
expand: PropTypes.string,
|
||||
collapse: PropTypes.string,
|
||||
}).loose;
|
||||
|
||||
export const RowSelectionType = PropTypes.oneOf(tuple('checkbox', 'radio'));
|
||||
// export type SelectionSelectFn<T> = (record: T, selected: boolean, selectedRows: Object[]) => any;
|
||||
|
||||
export const tableRowSelection = {
|
||||
type: RowSelectionType,
|
||||
selectedRowKeys: PropTypes.array,
|
||||
// onChange?: (selectedRowKeys: string[] | number[], selectedRows: Object[]) => any;
|
||||
getCheckboxProps: PropTypes.func,
|
||||
// onSelect?: SelectionSelectFn<T>;
|
||||
// onSelectAll?: (selected: boolean, selectedRows: Object[], changeRows: Object[]) => any;
|
||||
// onSelectInvert?: (selectedRows: Object[]) => any;
|
||||
selections: withUndefined(PropTypes.oneOfType([PropTypes.array, PropTypes.looseBool])),
|
||||
hideDefaultSelections: PropTypes.looseBool,
|
||||
fixed: PropTypes.looseBool,
|
||||
columnWidth: PropTypes.oneOfType([PropTypes.string, PropTypes.number]),
|
||||
selectWay: PropTypes.oneOf(
|
||||
tuple('onSelect', 'onSelectMultiple', 'onSelectAll', 'onSelectInvert'),
|
||||
),
|
||||
columnTitle: PropTypes.any,
|
||||
};
|
||||
|
||||
export type SortOrder = 'descend' | 'ascend';
|
||||
|
||||
const paginationProps = paginationConfig();
|
||||
|
||||
export const tableProps = {
|
||||
prefixCls: PropTypes.string,
|
||||
dropdownPrefixCls: PropTypes.string,
|
||||
rowSelection: PropTypes.oneOfType([PropTypes.shape(tableRowSelection).loose, Object]),
|
||||
pagination: withUndefined(
|
||||
PropTypes.oneOfType([
|
||||
PropTypes.shape<Partial<ExtractPropTypes<typeof paginationProps>>>(paginationProps).loose,
|
||||
PropTypes.looseBool,
|
||||
]),
|
||||
),
|
||||
size: PropTypes.oneOf(tuple('default', 'middle', 'small', 'large')),
|
||||
dataSource: PropTypes.array,
|
||||
components: PropTypes.object,
|
||||
columns: {
|
||||
type: Array as PropType<ColumnProps>,
|
||||
},
|
||||
rowKey: PropTypes.oneOfType([PropTypes.string, PropTypes.func]),
|
||||
rowClassName: PropTypes.func,
|
||||
expandedRowRender: PropTypes.any,
|
||||
defaultExpandAllRows: PropTypes.looseBool,
|
||||
defaultExpandedRowKeys: PropTypes.array,
|
||||
expandedRowKeys: PropTypes.array,
|
||||
expandIconAsCell: PropTypes.looseBool,
|
||||
expandIconColumnIndex: PropTypes.number,
|
||||
expandRowByClick: PropTypes.looseBool,
|
||||
loading: PropTypes.oneOfType([PropTypes.shape(spinProps()).loose, PropTypes.looseBool]),
|
||||
locale: TableLocale,
|
||||
indentSize: PropTypes.number,
|
||||
customRow: PropTypes.func,
|
||||
customHeaderRow: PropTypes.func,
|
||||
useFixedHeader: PropTypes.looseBool,
|
||||
bordered: PropTypes.looseBool,
|
||||
showHeader: PropTypes.looseBool,
|
||||
footer: PropTypes.func,
|
||||
title: PropTypes.func,
|
||||
scroll: {
|
||||
type: Object as PropType<{
|
||||
x?: boolean | number | string;
|
||||
y?: boolean | number | string;
|
||||
scrollToFirstRowOnChange?: boolean;
|
||||
}>,
|
||||
},
|
||||
childrenColumnName: PropTypes.oneOfType([PropTypes.array, PropTypes.string]),
|
||||
bodyStyle: PropTypes.style,
|
||||
sortDirections: {
|
||||
type: Array as PropType<SortOrder[]>,
|
||||
},
|
||||
tableLayout: PropTypes.string,
|
||||
getPopupContainer: PropTypes.func,
|
||||
expandIcon: PropTypes.func,
|
||||
transformCellText: PropTypes.func,
|
||||
onExpandedRowsChange: PropTypes.func,
|
||||
onExpand: PropTypes.func,
|
||||
onChange: PropTypes.func,
|
||||
onRowClick: PropTypes.func,
|
||||
// style?: React.CSSProperties;
|
||||
// children?: React.ReactNode;
|
||||
};
|
||||
|
||||
export type TableRowSelection = Partial<ExtractPropTypes<typeof tableRowSelection>>;
|
||||
|
||||
export type TableProps = Partial<ExtractPropTypes<typeof tableProps>>;
|
||||
|
||||
export interface TableStateFilters {
|
||||
[key: string]: string[];
|
||||
}
|
||||
|
||||
export interface TableState {
|
||||
pagination?: Partial<ExtractPropTypes<typeof PaginationProps>>;
|
||||
filters?: TableStateFilters;
|
||||
sortColumn?: ColumnProps | null;
|
||||
sortOrder?: SortOrder;
|
||||
columns?: ColumnProps[];
|
||||
}
|
||||
|
||||
export interface TransformCellTextProps {
|
||||
text: any;
|
||||
column: ColumnProps;
|
||||
record: any;
|
||||
index: number;
|
||||
}
|
||||
|
||||
// export type SelectionItemSelectFn = (key: string[]) => any;
|
||||
|
||||
// export interface SelectionItem {
|
||||
// key: PropTypes.string,
|
||||
// text: PropTypes.any,
|
||||
// onSelect: SelectionItemSelectFn;
|
||||
// }
|
||||
export type TableStore = UnwrapRef<{
|
||||
selectedRowKeys: any[];
|
||||
selectionDirty: boolean;
|
||||
}>;
|
||||
export const SelectionCheckboxAllProps = {
|
||||
propsSymbol: PropTypes.any,
|
||||
store: PropTypes.any,
|
||||
locale: PropTypes.any,
|
||||
disabled: PropTypes.looseBool,
|
||||
getCheckboxPropsByItem: PropTypes.func,
|
||||
getRecordKey: PropTypes.func,
|
||||
data: PropTypes.array,
|
||||
prefixCls: PropTypes.string,
|
||||
hideDefaultSelections: PropTypes.looseBool,
|
||||
selections: PropTypes.oneOfType([PropTypes.array, PropTypes.looseBool]),
|
||||
getPopupContainer: PropTypes.func,
|
||||
onSelect: PropTypes.func,
|
||||
};
|
||||
|
||||
// export interface SelectionCheckboxAllState {
|
||||
// checked: PropTypes.looseBool,
|
||||
// indeterminate: PropTypes.looseBool,
|
||||
// }
|
||||
|
||||
export const SelectionBoxProps = {
|
||||
store: PropTypes.any,
|
||||
type: RowSelectionType,
|
||||
defaultSelection: PropTypes.array,
|
||||
rowIndex: PropTypes.oneOfType([PropTypes.string, PropTypes.number]),
|
||||
name: PropTypes.string,
|
||||
disabled: PropTypes.looseBool,
|
||||
id: PropTypes.string,
|
||||
// onChange: React.ChangeEventHandler<HTMLInputElement>;
|
||||
};
|
||||
|
||||
// export interface SelectionBoxState {
|
||||
// checked?: PropTypes.looseBool,
|
||||
// }
|
||||
|
||||
export const FilterMenuProps = {
|
||||
locale: TableLocale,
|
||||
selectedKeys: PropTypes.array,
|
||||
column: PropTypes.object,
|
||||
confirmFilter: PropTypes.func,
|
||||
prefixCls: PropTypes.string,
|
||||
dropdownPrefixCls: PropTypes.string,
|
||||
getPopupContainer: PropTypes.func,
|
||||
handleFilter: PropTypes.func,
|
||||
};
|
||||
|
||||
// export interface FilterMenuState {
|
||||
// selectedKeys: string[];
|
||||
// keyPathOfSelectedItem: { [key: string]: string };
|
||||
// visible?: PropTypes.looseBool,
|
||||
// }
|
|
@ -0,0 +1,205 @@
|
|||
import type {
|
||||
GetRowKey,
|
||||
ColumnType as RcColumnType,
|
||||
RenderedCell as RcRenderedCell,
|
||||
ExpandableConfig,
|
||||
DefaultRecordType,
|
||||
} from '../vc-table/interface';
|
||||
import type { TooltipProps } from '../tooltip';
|
||||
import type { CheckboxProps } from '../checkbox';
|
||||
import type { PaginationProps } from '../pagination';
|
||||
import type { Breakpoint } from '../_util/responsiveObserve';
|
||||
import type { INTERNAL_SELECTION_ITEM } from './hooks/useSelection';
|
||||
import type { VueNode } from '../_util/type';
|
||||
import { tuple } from '../_util/type';
|
||||
// import { TableAction } from './Table';
|
||||
|
||||
export type { GetRowKey, ExpandableConfig };
|
||||
|
||||
export type Key = string | number;
|
||||
|
||||
export type RowSelectionType = 'checkbox' | 'radio';
|
||||
|
||||
export type SelectionItemSelectFn = (currentRowKeys: Key[]) => void;
|
||||
|
||||
export type ExpandType = null | 'row' | 'nest';
|
||||
|
||||
export interface TableLocale {
|
||||
filterTitle?: string;
|
||||
filterConfirm?: any;
|
||||
filterReset?: any;
|
||||
filterEmptyText?: any;
|
||||
emptyText?: any | (() => any);
|
||||
selectAll?: any;
|
||||
selectNone?: any;
|
||||
selectInvert?: any;
|
||||
selectionAll?: any;
|
||||
sortTitle?: string;
|
||||
expand?: string;
|
||||
collapse?: string;
|
||||
triggerDesc?: string;
|
||||
triggerAsc?: string;
|
||||
cancelSort?: string;
|
||||
}
|
||||
|
||||
export type SortOrder = 'descend' | 'ascend' | null;
|
||||
|
||||
const TableActions = tuple('paginate', 'sort', 'filter');
|
||||
export type TableAction = typeof TableActions[number];
|
||||
|
||||
export type CompareFn<T> = (a: T, b: T, sortOrder?: SortOrder) => number;
|
||||
|
||||
export interface ColumnFilterItem {
|
||||
text: VueNode;
|
||||
value: string | number | boolean;
|
||||
children?: ColumnFilterItem[];
|
||||
}
|
||||
|
||||
export interface ColumnTitleProps<RecordType> {
|
||||
/** @deprecated Please use `sorterColumns` instead. */
|
||||
sortOrder?: SortOrder;
|
||||
/** @deprecated Please use `sorterColumns` instead. */
|
||||
sortColumn?: ColumnType<RecordType>;
|
||||
sortColumns?: { column: ColumnType<RecordType>; order: SortOrder }[];
|
||||
|
||||
filters?: Record<string, string[]>;
|
||||
}
|
||||
|
||||
export type ColumnTitle<RecordType> = VueNode | ((props: ColumnTitleProps<RecordType>) => VueNode);
|
||||
|
||||
export type FilterValue = (Key | boolean)[];
|
||||
export type FilterKey = Key[] | null;
|
||||
export interface FilterConfirmProps {
|
||||
closeDropdown: boolean;
|
||||
}
|
||||
|
||||
export interface FilterDropdownProps<RecordType> {
|
||||
prefixCls: string;
|
||||
setSelectedKeys: (selectedKeys: Key[]) => void;
|
||||
selectedKeys: Key[];
|
||||
confirm: (param?: FilterConfirmProps) => void;
|
||||
clearFilters?: () => void;
|
||||
filters?: ColumnFilterItem[];
|
||||
visible: boolean;
|
||||
column: ColumnType<RecordType>;
|
||||
}
|
||||
|
||||
export interface ColumnType<RecordType = DefaultRecordType> extends RcColumnType<RecordType> {
|
||||
title?: ColumnTitle<RecordType>;
|
||||
// Sorter
|
||||
sorter?:
|
||||
| boolean
|
||||
| CompareFn<RecordType>
|
||||
| {
|
||||
compare?: CompareFn<RecordType>;
|
||||
/** Config multiple sorter order priority */
|
||||
multiple?: number;
|
||||
};
|
||||
sortOrder?: SortOrder;
|
||||
defaultSortOrder?: SortOrder;
|
||||
sortDirections?: SortOrder[];
|
||||
showSorterTooltip?: boolean | TooltipProps;
|
||||
|
||||
// Filter
|
||||
filtered?: boolean;
|
||||
filters?: ColumnFilterItem[];
|
||||
filterDropdown?: VueNode | ((props: FilterDropdownProps<RecordType>) => VueNode);
|
||||
filterMultiple?: boolean;
|
||||
filteredValue?: FilterValue | null;
|
||||
defaultFilteredValue?: FilterValue | null;
|
||||
filterIcon?: VueNode | ((opt: { filtered: boolean; column: ColumnType }) => VueNode);
|
||||
onFilter?: (value: string | number | boolean, record: RecordType) => boolean;
|
||||
filterDropdownVisible?: boolean;
|
||||
onFilterDropdownVisibleChange?: (visible: boolean) => void;
|
||||
|
||||
// Responsive
|
||||
responsive?: Breakpoint[];
|
||||
}
|
||||
|
||||
export interface ColumnGroupType<RecordType> extends Omit<ColumnType<RecordType>, 'dataIndex'> {
|
||||
children: ColumnsType<RecordType>;
|
||||
}
|
||||
|
||||
export type ColumnsType<RecordType = DefaultRecordType> = (
|
||||
| ColumnGroupType<RecordType>
|
||||
| ColumnType<RecordType>
|
||||
)[];
|
||||
|
||||
export interface SelectionItem {
|
||||
key: string;
|
||||
text: VueNode;
|
||||
onSelect?: SelectionItemSelectFn;
|
||||
}
|
||||
|
||||
export type SelectionSelectFn<T> = (
|
||||
record: T,
|
||||
selected: boolean,
|
||||
selectedRows: T[],
|
||||
nativeEvent: Event,
|
||||
) => void;
|
||||
|
||||
export interface TableRowSelection<T = DefaultRecordType> {
|
||||
/** Keep the selection keys in list even the key not exist in `dataSource` anymore */
|
||||
preserveSelectedRowKeys?: boolean;
|
||||
type?: RowSelectionType;
|
||||
selectedRowKeys?: Key[];
|
||||
defaultSelectedRowKeys?: Key[];
|
||||
onChange?: (selectedRowKeys: Key[], selectedRows: T[]) => void;
|
||||
getCheckboxProps?: (record: T) => Partial<Omit<CheckboxProps, 'checked' | 'defaultChecked'>>;
|
||||
onSelect?: SelectionSelectFn<T>;
|
||||
onSelectMultiple?: (selected: boolean, selectedRows: T[], changeRows: T[]) => void;
|
||||
/** @deprecated This function is meaningless and should use `onChange` instead */
|
||||
onSelectAll?: (selected: boolean, selectedRows: T[], changeRows: T[]) => void;
|
||||
/** @deprecated This function is meaningless and should use `onChange` instead */
|
||||
onSelectInvert?: (selectedRowKeys: Key[]) => void;
|
||||
onSelectNone?: () => void;
|
||||
selections?: INTERNAL_SELECTION_ITEM[] | boolean;
|
||||
hideSelectAll?: boolean;
|
||||
fixed?: boolean;
|
||||
columnWidth?: string | number;
|
||||
columnTitle?: string | VueNode;
|
||||
checkStrictly?: boolean;
|
||||
renderCell?: (
|
||||
value: boolean,
|
||||
record: T,
|
||||
index: number,
|
||||
originNode: VueNode,
|
||||
) => VueNode | RcRenderedCell<T>;
|
||||
}
|
||||
|
||||
export type TransformColumns<RecordType> = (
|
||||
columns: ColumnsType<RecordType>,
|
||||
) => ColumnsType<RecordType>;
|
||||
|
||||
export interface TableCurrentDataSource<RecordType = DefaultRecordType> {
|
||||
currentDataSource: RecordType[];
|
||||
action: TableAction;
|
||||
}
|
||||
|
||||
export interface SorterResult<RecordType = DefaultRecordType> {
|
||||
column?: ColumnType<RecordType>;
|
||||
order?: SortOrder;
|
||||
field?: Key | readonly Key[];
|
||||
columnKey?: Key;
|
||||
}
|
||||
|
||||
export type GetPopupContainer = (triggerNode: HTMLElement) => HTMLElement;
|
||||
|
||||
type TablePaginationPosition =
|
||||
| 'topLeft'
|
||||
| 'topCenter'
|
||||
| 'topRight'
|
||||
| 'bottomLeft'
|
||||
| 'bottomCenter'
|
||||
| 'bottomRight';
|
||||
|
||||
export interface TablePaginationConfig extends PaginationProps {
|
||||
position?: TablePaginationPosition[];
|
||||
}
|
||||
|
||||
export interface TransformCellTextProps {
|
||||
text: any;
|
||||
column: ColumnType;
|
||||
record: any;
|
||||
index: number;
|
||||
}
|
|
@ -0,0 +1,129 @@
|
|||
@import './index';
|
||||
@import './size';
|
||||
|
||||
@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: @table-border;
|
||||
border-right: 0;
|
||||
border-bottom: 0;
|
||||
|
||||
> .@{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}-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;
|
||||
}
|
||||
|
||||
&-scrollbar {
|
||||
box-shadow: 0 @border-width-base 0 @border-width-base @table-header-bg;
|
||||
}
|
||||
}
|
File diff suppressed because it is too large
Load Diff
|
@ -3,9 +3,12 @@ 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';
|
|
@ -0,0 +1,45 @@
|
|||
// ================================================================
|
||||
// = 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;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,162 @@
|
|||
@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;
|
||||
}
|
||||
}
|
||||
|
||||
.@{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: @padding-xs;
|
||||
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-container {
|
||||
.@{table-wrapepr-rtl-cls} & {
|
||||
right: auto;
|
||||
left: 0;
|
||||
}
|
||||
}
|
||||
|
||||
// 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,38 +1,34 @@
|
|||
@table-padding-vertical-md: (@table-padding-vertical * 3 / 4);
|
||||
@table-padding-horizontal-md: (@table-padding-horizontal / 2);
|
||||
@table-padding-vertical-sm: (@table-padding-vertical / 2);
|
||||
@table-padding-horizontal-sm: (@table-padding-horizontal / 2);
|
||||
@import './index';
|
||||
|
||||
.table-size(@size, @padding-vertical, @padding-horizontal) {
|
||||
.table-size(@size, @padding-vertical, @padding-horizontal, @font-size) {
|
||||
.@{table-prefix-cls}.@{table-prefix-cls}-@{size} {
|
||||
> .@{table-prefix-cls}-title,
|
||||
> .@{table-prefix-cls}-content > .@{table-prefix-cls}-footer {
|
||||
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}-content {
|
||||
> .@{table-prefix-cls}-header > table,
|
||||
> .@{table-prefix-cls}-body > table,
|
||||
> .@{table-prefix-cls}-scroll > .@{table-prefix-cls}-header > table,
|
||||
> .@{table-prefix-cls}-scroll > .@{table-prefix-cls}-body > table,
|
||||
> .@{table-prefix-cls}-fixed-left > .@{table-prefix-cls}-header > table,
|
||||
> .@{table-prefix-cls}-fixed-right > .@{table-prefix-cls}-header > table,
|
||||
> .@{table-prefix-cls}-fixed-left
|
||||
> .@{table-prefix-cls}-body-outer
|
||||
> .@{table-prefix-cls}-body-inner
|
||||
> table,
|
||||
> .@{table-prefix-cls}-fixed-right
|
||||
> .@{table-prefix-cls}-body-outer
|
||||
> .@{table-prefix-cls}-body-inner
|
||||
> table {
|
||||
> .@{table-prefix-cls}-thead > tr > th,
|
||||
> .@{table-prefix-cls}-tbody > tr > td {
|
||||
padding: @padding-vertical @padding-horizontal;
|
||||
}
|
||||
}
|
||||
|
||||
.@{table-prefix-cls}-filter-trigger {
|
||||
margin-right: -(@padding-horizontal / 2);
|
||||
}
|
||||
|
||||
tr.@{table-prefix-cls}-expanded-row td > .@{table-prefix-cls}-wrapper {
|
||||
margin: -@padding-vertical (-@table-padding-horizontal / 2) -@padding-vertical - 1px;
|
||||
.@{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)));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -40,14 +36,17 @@
|
|||
// ================================================================
|
||||
// = Middle =
|
||||
// ================================================================
|
||||
.table-size(~'middle', @table-padding-vertical-md, @table-padding-horizontal-md);
|
||||
.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-size(~'small', @table-padding-vertical-sm, @table-padding-horizontal-sm, @table-font-size-sm);
|
||||
|
||||
.@{table-prefix-cls}-small {
|
||||
.@{table-prefix-cls}-thead > tr > th {
|
||||
background-color: @table-header-bg-sm;
|
||||
}
|
||||
.@{table-prefix-cls}-selection-column {
|
||||
width: 46px;
|
||||
min-width: 46px;
|
||||
|
|
|
@ -1,73 +1,63 @@
|
|||
export function flatArray(data = [], childrenName = 'children') {
|
||||
const result = [];
|
||||
const loop = array => {
|
||||
array.forEach(item => {
|
||||
if (item[childrenName]) {
|
||||
const newItem = { ...item };
|
||||
delete newItem[childrenName];
|
||||
result.push(newItem);
|
||||
if (item[childrenName].length > 0) {
|
||||
loop(item[childrenName]);
|
||||
}
|
||||
} else {
|
||||
result.push(item);
|
||||
}
|
||||
});
|
||||
};
|
||||
loop(data);
|
||||
return result;
|
||||
import { camelize } from 'vue';
|
||||
import { flattenChildren } from '../_util/props-util';
|
||||
import type { ColumnType, ColumnsType, ColumnTitle, ColumnTitleProps, Key } from './interface';
|
||||
|
||||
export function getColumnKey<RecordType>(column: ColumnType<RecordType>, defaultKey: string): Key {
|
||||
if ('key' in column && column.key !== undefined && column.key !== null) {
|
||||
return column.key;
|
||||
}
|
||||
if (column.dataIndex) {
|
||||
return (Array.isArray(column.dataIndex) ? column.dataIndex.join('.') : column.dataIndex) as Key;
|
||||
}
|
||||
|
||||
return defaultKey;
|
||||
}
|
||||
|
||||
export function treeMap(tree, mapper, childrenName = 'children') {
|
||||
return tree.map((node, index) => {
|
||||
const extra = {};
|
||||
if (node[childrenName]) {
|
||||
extra[childrenName] = treeMap(node[childrenName], mapper, childrenName);
|
||||
export function getColumnPos(index: number, pos?: string) {
|
||||
return pos ? `${pos}-${index}` : `${index}`;
|
||||
}
|
||||
|
||||
export function renderColumnTitle<RecordType>(
|
||||
title: ColumnTitle<RecordType>,
|
||||
props: ColumnTitleProps<RecordType>,
|
||||
) {
|
||||
if (typeof title === 'function') {
|
||||
return title(props);
|
||||
}
|
||||
|
||||
return title;
|
||||
}
|
||||
|
||||
export function convertChildrenToColumns<RecordType>(
|
||||
elements: any[] = [],
|
||||
): ColumnsType<RecordType> {
|
||||
const flattenElements = flattenChildren(elements);
|
||||
const columns = [];
|
||||
flattenElements.forEach(element => {
|
||||
if (!element) {
|
||||
return;
|
||||
}
|
||||
return {
|
||||
...mapper(node, index),
|
||||
...extra,
|
||||
};
|
||||
const key = element.key;
|
||||
const style = element.props?.style || {};
|
||||
const cls = element.props?.class || '';
|
||||
const props = element.props || {};
|
||||
for (const [k, v] of Object.entries(props)) {
|
||||
props[camelize(k)] = v;
|
||||
}
|
||||
const { default: children, ...restSlots } = element.children || {};
|
||||
const column = { ...restSlots, ...props, style, class: cls };
|
||||
if (key) {
|
||||
column.key = key;
|
||||
}
|
||||
if (element.type?.__ANT_TABLE_COLUMN_GROUP) {
|
||||
column.children = convertChildrenToColumns(
|
||||
typeof children === 'function' ? children() : children,
|
||||
);
|
||||
} else {
|
||||
const customRender = element.children?.default;
|
||||
column.customRender = column.customRender || customRender;
|
||||
}
|
||||
columns.push(column);
|
||||
});
|
||||
}
|
||||
|
||||
export function flatFilter(tree, callback) {
|
||||
return tree.reduce((acc, node) => {
|
||||
if (callback(node)) {
|
||||
acc.push(node);
|
||||
}
|
||||
if (node.children) {
|
||||
const children = flatFilter(node.children, callback);
|
||||
acc.push(...children);
|
||||
}
|
||||
return acc;
|
||||
}, []);
|
||||
}
|
||||
|
||||
// export function normalizeColumns (elements) {
|
||||
// const columns = []
|
||||
// React.Children.forEach(elements, (element) => {
|
||||
// if (!React.isValidElement(element)) {
|
||||
// return
|
||||
// }
|
||||
// const column = {
|
||||
// ...element.props,
|
||||
// }
|
||||
// if (element.key) {
|
||||
// column.key = element.key
|
||||
// }
|
||||
// if (element.type && element.type.__ANT_TABLE_COLUMN_GROUP) {
|
||||
// column.children = normalizeColumns(column.children)
|
||||
// }
|
||||
// columns.push(column)
|
||||
// })
|
||||
// return columns
|
||||
// }
|
||||
|
||||
export function generateValueMaps(items, maps = {}) {
|
||||
(items || []).forEach(({ value, children }) => {
|
||||
maps[value.toString()] = value;
|
||||
generateValueMaps(children, maps);
|
||||
});
|
||||
return maps;
|
||||
return columns;
|
||||
}
|
||||
|
|
|
@ -421,340 +421,327 @@ exports[`renders ./components/transfer/demo/table-transfer.vue correctly 1`] = `
|
|||
<div class="ant-spin-nested-loading">
|
||||
<!---->
|
||||
<div class="ant-spin-container">
|
||||
<div class="ant-table ant-table-small ant-table-scroll-position-left">
|
||||
<div class="ant-table ant-table-small">
|
||||
<!---->
|
||||
<div class="ant-table-content">
|
||||
<!---->
|
||||
<div class="ant-table-body">
|
||||
<table class="">
|
||||
<div class="ant-table-container">
|
||||
<div class="ant-table-content">
|
||||
<table style="table-layout: auto;">
|
||||
<colgroup>
|
||||
<col data-key="selection-column" class="ant-table-selection-col">
|
||||
<col data-key="title">
|
||||
<col data-key="description">
|
||||
<col class="ant-table-selection-col">
|
||||
</colgroup>
|
||||
<thead class="ant-table-thead">
|
||||
<tr>
|
||||
<th colstart="0" colspan="1" colend="0" rowspan="1" class="ant-table-selection-column"><span class="ant-table-header-column"><div><span class="ant-table-column-title"><div class="ant-table-selection"><label class="ant-checkbox-wrapper"><span class="ant-checkbox"><input type="checkbox" class="ant-checkbox-input"><span class="ant-checkbox-inner"></span></span>
|
||||
<!----></label>
|
||||
<th class="ant-table-cell ant-table-selection-column" colstart="0" colend="0">
|
||||
<!---->
|
||||
</div></span><span class="ant-table-column-sorter"><!----></span>
|
||||
</div></span>
|
||||
<!---->
|
||||
</th>
|
||||
<th colstart="1" colspan="1" colend="1" rowspan="1" class=""><span class="ant-table-header-column"><div><span class="ant-table-column-title">Name</span><span class="ant-table-column-sorter"><!----></span>
|
||||
</div></span>
|
||||
<!---->
|
||||
</th>
|
||||
<th colstart="2" colspan="1" colend="2" rowspan="1" class=""><span class="ant-table-header-column"><div><span class="ant-table-column-title">Description</span><span class="ant-table-column-sorter"><!----></span>
|
||||
</div></span>
|
||||
<!---->
|
||||
</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody class="ant-table-tbody">
|
||||
<tr class="ant-table-row ant-table-row-level-0" data-row-key="0">
|
||||
<td class="ant-table-selection-column">
|
||||
<!---->
|
||||
<!----><span><label class="ant-checkbox-wrapper ant-checkbox-wrapper-disabled"><span class="ant-checkbox ant-checkbox-disabled"><input type="checkbox" disabled="" class="ant-checkbox-input"><span class="ant-checkbox-inner"></span></span>
|
||||
<!----></label></span>
|
||||
</td>
|
||||
<td class="">
|
||||
<!---->
|
||||
<!---->content1
|
||||
</td>
|
||||
<td class="">
|
||||
<!---->
|
||||
<!---->description of content1
|
||||
</td>
|
||||
</tr>
|
||||
<tr class="ant-table-row ant-table-row-level-0" data-row-key="1">
|
||||
<td class="ant-table-selection-column">
|
||||
<!---->
|
||||
<!----><span><label class="ant-checkbox-wrapper"><span class="ant-checkbox"><input type="checkbox" class="ant-checkbox-input"><span class="ant-checkbox-inner"></span></span>
|
||||
<!----></label></span>
|
||||
</td>
|
||||
<td class="">
|
||||
<!---->
|
||||
<!---->content2
|
||||
</td>
|
||||
<td class="">
|
||||
<!---->
|
||||
<!---->description of content2
|
||||
</td>
|
||||
</tr>
|
||||
<tr class="ant-table-row ant-table-row-level-0" data-row-key="3">
|
||||
<td class="ant-table-selection-column">
|
||||
<!---->
|
||||
<!----><span><label class="ant-checkbox-wrapper"><span class="ant-checkbox"><input type="checkbox" class="ant-checkbox-input"><span class="ant-checkbox-inner"></span></span>
|
||||
<!----></label></span>
|
||||
</td>
|
||||
<td class="">
|
||||
<!---->
|
||||
<!---->content4
|
||||
</td>
|
||||
<td class="">
|
||||
<!---->
|
||||
<!---->description of content4
|
||||
</td>
|
||||
</tr>
|
||||
<tr class="ant-table-row ant-table-row-level-0" data-row-key="4">
|
||||
<td class="ant-table-selection-column">
|
||||
<!---->
|
||||
<!----><span><label class="ant-checkbox-wrapper ant-checkbox-wrapper-disabled"><span class="ant-checkbox ant-checkbox-disabled"><input type="checkbox" disabled="" class="ant-checkbox-input"><span class="ant-checkbox-inner"></span></span>
|
||||
<!----></label></span>
|
||||
</td>
|
||||
<td class="">
|
||||
<!---->
|
||||
<!---->content5
|
||||
</td>
|
||||
<td class="">
|
||||
<!---->
|
||||
<!---->description of content5
|
||||
</td>
|
||||
</tr>
|
||||
<tr class="ant-table-row ant-table-row-level-0" data-row-key="6">
|
||||
<td class="ant-table-selection-column">
|
||||
<!---->
|
||||
<!----><span><label class="ant-checkbox-wrapper"><span class="ant-checkbox"><input type="checkbox" class="ant-checkbox-input"><span class="ant-checkbox-inner"></span></span>
|
||||
<!----></label></span>
|
||||
</td>
|
||||
<td class="">
|
||||
<!---->
|
||||
<!---->content7
|
||||
</td>
|
||||
<td class="">
|
||||
<!---->
|
||||
<!---->description of content7
|
||||
</td>
|
||||
</tr>
|
||||
<tr class="ant-table-row ant-table-row-level-0" data-row-key="7">
|
||||
<td class="ant-table-selection-column">
|
||||
<!---->
|
||||
<!----><span><label class="ant-checkbox-wrapper"><span class="ant-checkbox"><input type="checkbox" class="ant-checkbox-input"><span class="ant-checkbox-inner"></span></span>
|
||||
<!----></label></span>
|
||||
</td>
|
||||
<td class="">
|
||||
<!---->
|
||||
<!---->content8
|
||||
</td>
|
||||
<td class="">
|
||||
<!---->
|
||||
<!---->description of content8
|
||||
</td>
|
||||
</tr>
|
||||
<tr class="ant-table-row ant-table-row-level-0" data-row-key="9">
|
||||
<td class="ant-table-selection-column">
|
||||
<!---->
|
||||
<!----><span><label class="ant-checkbox-wrapper"><span class="ant-checkbox"><input type="checkbox" class="ant-checkbox-input"><span class="ant-checkbox-inner"></span></span>
|
||||
<!----></label></span>
|
||||
</td>
|
||||
<td class="">
|
||||
<!---->
|
||||
<!---->content10
|
||||
</td>
|
||||
<td class="">
|
||||
<!---->
|
||||
<!---->description of content10
|
||||
</td>
|
||||
</tr>
|
||||
<tr class="ant-table-row ant-table-row-level-0" data-row-key="10">
|
||||
<td class="ant-table-selection-column">
|
||||
<!---->
|
||||
<!----><span><label class="ant-checkbox-wrapper"><span class="ant-checkbox"><input type="checkbox" class="ant-checkbox-input"><span class="ant-checkbox-inner"></span></span>
|
||||
<!----></label></span>
|
||||
</td>
|
||||
<td class="">
|
||||
<!---->
|
||||
<!---->content11
|
||||
</td>
|
||||
<td class="">
|
||||
<!---->
|
||||
<!---->description of content11
|
||||
</td>
|
||||
</tr>
|
||||
<tr class="ant-table-row ant-table-row-level-0" data-row-key="12">
|
||||
<td class="ant-table-selection-column">
|
||||
<!---->
|
||||
<!----><span><label class="ant-checkbox-wrapper ant-checkbox-wrapper-disabled"><span class="ant-checkbox ant-checkbox-disabled"><input type="checkbox" disabled="" class="ant-checkbox-input"><span class="ant-checkbox-inner"></span></span>
|
||||
<!----></label></span>
|
||||
</td>
|
||||
<td class="">
|
||||
<!---->
|
||||
<!---->content13
|
||||
</td>
|
||||
<td class="">
|
||||
<!---->
|
||||
<!---->description of content13
|
||||
</td>
|
||||
</tr>
|
||||
<tr class="ant-table-row ant-table-row-level-0" data-row-key="13">
|
||||
<td class="ant-table-selection-column">
|
||||
<!---->
|
||||
<!----><span><label class="ant-checkbox-wrapper"><span class="ant-checkbox"><input type="checkbox" class="ant-checkbox-input"><span class="ant-checkbox-inner"></span></span>
|
||||
<!----></label></span>
|
||||
</td>
|
||||
<td class="">
|
||||
<!---->
|
||||
<!---->content14
|
||||
</td>
|
||||
<td class="">
|
||||
<!---->
|
||||
<!---->description of content14
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
<!---->
|
||||
<!---->
|
||||
</div>
|
||||
</div>
|
||||
<ul unselectable="on" class="ant-pagination mini ant-table-pagination">
|
||||
<!---->
|
||||
<li title="Previous Page" class="ant-pagination-prev ant-pagination-disabled" aria-disabled="true"><button class="ant-pagination-item-link" type="button" tabindex="-1" disabled=""><span role="img" aria-label="left" class="anticon anticon-left"><svg focusable="false" class="" data-icon="left" width="1em" height="1em" fill="currentColor" aria-hidden="true" viewBox="64 64 896 896"><path d="M724 218.3V141c0-6.7-7.7-10.4-12.9-6.3L260.3 486.8a31.86 31.86 0 000 50.3l450.8 352.1c5.3 4.1 12.9.4 12.9-6.3v-77.3c0-4.9-2.3-9.6-6.1-12.6l-360-281 360-281.1c3.8-3 6.1-7.7 6.1-12.6z"></path></svg></span></button></li>
|
||||
<li title="1" tabindex="0" class="ant-pagination-item ant-pagination-item-1 ant-pagination-item-active"><a rel="nofollow">1</a></li>
|
||||
<li title="2" tabindex="0" class="ant-pagination-item ant-pagination-item-2"><a rel="nofollow">2</a></li>
|
||||
<li title="Next Page" tabindex="0" class="ant-pagination-next" aria-disabled="false"><button class="ant-pagination-item-link" type="button" tabindex="-1"><span role="img" aria-label="right" class="anticon anticon-right"><svg focusable="false" class="" data-icon="right" width="1em" height="1em" fill="currentColor" aria-hidden="true" viewBox="64 64 896 896"><path d="M765.7 486.8L314.9 134.7A7.97 7.97 0 00302 141v77.3c0 4.9 2.3 9.6 6.1 12.6l360 281.1-360 281.1c-3.9 3-6.1 7.7-6.1 12.6V883c0 6.7 7.7 10.4 12.9 6.3l450.8-352.1a31.96 31.96 0 000-50.4z"></path></svg></span></button></li>
|
||||
<!---->
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!---->
|
||||
</div>
|
||||
<div class="ant-transfer-operation"><button disabled="" class="ant-btn ant-btn-primary ant-btn-sm ant-btn-icon-only" type="button"><span role="img" aria-label="right" class="anticon anticon-right"><svg focusable="false" class="" data-icon="right" width="1em" height="1em" fill="currentColor" aria-hidden="true" viewBox="64 64 896 896"><path d="M765.7 486.8L314.9 134.7A7.97 7.97 0 00302 141v77.3c0 4.9 2.3 9.6 6.1 12.6l360 281.1-360 281.1c-3.9 3-6.1 7.7-6.1 12.6V883c0 6.7 7.7 10.4 12.9 6.3l450.8-352.1a31.96 31.96 0 000-50.4z"></path></svg></span></button><button disabled="" class="ant-btn ant-btn-primary ant-btn-sm ant-btn-icon-only" type="button"><span role="img" aria-label="left" class="anticon anticon-left"><svg focusable="false" class="" data-icon="left" width="1em" height="1em" fill="currentColor" aria-hidden="true" viewBox="64 64 896 896"><path d="M724 218.3V141c0-6.7-7.7-10.4-12.9-6.3L260.3 486.8a31.86 31.86 0 000 50.3l450.8 352.1c5.3 4.1 12.9.4 12.9-6.3v-77.3c0-4.9-2.3-9.6-6.1-12.6l-360-281 360-281.1c3.8-3 6.1-7.7 6.1-12.6z"></path></svg></span></button></div>
|
||||
<div class="ant-transfer-list">
|
||||
<div class="ant-transfer-list-header">
|
||||
<!---->
|
||||
<!----><span tabindex="-1" role="img" aria-label="down" class="anticon anticon-down ant-dropdown-trigger ant-transfer-list-header-dropdown"><svg focusable="false" class="" data-icon="down" width="1em" height="1em" fill="currentColor" aria-hidden="true" viewBox="64 64 896 896"><path d="M884 256h-75c-5.1 0-9.9 2.5-12.9 6.6L512 654.2 227.9 262.6c-3-4.1-7.8-6.6-12.9-6.6h-75c-6.5 0-10.3 7.4-6.5 12.7l352.6 486.1c12.8 17.6 39 17.6 51.7 0l352.6-486.1c3.9-5.3.1-12.7-6.4-12.7z"></path></svg></span><span class="ant-transfer-list-header-selected"><span>6 items</span><span class="ant-transfer-list-header-title"></span></span>
|
||||
</div>
|
||||
<div class="ant-transfer-list-body">
|
||||
<!---->
|
||||
<div class="ant-transfer-list-body-customize-wrapper">
|
||||
<div class="ant-table-wrapper">
|
||||
<div class="ant-spin-nested-loading">
|
||||
<!---->
|
||||
<div class="ant-spin-container">
|
||||
<div class="ant-table ant-table-small ant-table-scroll-position-left">
|
||||
<!---->
|
||||
<div class="ant-table-content">
|
||||
<!---->
|
||||
<div class="ant-table-body">
|
||||
<table class="">
|
||||
<colgroup>
|
||||
<col data-key="selection-column" class="ant-table-selection-col">
|
||||
<col data-key="title">
|
||||
</colgroup>
|
||||
<thead class="ant-table-thead">
|
||||
<tr>
|
||||
<th colstart="0" colspan="1" colend="0" rowspan="1" class="ant-table-selection-column"><span class="ant-table-header-column"><div><span class="ant-table-column-title"><div class="ant-table-selection"><label class="ant-checkbox-wrapper"><span class="ant-checkbox"><input type="checkbox" class="ant-checkbox-input"><span class="ant-checkbox-inner"></span></span>
|
||||
<!----></label>
|
||||
<div class="ant-table-selection"><label class="ant-checkbox-wrapper"><span class="ant-checkbox"><input type="checkbox" class="ant-checkbox-input"><span class="ant-checkbox-inner"></span></span>
|
||||
<!---->
|
||||
</label>
|
||||
<!---->
|
||||
</div>
|
||||
</th>
|
||||
<th class="ant-table-cell" colstart="1" colend="1">
|
||||
<!---->Name
|
||||
</th>
|
||||
<th class="ant-table-cell" colstart="2" colend="2">
|
||||
<!---->Description
|
||||
</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody class="ant-table-tbody">
|
||||
<!---->
|
||||
</div></span><span class="ant-table-column-sorter"><!----></span>
|
||||
</div></span>
|
||||
<!---->
|
||||
</th>
|
||||
<th colstart="1" colspan="1" colend="1" rowspan="1" class=""><span class="ant-table-header-column"><div><span class="ant-table-column-title">Name</span><span class="ant-table-column-sorter"><!----></span>
|
||||
</div></span>
|
||||
<!---->
|
||||
</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody class="ant-table-tbody">
|
||||
<tr class="ant-table-row ant-table-row-level-0" data-row-key="2">
|
||||
<td class="ant-table-selection-column">
|
||||
<tr data-row-key="0" class="ant-table-row ant-table-row-level-0">
|
||||
<td class="ant-table-cell ant-table-cell-with-append ant-table-selection-column">
|
||||
<!----><label class="ant-checkbox-wrapper ant-checkbox-wrapper-disabled"><span class="ant-checkbox ant-checkbox-disabled"><input type="checkbox" disabled="" class="ant-checkbox-input"><span class="ant-checkbox-inner"></span></span>
|
||||
<!---->
|
||||
</label>
|
||||
</td>
|
||||
<td class="ant-table-cell ant-table-cell-with-append">
|
||||
<!---->content1
|
||||
</td>
|
||||
<td class="ant-table-cell ant-table-cell-with-append">
|
||||
<!---->description of content1
|
||||
</td>
|
||||
</tr>
|
||||
<!---->
|
||||
<tr data-row-key="1" class="ant-table-row ant-table-row-level-0">
|
||||
<td class="ant-table-cell ant-table-cell-with-append ant-table-selection-column">
|
||||
<!----><label class="ant-checkbox-wrapper"><span class="ant-checkbox"><input type="checkbox" class="ant-checkbox-input"><span class="ant-checkbox-inner"></span></span>
|
||||
<!---->
|
||||
</label>
|
||||
</td>
|
||||
<td class="ant-table-cell ant-table-cell-with-append">
|
||||
<!---->content2
|
||||
</td>
|
||||
<td class="ant-table-cell ant-table-cell-with-append">
|
||||
<!---->description of content2
|
||||
</td>
|
||||
</tr>
|
||||
<!---->
|
||||
<tr data-row-key="3" class="ant-table-row ant-table-row-level-0">
|
||||
<td class="ant-table-cell ant-table-cell-with-append ant-table-selection-column">
|
||||
<!----><label class="ant-checkbox-wrapper"><span class="ant-checkbox"><input type="checkbox" class="ant-checkbox-input"><span class="ant-checkbox-inner"></span></span>
|
||||
<!---->
|
||||
</label>
|
||||
</td>
|
||||
<td class="ant-table-cell ant-table-cell-with-append">
|
||||
<!---->content4
|
||||
</td>
|
||||
<td class="ant-table-cell ant-table-cell-with-append">
|
||||
<!---->description of content4
|
||||
</td>
|
||||
</tr>
|
||||
<!---->
|
||||
<tr data-row-key="4" class="ant-table-row ant-table-row-level-0">
|
||||
<td class="ant-table-cell ant-table-cell-with-append ant-table-selection-column">
|
||||
<!----><label class="ant-checkbox-wrapper ant-checkbox-wrapper-disabled"><span class="ant-checkbox ant-checkbox-disabled"><input type="checkbox" disabled="" class="ant-checkbox-input"><span class="ant-checkbox-inner"></span></span>
|
||||
<!---->
|
||||
</label>
|
||||
</td>
|
||||
<td class="ant-table-cell ant-table-cell-with-append">
|
||||
<!---->content5
|
||||
</td>
|
||||
<td class="ant-table-cell ant-table-cell-with-append">
|
||||
<!---->description of content5
|
||||
</td>
|
||||
</tr>
|
||||
<!---->
|
||||
<tr data-row-key="6" class="ant-table-row ant-table-row-level-0">
|
||||
<td class="ant-table-cell ant-table-cell-with-append ant-table-selection-column">
|
||||
<!----><label class="ant-checkbox-wrapper"><span class="ant-checkbox"><input type="checkbox" class="ant-checkbox-input"><span class="ant-checkbox-inner"></span></span>
|
||||
<!---->
|
||||
</label>
|
||||
</td>
|
||||
<td class="ant-table-cell ant-table-cell-with-append">
|
||||
<!---->content7
|
||||
</td>
|
||||
<td class="ant-table-cell ant-table-cell-with-append">
|
||||
<!---->description of content7
|
||||
</td>
|
||||
</tr>
|
||||
<!---->
|
||||
<tr data-row-key="7" class="ant-table-row ant-table-row-level-0">
|
||||
<td class="ant-table-cell ant-table-cell-with-append ant-table-selection-column">
|
||||
<!----><label class="ant-checkbox-wrapper"><span class="ant-checkbox"><input type="checkbox" class="ant-checkbox-input"><span class="ant-checkbox-inner"></span></span>
|
||||
<!---->
|
||||
</label>
|
||||
</td>
|
||||
<td class="ant-table-cell ant-table-cell-with-append">
|
||||
<!---->content8
|
||||
</td>
|
||||
<td class="ant-table-cell ant-table-cell-with-append">
|
||||
<!---->description of content8
|
||||
</td>
|
||||
</tr>
|
||||
<!---->
|
||||
<tr data-row-key="9" class="ant-table-row ant-table-row-level-0">
|
||||
<td class="ant-table-cell ant-table-cell-with-append ant-table-selection-column">
|
||||
<!----><label class="ant-checkbox-wrapper"><span class="ant-checkbox"><input type="checkbox" class="ant-checkbox-input"><span class="ant-checkbox-inner"></span></span>
|
||||
<!---->
|
||||
</label>
|
||||
</td>
|
||||
<td class="ant-table-cell ant-table-cell-with-append">
|
||||
<!---->content10
|
||||
</td>
|
||||
<td class="ant-table-cell ant-table-cell-with-append">
|
||||
<!---->description of content10
|
||||
</td>
|
||||
</tr>
|
||||
<!---->
|
||||
<tr data-row-key="10" class="ant-table-row ant-table-row-level-0">
|
||||
<td class="ant-table-cell ant-table-cell-with-append ant-table-selection-column">
|
||||
<!----><label class="ant-checkbox-wrapper"><span class="ant-checkbox"><input type="checkbox" class="ant-checkbox-input"><span class="ant-checkbox-inner"></span></span>
|
||||
<!---->
|
||||
</label>
|
||||
</td>
|
||||
<td class="ant-table-cell ant-table-cell-with-append">
|
||||
<!---->content11
|
||||
</td>
|
||||
<td class="ant-table-cell ant-table-cell-with-append">
|
||||
<!---->description of content11
|
||||
</td>
|
||||
</tr>
|
||||
<!---->
|
||||
<tr data-row-key="12" class="ant-table-row ant-table-row-level-0">
|
||||
<td class="ant-table-cell ant-table-cell-with-append ant-table-selection-column">
|
||||
<!----><label class="ant-checkbox-wrapper ant-checkbox-wrapper-disabled"><span class="ant-checkbox ant-checkbox-disabled"><input type="checkbox" disabled="" class="ant-checkbox-input"><span class="ant-checkbox-inner"></span></span>
|
||||
<!---->
|
||||
</label>
|
||||
</td>
|
||||
<td class="ant-table-cell ant-table-cell-with-append">
|
||||
<!---->content13
|
||||
</td>
|
||||
<td class="ant-table-cell ant-table-cell-with-append">
|
||||
<!---->description of content13
|
||||
</td>
|
||||
</tr>
|
||||
<!---->
|
||||
<tr data-row-key="13" class="ant-table-row ant-table-row-level-0">
|
||||
<td class="ant-table-cell ant-table-cell-with-append ant-table-selection-column">
|
||||
<!----><label class="ant-checkbox-wrapper"><span class="ant-checkbox"><input type="checkbox" class="ant-checkbox-input"><span class="ant-checkbox-inner"></span></span>
|
||||
<!---->
|
||||
</label>
|
||||
</td>
|
||||
<td class="ant-table-cell ant-table-cell-with-append">
|
||||
<!---->content14
|
||||
</td>
|
||||
<td class="ant-table-cell ant-table-cell-with-append">
|
||||
<!---->description of content14
|
||||
</td>
|
||||
</tr>
|
||||
<!---->
|
||||
</tbody>
|
||||
<!---->
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
<!---->
|
||||
<!----><span><label class="ant-checkbox-wrapper"><span class="ant-checkbox"><input type="checkbox" class="ant-checkbox-input"><span class="ant-checkbox-inner"></span></span>
|
||||
<!----></label></span>
|
||||
</td>
|
||||
<td class="">
|
||||
</div>
|
||||
<ul unselectable="on" class="ant-pagination mini ant-table-pagination ant-table-pagination-right">
|
||||
<!---->
|
||||
<!---->content3
|
||||
</td>
|
||||
</tr>
|
||||
<tr class="ant-table-row ant-table-row-level-0" data-row-key="5">
|
||||
<td class="ant-table-selection-column">
|
||||
<li title="Previous Page" class="ant-pagination-prev ant-pagination-disabled" aria-disabled="true"><button class="ant-pagination-item-link" type="button" tabindex="-1" disabled=""><span role="img" aria-label="left" class="anticon anticon-left"><svg focusable="false" class="" data-icon="left" width="1em" height="1em" fill="currentColor" aria-hidden="true" viewBox="64 64 896 896"><path d="M724 218.3V141c0-6.7-7.7-10.4-12.9-6.3L260.3 486.8a31.86 31.86 0 000 50.3l450.8 352.1c5.3 4.1 12.9.4 12.9-6.3v-77.3c0-4.9-2.3-9.6-6.1-12.6l-360-281 360-281.1c3.8-3 6.1-7.7 6.1-12.6z"></path></svg></span></button></li>
|
||||
<li title="1" tabindex="0" class="ant-pagination-item ant-pagination-item-1 ant-pagination-item-active"><a rel="nofollow">1</a></li>
|
||||
<li title="2" tabindex="0" class="ant-pagination-item ant-pagination-item-2"><a rel="nofollow">2</a></li>
|
||||
<li title="Next Page" tabindex="0" class="ant-pagination-next" aria-disabled="false"><button class="ant-pagination-item-link" type="button" tabindex="-1"><span role="img" aria-label="right" class="anticon anticon-right"><svg focusable="false" class="" data-icon="right" width="1em" height="1em" fill="currentColor" aria-hidden="true" viewBox="64 64 896 896"><path d="M765.7 486.8L314.9 134.7A7.97 7.97 0 00302 141v77.3c0 4.9 2.3 9.6 6.1 12.6l360 281.1-360 281.1c-3.9 3-6.1 7.7-6.1 12.6V883c0 6.7 7.7 10.4 12.9 6.3l450.8-352.1a31.96 31.96 0 000-50.4z"></path></svg></span></button></li>
|
||||
<!---->
|
||||
<!----><span><label class="ant-checkbox-wrapper"><span class="ant-checkbox"><input type="checkbox" class="ant-checkbox-input"><span class="ant-checkbox-inner"></span></span>
|
||||
<!----></label></span>
|
||||
</td>
|
||||
<td class="">
|
||||
<!---->
|
||||
<!---->content6
|
||||
</td>
|
||||
</tr>
|
||||
<tr class="ant-table-row ant-table-row-level-0" data-row-key="8">
|
||||
<td class="ant-table-selection-column">
|
||||
<!---->
|
||||
<!----><span><label class="ant-checkbox-wrapper ant-checkbox-wrapper-disabled"><span class="ant-checkbox ant-checkbox-disabled"><input type="checkbox" disabled="" class="ant-checkbox-input"><span class="ant-checkbox-inner"></span></span>
|
||||
<!----></label></span>
|
||||
</td>
|
||||
<td class="">
|
||||
<!---->
|
||||
<!---->content9
|
||||
</td>
|
||||
</tr>
|
||||
<tr class="ant-table-row ant-table-row-level-0" data-row-key="11">
|
||||
<td class="ant-table-selection-column">
|
||||
<!---->
|
||||
<!----><span><label class="ant-checkbox-wrapper"><span class="ant-checkbox"><input type="checkbox" class="ant-checkbox-input"><span class="ant-checkbox-inner"></span></span>
|
||||
<!----></label></span>
|
||||
</td>
|
||||
<td class="">
|
||||
<!---->
|
||||
<!---->content12
|
||||
</td>
|
||||
</tr>
|
||||
<tr class="ant-table-row ant-table-row-level-0" data-row-key="14">
|
||||
<td class="ant-table-selection-column">
|
||||
<!---->
|
||||
<!----><span><label class="ant-checkbox-wrapper"><span class="ant-checkbox"><input type="checkbox" class="ant-checkbox-input"><span class="ant-checkbox-inner"></span></span>
|
||||
<!----></label></span>
|
||||
</td>
|
||||
<td class="">
|
||||
<!---->
|
||||
<!---->content15
|
||||
</td>
|
||||
</tr>
|
||||
<tr class="ant-table-row ant-table-row-level-0" data-row-key="17">
|
||||
<td class="ant-table-selection-column">
|
||||
<!---->
|
||||
<!----><span><label class="ant-checkbox-wrapper"><span class="ant-checkbox"><input type="checkbox" class="ant-checkbox-input"><span class="ant-checkbox-inner"></span></span>
|
||||
<!----></label></span>
|
||||
</td>
|
||||
<td class="">
|
||||
<!---->
|
||||
<!---->content18
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!---->
|
||||
<!---->
|
||||
</div>
|
||||
</div>
|
||||
<ul unselectable="on" class="ant-pagination mini ant-table-pagination">
|
||||
<!---->
|
||||
<li title="Previous Page" class="ant-pagination-prev ant-pagination-disabled" aria-disabled="true"><button class="ant-pagination-item-link" type="button" tabindex="-1" disabled=""><span role="img" aria-label="left" class="anticon anticon-left"><svg focusable="false" class="" data-icon="left" width="1em" height="1em" fill="currentColor" aria-hidden="true" viewBox="64 64 896 896"><path d="M724 218.3V141c0-6.7-7.7-10.4-12.9-6.3L260.3 486.8a31.86 31.86 0 000 50.3l450.8 352.1c5.3 4.1 12.9.4 12.9-6.3v-77.3c0-4.9-2.3-9.6-6.1-12.6l-360-281 360-281.1c3.8-3 6.1-7.7 6.1-12.6z"></path></svg></span></button></li>
|
||||
<li title="1" tabindex="0" class="ant-pagination-item ant-pagination-item-1 ant-pagination-item-active"><a rel="nofollow">1</a></li>
|
||||
<li title="Next Page" class="ant-pagination-next ant-pagination-disabled" aria-disabled="true"><button class="ant-pagination-item-link" type="button" tabindex="-1" disabled=""><span role="img" aria-label="right" class="anticon anticon-right"><svg focusable="false" class="" data-icon="right" width="1em" height="1em" fill="currentColor" aria-hidden="true" viewBox="64 64 896 896"><path d="M765.7 486.8L314.9 134.7A7.97 7.97 0 00302 141v77.3c0 4.9 2.3 9.6 6.1 12.6l360 281.1-360 281.1c-3.9 3-6.1 7.7-6.1 12.6V883c0 6.7 7.7 10.4 12.9 6.3l450.8-352.1a31.96 31.96 0 000-50.4z"></path></svg></span></button></li>
|
||||
<!---->
|
||||
</ul>
|
||||
<!---->
|
||||
</div>
|
||||
</div>
|
||||
<div class="ant-transfer-operation"><button disabled="" class="ant-btn ant-btn-primary ant-btn-sm ant-btn-icon-only" type="button"><span role="img" aria-label="right" class="anticon anticon-right"><svg focusable="false" class="" data-icon="right" width="1em" height="1em" fill="currentColor" aria-hidden="true" viewBox="64 64 896 896"><path d="M765.7 486.8L314.9 134.7A7.97 7.97 0 00302 141v77.3c0 4.9 2.3 9.6 6.1 12.6l360 281.1-360 281.1c-3.9 3-6.1 7.7-6.1 12.6V883c0 6.7 7.7 10.4 12.9 6.3l450.8-352.1a31.96 31.96 0 000-50.4z"></path></svg></span></button><button disabled="" class="ant-btn ant-btn-primary ant-btn-sm ant-btn-icon-only" type="button"><span role="img" aria-label="left" class="anticon anticon-left"><svg focusable="false" class="" data-icon="left" width="1em" height="1em" fill="currentColor" aria-hidden="true" viewBox="64 64 896 896"><path d="M724 218.3V141c0-6.7-7.7-10.4-12.9-6.3L260.3 486.8a31.86 31.86 0 000 50.3l450.8 352.1c5.3 4.1 12.9.4 12.9-6.3v-77.3c0-4.9-2.3-9.6-6.1-12.6l-360-281 360-281.1c3.8-3 6.1-7.7 6.1-12.6z"></path></svg></span></button></div>
|
||||
<div class="ant-transfer-list">
|
||||
<div class="ant-transfer-list-header">
|
||||
<!---->
|
||||
<!----><span tabindex="-1" role="img" aria-label="down" class="anticon anticon-down ant-dropdown-trigger ant-transfer-list-header-dropdown"><svg focusable="false" class="" data-icon="down" width="1em" height="1em" fill="currentColor" aria-hidden="true" viewBox="64 64 896 896"><path d="M884 256h-75c-5.1 0-9.9 2.5-12.9 6.6L512 654.2 227.9 262.6c-3-4.1-7.8-6.6-12.9-6.6h-75c-6.5 0-10.3 7.4-6.5 12.7l352.6 486.1c12.8 17.6 39 17.6 51.7 0l352.6-486.1c3.9-5.3.1-12.7-6.4-12.7z"></path></svg></span><span class="ant-transfer-list-header-selected"><span>6 items</span><span class="ant-transfer-list-header-title"></span></span>
|
||||
</div>
|
||||
<div class="ant-transfer-list-body">
|
||||
<!---->
|
||||
<div class="ant-transfer-list-body-customize-wrapper">
|
||||
<div class="ant-table-wrapper">
|
||||
<div class="ant-spin-nested-loading">
|
||||
<!---->
|
||||
<div class="ant-spin-container">
|
||||
<div class="ant-table ant-table-small">
|
||||
<!---->
|
||||
<div class="ant-table-container">
|
||||
<div class="ant-table-content">
|
||||
<table style="table-layout: auto;">
|
||||
<colgroup>
|
||||
<col class="ant-table-selection-col">
|
||||
</colgroup>
|
||||
<thead class="ant-table-thead">
|
||||
<tr>
|
||||
<th class="ant-table-cell ant-table-selection-column" colstart="0" colend="0">
|
||||
<!---->
|
||||
<div class="ant-table-selection"><label class="ant-checkbox-wrapper"><span class="ant-checkbox"><input type="checkbox" class="ant-checkbox-input"><span class="ant-checkbox-inner"></span></span>
|
||||
<!---->
|
||||
</label>
|
||||
<!---->
|
||||
</div>
|
||||
</th>
|
||||
<th class="ant-table-cell" colstart="1" colend="1">
|
||||
<!---->Name
|
||||
</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody class="ant-table-tbody">
|
||||
<!---->
|
||||
<tr data-row-key="2" class="ant-table-row ant-table-row-level-0">
|
||||
<td class="ant-table-cell ant-table-cell-with-append ant-table-selection-column">
|
||||
<!----><label class="ant-checkbox-wrapper"><span class="ant-checkbox"><input type="checkbox" class="ant-checkbox-input"><span class="ant-checkbox-inner"></span></span>
|
||||
<!---->
|
||||
</label>
|
||||
</td>
|
||||
<td class="ant-table-cell ant-table-cell-with-append">
|
||||
<!---->content3
|
||||
</td>
|
||||
</tr>
|
||||
<!---->
|
||||
<tr data-row-key="5" class="ant-table-row ant-table-row-level-0">
|
||||
<td class="ant-table-cell ant-table-cell-with-append ant-table-selection-column">
|
||||
<!----><label class="ant-checkbox-wrapper"><span class="ant-checkbox"><input type="checkbox" class="ant-checkbox-input"><span class="ant-checkbox-inner"></span></span>
|
||||
<!---->
|
||||
</label>
|
||||
</td>
|
||||
<td class="ant-table-cell ant-table-cell-with-append">
|
||||
<!---->content6
|
||||
</td>
|
||||
</tr>
|
||||
<!---->
|
||||
<tr data-row-key="8" class="ant-table-row ant-table-row-level-0">
|
||||
<td class="ant-table-cell ant-table-cell-with-append ant-table-selection-column">
|
||||
<!----><label class="ant-checkbox-wrapper ant-checkbox-wrapper-disabled"><span class="ant-checkbox ant-checkbox-disabled"><input type="checkbox" disabled="" class="ant-checkbox-input"><span class="ant-checkbox-inner"></span></span>
|
||||
<!---->
|
||||
</label>
|
||||
</td>
|
||||
<td class="ant-table-cell ant-table-cell-with-append">
|
||||
<!---->content9
|
||||
</td>
|
||||
</tr>
|
||||
<!---->
|
||||
<tr data-row-key="11" class="ant-table-row ant-table-row-level-0">
|
||||
<td class="ant-table-cell ant-table-cell-with-append ant-table-selection-column">
|
||||
<!----><label class="ant-checkbox-wrapper"><span class="ant-checkbox"><input type="checkbox" class="ant-checkbox-input"><span class="ant-checkbox-inner"></span></span>
|
||||
<!---->
|
||||
</label>
|
||||
</td>
|
||||
<td class="ant-table-cell ant-table-cell-with-append">
|
||||
<!---->content12
|
||||
</td>
|
||||
</tr>
|
||||
<!---->
|
||||
<tr data-row-key="14" class="ant-table-row ant-table-row-level-0">
|
||||
<td class="ant-table-cell ant-table-cell-with-append ant-table-selection-column">
|
||||
<!----><label class="ant-checkbox-wrapper"><span class="ant-checkbox"><input type="checkbox" class="ant-checkbox-input"><span class="ant-checkbox-inner"></span></span>
|
||||
<!---->
|
||||
</label>
|
||||
</td>
|
||||
<td class="ant-table-cell ant-table-cell-with-append">
|
||||
<!---->content15
|
||||
</td>
|
||||
</tr>
|
||||
<!---->
|
||||
<tr data-row-key="17" class="ant-table-row ant-table-row-level-0">
|
||||
<td class="ant-table-cell ant-table-cell-with-append ant-table-selection-column">
|
||||
<!----><label class="ant-checkbox-wrapper"><span class="ant-checkbox"><input type="checkbox" class="ant-checkbox-input"><span class="ant-checkbox-inner"></span></span>
|
||||
<!---->
|
||||
</label>
|
||||
</td>
|
||||
<td class="ant-table-cell ant-table-cell-with-append">
|
||||
<!---->content18
|
||||
</td>
|
||||
</tr>
|
||||
<!---->
|
||||
</tbody>
|
||||
<!---->
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
<!---->
|
||||
</div>
|
||||
<ul unselectable="on" class="ant-pagination mini ant-table-pagination ant-table-pagination-right">
|
||||
<!---->
|
||||
<li title="Previous Page" class="ant-pagination-prev ant-pagination-disabled" aria-disabled="true"><button class="ant-pagination-item-link" type="button" tabindex="-1" disabled=""><span role="img" aria-label="left" class="anticon anticon-left"><svg focusable="false" class="" data-icon="left" width="1em" height="1em" fill="currentColor" aria-hidden="true" viewBox="64 64 896 896"><path d="M724 218.3V141c0-6.7-7.7-10.4-12.9-6.3L260.3 486.8a31.86 31.86 0 000 50.3l450.8 352.1c5.3 4.1 12.9.4 12.9-6.3v-77.3c0-4.9-2.3-9.6-6.1-12.6l-360-281 360-281.1c3.8-3 6.1-7.7 6.1-12.6z"></path></svg></span></button></li>
|
||||
<li title="1" tabindex="0" class="ant-pagination-item ant-pagination-item-1 ant-pagination-item-active"><a rel="nofollow">1</a></li>
|
||||
<li title="Next Page" class="ant-pagination-next ant-pagination-disabled" aria-disabled="true"><button class="ant-pagination-item-link" type="button" tabindex="-1" disabled=""><span role="img" aria-label="right" class="anticon anticon-right"><svg focusable="false" class="" data-icon="right" width="1em" height="1em" fill="currentColor" aria-hidden="true" viewBox="64 64 896 896"><path d="M765.7 486.8L314.9 134.7A7.97 7.97 0 00302 141v77.3c0 4.9 2.3 9.6 6.1 12.6l360 281.1-360 281.1c-3.9 3-6.1 7.7-6.1 12.6V883c0 6.7 7.7 10.4 12.9 6.3l450.8-352.1a31.96 31.96 0 000-50.4z"></path></svg></span></button></li>
|
||||
<!---->
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!---->
|
||||
</div>
|
||||
</div><button style="margin-top: 16px;" type="button" role="switch" aria-checked="false" class="ant-switch">
|
||||
<!----><span class="ant-switch-inner">disabled</span>
|
||||
</button><button style="margin-top: 16px;" type="button" role="switch" aria-checked="false" class="ant-switch">
|
||||
<!----><span class="ant-switch-inner">showSearch</span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!---->
|
||||
</div>
|
||||
</div><button style="margin-top: 16px;" type="button" role="switch" aria-checked="false" class="ant-switch">
|
||||
<!----><span class="ant-switch-inner">disabled</span>
|
||||
</button><button style="margin-top: 16px;" type="button" role="switch" aria-checked="false" class="ant-switch">
|
||||
<!----><span class="ant-switch-inner">showSearch</span>
|
||||
</button></div>
|
||||
`;
|
||||
|
||||
exports[`renders ./components/transfer/demo/tree-transfer.vue correctly 1`] = `
|
||||
|
|
|
@ -0,0 +1,238 @@
|
|||
import Cell from '../Cell';
|
||||
import { getColumnsKey } from '../utils/valueUtil';
|
||||
import type { CustomizeComponent, GetComponentProps, Key, GetRowKey } from '../interface';
|
||||
import ExpandedRow from './ExpandedRow';
|
||||
import { computed, defineComponent, ref, watchEffect } from 'vue';
|
||||
import { useInjectTable } from '../context/TableContext';
|
||||
import { useInjectBody } from '../context/BodyContext';
|
||||
import classNames from '../../_util/classNames';
|
||||
import { parseStyleText } from '../../_util/props-util';
|
||||
|
||||
export interface BodyRowProps<RecordType> {
|
||||
record: RecordType;
|
||||
index: number;
|
||||
recordKey: Key;
|
||||
expandedKeys: Set<Key>;
|
||||
rowComponent: CustomizeComponent;
|
||||
cellComponent: CustomizeComponent;
|
||||
customRow: GetComponentProps<RecordType>;
|
||||
rowExpandable: (record: RecordType) => boolean;
|
||||
indent?: number;
|
||||
rowKey: Key;
|
||||
getRowKey: GetRowKey<RecordType>;
|
||||
childrenColumnName: string;
|
||||
}
|
||||
|
||||
export default defineComponent<BodyRowProps<unknown>>({
|
||||
name: 'BodyRow',
|
||||
inheritAttrs: false,
|
||||
props: [
|
||||
'record',
|
||||
'index',
|
||||
'recordKey',
|
||||
'expandedKeys',
|
||||
'rowComponent',
|
||||
'cellComponent',
|
||||
'customRow',
|
||||
'rowExpandable',
|
||||
'indent',
|
||||
'rowKey',
|
||||
'getRowKey',
|
||||
'childrenColumnName',
|
||||
] as any,
|
||||
setup(props, { attrs }) {
|
||||
const tableContext = useInjectTable();
|
||||
const bodyContext = useInjectBody();
|
||||
const expandRended = ref(false);
|
||||
|
||||
const expanded = computed(() => props.expandedKeys && props.expandedKeys.has(props.recordKey));
|
||||
|
||||
watchEffect(() => {
|
||||
if (expanded.value) {
|
||||
expandRended.value = true;
|
||||
}
|
||||
});
|
||||
|
||||
const rowSupportExpand = computed(
|
||||
() =>
|
||||
bodyContext.expandableType === 'row' &&
|
||||
(!props.rowExpandable || props.rowExpandable(props.record)),
|
||||
);
|
||||
// Only when row is not expandable and `children` exist in record
|
||||
const nestExpandable = computed(() => bodyContext.expandableType === 'nest');
|
||||
const hasNestChildren = computed(
|
||||
() => props.childrenColumnName && props.record && props.record[props.childrenColumnName],
|
||||
);
|
||||
const mergedExpandable = computed(() => rowSupportExpand.value || nestExpandable.value);
|
||||
|
||||
const onInternalTriggerExpand = (record, event) => {
|
||||
bodyContext.onTriggerExpand(record, event);
|
||||
};
|
||||
|
||||
// =========================== onRow ===========================
|
||||
const additionalProps = computed<Record<string, any>>(
|
||||
() => props.customRow?.(props.record, props.index) || {},
|
||||
);
|
||||
|
||||
const onClick = (event, ...args) => {
|
||||
if (bodyContext.expandRowByClick && mergedExpandable.value) {
|
||||
onInternalTriggerExpand(props.record, event);
|
||||
}
|
||||
|
||||
if (additionalProps.value?.onClick) {
|
||||
additionalProps.value.onClick(event, ...args);
|
||||
}
|
||||
};
|
||||
|
||||
const computeRowClassName = computed(() => {
|
||||
const { record, index, indent } = props;
|
||||
const { rowClassName } = bodyContext;
|
||||
if (typeof rowClassName === 'string') {
|
||||
return rowClassName;
|
||||
} else if (typeof rowClassName === 'function') {
|
||||
return rowClassName(record, index, indent);
|
||||
}
|
||||
return '';
|
||||
});
|
||||
|
||||
const columnsKey = computed(() => getColumnsKey(bodyContext.flattenColumns));
|
||||
|
||||
return () => {
|
||||
const { class: className, style } = attrs as any;
|
||||
const {
|
||||
record,
|
||||
index,
|
||||
rowKey,
|
||||
indent = 0,
|
||||
rowComponent: RowComponent,
|
||||
cellComponent,
|
||||
} = props;
|
||||
const { prefixCls, fixedInfoList, transformCellText } = tableContext;
|
||||
const {
|
||||
fixHeader,
|
||||
fixColumn,
|
||||
horizonScroll,
|
||||
componentWidth,
|
||||
flattenColumns,
|
||||
expandedRowClassName,
|
||||
indentSize,
|
||||
expandIcon,
|
||||
expandedRowRender,
|
||||
expandIconColumnIndex,
|
||||
} = bodyContext;
|
||||
const baseRowNode = (
|
||||
<RowComponent
|
||||
{...additionalProps.value}
|
||||
data-row-key={rowKey}
|
||||
class={classNames(
|
||||
className,
|
||||
`${prefixCls}-row`,
|
||||
`${prefixCls}-row-level-${indent}`,
|
||||
computeRowClassName.value,
|
||||
additionalProps.value.class,
|
||||
)}
|
||||
style={{
|
||||
...style,
|
||||
...parseStyleText(additionalProps.value.style),
|
||||
}}
|
||||
onClick={onClick}
|
||||
>
|
||||
{flattenColumns.map((column, colIndex) => {
|
||||
const { customRender, dataIndex, className: columnClassName } = column;
|
||||
|
||||
const key = columnsKey[colIndex];
|
||||
const fixedInfo = fixedInfoList[colIndex];
|
||||
|
||||
let additionalCellProps;
|
||||
if (column.customCell) {
|
||||
additionalCellProps = column.customCell(record, index);
|
||||
}
|
||||
|
||||
return (
|
||||
<Cell
|
||||
cellType="body"
|
||||
class={columnClassName}
|
||||
ellipsis={column.ellipsis}
|
||||
align={column.align}
|
||||
component={cellComponent}
|
||||
prefixCls={prefixCls}
|
||||
key={key}
|
||||
record={record}
|
||||
index={index}
|
||||
dataIndex={dataIndex}
|
||||
customRender={customRender}
|
||||
{...fixedInfo}
|
||||
additionalProps={additionalCellProps}
|
||||
column={column}
|
||||
transformCellText={transformCellText}
|
||||
v-slots={{
|
||||
// ============= Used for nest expandable =============
|
||||
appendNode: () => {
|
||||
if (colIndex === (expandIconColumnIndex || 0) && nestExpandable.value) {
|
||||
return (
|
||||
<>
|
||||
<span
|
||||
style={{ paddingLeft: `${indentSize * indent}px` }}
|
||||
class={`${prefixCls}-row-indent indent-level-${indent}`}
|
||||
/>
|
||||
{expandIcon({
|
||||
prefixCls,
|
||||
expanded: expanded.value,
|
||||
expandable: hasNestChildren.value,
|
||||
record,
|
||||
onExpand: onInternalTriggerExpand,
|
||||
})}
|
||||
</>
|
||||
);
|
||||
}
|
||||
return null;
|
||||
},
|
||||
}}
|
||||
/>
|
||||
);
|
||||
})}
|
||||
</RowComponent>
|
||||
);
|
||||
|
||||
// ======================== Expand Row =========================
|
||||
let expandRowNode;
|
||||
if (rowSupportExpand.value && (expandRended.value || expanded.value)) {
|
||||
const expandContent = expandedRowRender({
|
||||
record,
|
||||
index,
|
||||
indent: indent + 1,
|
||||
expanded: expanded.value,
|
||||
});
|
||||
const computedExpandedRowClassName =
|
||||
expandedRowClassName && expandedRowClassName(record, index, indent);
|
||||
expandRowNode = (
|
||||
<ExpandedRow
|
||||
expanded={expanded.value}
|
||||
class={classNames(
|
||||
`${prefixCls}-expanded-row`,
|
||||
`${prefixCls}-expanded-row-level-${indent + 1}`,
|
||||
computedExpandedRowClassName,
|
||||
)}
|
||||
prefixCls={prefixCls}
|
||||
fixHeader={fixHeader}
|
||||
fixColumn={fixColumn}
|
||||
horizonScroll={horizonScroll}
|
||||
component={RowComponent}
|
||||
componentWidth={componentWidth}
|
||||
cellComponent={cellComponent}
|
||||
colSpan={flattenColumns.length}
|
||||
>
|
||||
{expandContent}
|
||||
</ExpandedRow>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
{baseRowNode}
|
||||
{expandRowNode}
|
||||
</>
|
||||
);
|
||||
};
|
||||
},
|
||||
});
|
|
@ -0,0 +1,84 @@
|
|||
import type { CustomizeComponent } from '../interface';
|
||||
import Cell from '../Cell';
|
||||
import { defineComponent } from 'vue';
|
||||
import { useInjectTable } from '../context/TableContext';
|
||||
|
||||
export interface ExpandedRowProps {
|
||||
prefixCls: string;
|
||||
component: CustomizeComponent;
|
||||
cellComponent: CustomizeComponent;
|
||||
fixHeader: boolean;
|
||||
fixColumn: boolean;
|
||||
horizonScroll: boolean;
|
||||
componentWidth: number;
|
||||
expanded: boolean;
|
||||
colSpan: number;
|
||||
}
|
||||
|
||||
export default defineComponent<ExpandedRowProps>({
|
||||
name: 'ExpandedRow',
|
||||
inheritAttrs: false,
|
||||
props: [
|
||||
'prefixCls',
|
||||
'component',
|
||||
'cellComponent',
|
||||
'fixHeader',
|
||||
'fixColumn',
|
||||
'horizonScroll',
|
||||
'componentWidth',
|
||||
'expanded',
|
||||
'colSpan',
|
||||
] as any,
|
||||
setup(props, { slots, attrs }) {
|
||||
const tableContext = useInjectTable();
|
||||
return () => {
|
||||
const {
|
||||
prefixCls,
|
||||
component: Component,
|
||||
cellComponent,
|
||||
fixHeader,
|
||||
fixColumn,
|
||||
expanded,
|
||||
componentWidth,
|
||||
colSpan,
|
||||
} = props;
|
||||
|
||||
return (
|
||||
<Component
|
||||
class={attrs.class}
|
||||
style={{
|
||||
display: expanded ? null : 'none',
|
||||
}}
|
||||
>
|
||||
<Cell
|
||||
component={cellComponent}
|
||||
prefixCls={prefixCls}
|
||||
colSpan={colSpan}
|
||||
v-slots={{
|
||||
default: () => {
|
||||
let contentNode: any = slots.default?.();
|
||||
|
||||
if (fixColumn) {
|
||||
contentNode = (
|
||||
<div
|
||||
style={{
|
||||
width: componentWidth - (fixHeader ? tableContext.scrollbarSize : 0),
|
||||
position: 'sticky',
|
||||
left: 0,
|
||||
overflow: 'hidden',
|
||||
}}
|
||||
class={`${prefixCls}-expanded-row-fixed`}
|
||||
>
|
||||
{contentNode}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
return contentNode;
|
||||
},
|
||||
}}
|
||||
></Cell>
|
||||
</Component>
|
||||
);
|
||||
};
|
||||
},
|
||||
});
|
|
@ -0,0 +1,34 @@
|
|||
import { defineComponent, onMounted, ref } from 'vue';
|
||||
import VCResizeObserver from '../../vc-resize-observer';
|
||||
import type { Key } from '../interface';
|
||||
|
||||
export interface MeasureCellProps {
|
||||
columnKey: Key;
|
||||
onColumnResize: (key: Key, width: number) => void;
|
||||
}
|
||||
|
||||
export default defineComponent<MeasureCellProps>({
|
||||
name: 'MeasureCell',
|
||||
props: ['columnKey'] as any,
|
||||
setup(props, { emit }) {
|
||||
const tdRef = ref();
|
||||
onMounted(() => {
|
||||
if (tdRef.value) {
|
||||
emit('columnResize', props.columnKey, tdRef.value.offsetWidth);
|
||||
}
|
||||
});
|
||||
return () => {
|
||||
return (
|
||||
<VCResizeObserver
|
||||
onResize={({ offsetWidth }) => {
|
||||
emit('columnResize', props.columnKey, offsetWidth);
|
||||
}}
|
||||
>
|
||||
<td ref={tdRef} style={{ padding: 0, border: 0, height: 0 }}>
|
||||
<div style={{ height: 0, overflow: 'hidden' }}> </div>
|
||||
</td>
|
||||
</VCResizeObserver>
|
||||
);
|
||||
};
|
||||
},
|
||||
});
|
|
@ -0,0 +1,133 @@
|
|||
import type { GetRowKey, Key, GetComponentProps } from '../interface';
|
||||
import ExpandedRow from './ExpandedRow';
|
||||
import { getColumnsKey } from '../utils/valueUtil';
|
||||
import MeasureCell from './MeasureCell';
|
||||
import BodyRow from './BodyRow';
|
||||
import useFlattenRecords from '../hooks/useFlattenRecords';
|
||||
import { defineComponent, toRef } from 'vue';
|
||||
import { useInjectResize } from '../context/ResizeContext';
|
||||
import { useInjectTable } from '../context/TableContext';
|
||||
import { useInjectBody } from '../context/BodyContext';
|
||||
|
||||
export interface BodyProps<RecordType> {
|
||||
data: RecordType[];
|
||||
getRowKey: GetRowKey<RecordType>;
|
||||
measureColumnWidth: boolean;
|
||||
expandedKeys: Set<Key>;
|
||||
customRow: GetComponentProps<RecordType>;
|
||||
rowExpandable: (record: RecordType) => boolean;
|
||||
childrenColumnName: string;
|
||||
}
|
||||
|
||||
export default defineComponent<BodyProps<any>>({
|
||||
name: 'Body',
|
||||
props: [
|
||||
'data',
|
||||
'getRowKey',
|
||||
'measureColumnWidth',
|
||||
'expandedKeys',
|
||||
'customRow',
|
||||
'rowExpandable',
|
||||
'childrenColumnName',
|
||||
] as any,
|
||||
slots: ['emptyNode'],
|
||||
setup(props, { slots }) {
|
||||
const resizeContext = useInjectResize();
|
||||
const tableContext = useInjectTable();
|
||||
const bodyContext = useInjectBody();
|
||||
|
||||
const flattenData = useFlattenRecords(
|
||||
toRef(props, 'data'),
|
||||
toRef(props, 'childrenColumnName'),
|
||||
toRef(props, 'expandedKeys'),
|
||||
toRef(props, 'getRowKey'),
|
||||
);
|
||||
|
||||
return () => {
|
||||
const {
|
||||
data,
|
||||
getRowKey,
|
||||
measureColumnWidth,
|
||||
expandedKeys,
|
||||
customRow,
|
||||
rowExpandable,
|
||||
childrenColumnName,
|
||||
} = props;
|
||||
const { onColumnResize } = resizeContext;
|
||||
const { prefixCls, getComponent } = tableContext;
|
||||
const { fixHeader, horizonScroll, flattenColumns, componentWidth } = bodyContext;
|
||||
const WrapperComponent = getComponent(['body', 'wrapper'], 'tbody');
|
||||
const trComponent = getComponent(['body', 'row'], 'tr');
|
||||
const tdComponent = getComponent(['body', 'cell'], 'td');
|
||||
|
||||
let rows;
|
||||
if (data.length) {
|
||||
rows = flattenData.value.map((item, index) => {
|
||||
const { record, indent } = item;
|
||||
|
||||
const key = getRowKey(record, index);
|
||||
|
||||
return (
|
||||
<BodyRow
|
||||
key={key}
|
||||
rowKey={key}
|
||||
record={record}
|
||||
recordKey={key}
|
||||
index={index}
|
||||
rowComponent={trComponent}
|
||||
cellComponent={tdComponent}
|
||||
expandedKeys={expandedKeys}
|
||||
customRow={customRow}
|
||||
getRowKey={getRowKey}
|
||||
rowExpandable={rowExpandable}
|
||||
childrenColumnName={childrenColumnName}
|
||||
indent={indent}
|
||||
/>
|
||||
);
|
||||
});
|
||||
} else {
|
||||
rows = (
|
||||
<ExpandedRow
|
||||
expanded
|
||||
class={`${prefixCls}-placeholder`}
|
||||
prefixCls={prefixCls}
|
||||
fixHeader={fixHeader}
|
||||
fixColumn={horizonScroll}
|
||||
horizonScroll={horizonScroll}
|
||||
component={trComponent}
|
||||
componentWidth={componentWidth}
|
||||
cellComponent={tdComponent}
|
||||
colSpan={flattenColumns.length}
|
||||
>
|
||||
{slots.emptyNode?.()}
|
||||
</ExpandedRow>
|
||||
);
|
||||
}
|
||||
|
||||
const columnsKey = getColumnsKey(flattenColumns);
|
||||
|
||||
return (
|
||||
<WrapperComponent class={`${prefixCls}-tbody`}>
|
||||
{/* Measure body column width with additional hidden col */}
|
||||
{measureColumnWidth && (
|
||||
<tr
|
||||
aria-hidden="true"
|
||||
class={`${prefixCls}-measure-row`}
|
||||
style={{ height: 0, fontSize: 0 }}
|
||||
>
|
||||
{columnsKey.map(columnKey => (
|
||||
<MeasureCell
|
||||
key={columnKey}
|
||||
columnKey={columnKey}
|
||||
onColumnResize={onColumnResize}
|
||||
/>
|
||||
))}
|
||||
</tr>
|
||||
)}
|
||||
|
||||
{rows}
|
||||
</WrapperComponent>
|
||||
);
|
||||
};
|
||||
},
|
||||
});
|
|
@ -0,0 +1,276 @@
|
|||
import classNames from '../../_util/classNames';
|
||||
import { flattenChildren, isValidElement, parseStyleText } from '../../_util/props-util';
|
||||
import { CSSProperties, HTMLAttributes } from 'vue';
|
||||
import { defineComponent, isVNode } from 'vue';
|
||||
|
||||
import type {
|
||||
DataIndex,
|
||||
ColumnType,
|
||||
RenderedCell,
|
||||
CustomizeComponent,
|
||||
CellType,
|
||||
DefaultRecordType,
|
||||
AlignType,
|
||||
CellEllipsisType,
|
||||
TransformCellText,
|
||||
} from '../interface';
|
||||
import { getPathValue, validateValue } from '../utils/valueUtil';
|
||||
import { useInjectSlots } from '../../table/context';
|
||||
import { INTERNAL_COL_DEFINE } from '../utils/legacyUtil';
|
||||
|
||||
function isRenderCell<RecordType = DefaultRecordType>(
|
||||
data: RenderedCell<RecordType>,
|
||||
): data is RenderedCell<RecordType> {
|
||||
return data && typeof data === 'object' && !Array.isArray(data) && !isValidElement(data);
|
||||
}
|
||||
|
||||
export interface CellProps<RecordType = DefaultRecordType> {
|
||||
prefixCls?: string;
|
||||
record?: RecordType;
|
||||
/** `record` index. Not `column` index. */
|
||||
index?: number;
|
||||
dataIndex?: DataIndex;
|
||||
customRender?: ColumnType<RecordType>['customRender'];
|
||||
component?: CustomizeComponent;
|
||||
colSpan?: number;
|
||||
rowSpan?: number;
|
||||
ellipsis?: CellEllipsisType;
|
||||
align?: AlignType;
|
||||
|
||||
// Fixed
|
||||
fixLeft?: number | false;
|
||||
fixRight?: number | false;
|
||||
firstFixLeft?: boolean;
|
||||
lastFixLeft?: boolean;
|
||||
firstFixRight?: boolean;
|
||||
lastFixRight?: boolean;
|
||||
|
||||
// Additional
|
||||
/** @private Used for `expandable` with nest tree */
|
||||
appendNode?: any;
|
||||
|
||||
additionalProps?: HTMLAttributes;
|
||||
|
||||
rowType?: 'header' | 'body' | 'footer';
|
||||
|
||||
isSticky?: boolean;
|
||||
|
||||
column?: ColumnType<RecordType>;
|
||||
|
||||
cellType?: 'header' | 'body';
|
||||
|
||||
transformCellText?: TransformCellText<RecordType>;
|
||||
}
|
||||
export default defineComponent<CellProps>({
|
||||
name: 'Cell',
|
||||
props: [
|
||||
'prefixCls',
|
||||
'record',
|
||||
'index',
|
||||
'dataIndex',
|
||||
'customRender',
|
||||
'component',
|
||||
'colSpan',
|
||||
'rowSpan',
|
||||
'fixLeft',
|
||||
'fixRight',
|
||||
'firstFixLeft',
|
||||
'lastFixLeft',
|
||||
'firstFixRight',
|
||||
'lastFixRight',
|
||||
'appendNode',
|
||||
'additionalProps',
|
||||
'ellipsis',
|
||||
'align',
|
||||
'rowType',
|
||||
'isSticky',
|
||||
'column',
|
||||
'cellType',
|
||||
'transformCellText',
|
||||
] as any,
|
||||
slots: ['appendNode'],
|
||||
setup(props, { slots }) {
|
||||
const contextSlots = useInjectSlots();
|
||||
return () => {
|
||||
const {
|
||||
prefixCls,
|
||||
record,
|
||||
index,
|
||||
dataIndex,
|
||||
customRender,
|
||||
component: Component = 'td',
|
||||
colSpan,
|
||||
rowSpan,
|
||||
fixLeft,
|
||||
fixRight,
|
||||
firstFixLeft,
|
||||
lastFixLeft,
|
||||
firstFixRight,
|
||||
lastFixRight,
|
||||
appendNode = slots.appendNode?.(),
|
||||
additionalProps = {},
|
||||
ellipsis,
|
||||
align,
|
||||
rowType,
|
||||
isSticky,
|
||||
column = {},
|
||||
cellType,
|
||||
} = props;
|
||||
const cellPrefixCls = `${prefixCls}-cell`;
|
||||
|
||||
// ==================== Child Node ====================
|
||||
let cellProps: CellType;
|
||||
let childNode;
|
||||
const children = slots.default?.();
|
||||
if (validateValue(children) || cellType === 'header') {
|
||||
childNode = children;
|
||||
} else {
|
||||
const value = getPathValue(record, dataIndex);
|
||||
|
||||
// Customize render node
|
||||
childNode = value;
|
||||
if (customRender) {
|
||||
const renderData = customRender({
|
||||
text: value,
|
||||
value,
|
||||
record,
|
||||
index,
|
||||
column: column.__originColumn__,
|
||||
});
|
||||
|
||||
if (isRenderCell(renderData)) {
|
||||
childNode = renderData.children;
|
||||
cellProps = renderData.props;
|
||||
} else {
|
||||
childNode = renderData;
|
||||
}
|
||||
}
|
||||
|
||||
if (
|
||||
!(INTERNAL_COL_DEFINE in column) &&
|
||||
cellType === 'body' &&
|
||||
contextSlots.value.bodyCell &&
|
||||
!column.slots?.customRender
|
||||
) {
|
||||
childNode = flattenChildren(
|
||||
contextSlots.value.bodyCell({
|
||||
text: value,
|
||||
value,
|
||||
record,
|
||||
index,
|
||||
column: column.__originColumn__,
|
||||
}) as any,
|
||||
);
|
||||
}
|
||||
/** maybe we should @deprecated */
|
||||
if (props.transformCellText) {
|
||||
childNode = props.transformCellText({
|
||||
text: childNode,
|
||||
record,
|
||||
index,
|
||||
column: column.__originColumn__,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// Not crash if final `childNode` is not validate ReactNode
|
||||
if (
|
||||
typeof childNode === 'object' &&
|
||||
!Array.isArray(childNode) &&
|
||||
!isValidElement(childNode)
|
||||
) {
|
||||
childNode = null;
|
||||
}
|
||||
|
||||
if (ellipsis && (lastFixLeft || firstFixRight)) {
|
||||
childNode = <span class={`${cellPrefixCls}-content`}>{childNode}</span>;
|
||||
}
|
||||
|
||||
if (Array.isArray(childNode) && childNode.length === 1) {
|
||||
childNode = childNode[0];
|
||||
}
|
||||
const {
|
||||
colSpan: cellColSpan,
|
||||
rowSpan: cellRowSpan,
|
||||
style: cellStyle,
|
||||
class: cellClassName,
|
||||
...restCellProps
|
||||
} = cellProps || {};
|
||||
const mergedColSpan = cellColSpan !== undefined ? cellColSpan : colSpan;
|
||||
const mergedRowSpan = cellRowSpan !== undefined ? cellRowSpan : rowSpan;
|
||||
|
||||
if (mergedColSpan === 0 || mergedRowSpan === 0) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// ====================== Fixed =======================
|
||||
const fixedStyle: CSSProperties = {};
|
||||
const isFixLeft = typeof fixLeft === 'number';
|
||||
const isFixRight = typeof fixRight === 'number';
|
||||
|
||||
if (isFixLeft) {
|
||||
fixedStyle.position = 'sticky';
|
||||
fixedStyle.left = `${fixLeft}px`;
|
||||
}
|
||||
if (isFixRight) {
|
||||
fixedStyle.position = 'sticky';
|
||||
|
||||
fixedStyle.right = `${fixRight}px`;
|
||||
}
|
||||
|
||||
// ====================== Align =======================
|
||||
const alignStyle: CSSProperties = {};
|
||||
if (align) {
|
||||
alignStyle.textAlign = align;
|
||||
}
|
||||
|
||||
// ====================== Render ======================
|
||||
let title: string;
|
||||
const ellipsisConfig: CellEllipsisType = ellipsis === true ? { showTitle: true } : ellipsis;
|
||||
if (ellipsisConfig && (ellipsisConfig.showTitle || rowType === 'header')) {
|
||||
if (typeof childNode === 'string' || typeof childNode === 'number') {
|
||||
title = childNode.toString();
|
||||
} else if (isVNode(childNode) && typeof childNode.children === 'string') {
|
||||
title = childNode.children;
|
||||
}
|
||||
}
|
||||
|
||||
const componentProps = {
|
||||
title,
|
||||
...restCellProps,
|
||||
...additionalProps,
|
||||
colSpan: mergedColSpan && mergedColSpan !== 1 ? mergedColSpan : null,
|
||||
rowSpan: mergedRowSpan && mergedRowSpan !== 1 ? mergedRowSpan : null,
|
||||
class: classNames(
|
||||
cellPrefixCls,
|
||||
{
|
||||
[`${cellPrefixCls}-fix-left`]: isFixLeft,
|
||||
[`${cellPrefixCls}-fix-left-first`]: firstFixLeft,
|
||||
[`${cellPrefixCls}-fix-left-last`]: lastFixLeft,
|
||||
[`${cellPrefixCls}-fix-right`]: isFixRight,
|
||||
[`${cellPrefixCls}-fix-right-first`]: firstFixRight,
|
||||
[`${cellPrefixCls}-fix-right-last`]: lastFixRight,
|
||||
[`${cellPrefixCls}-ellipsis`]: ellipsis,
|
||||
[`${cellPrefixCls}-with-append`]: appendNode,
|
||||
[`${cellPrefixCls}-fix-sticky`]: (isFixLeft || isFixRight) && isSticky,
|
||||
},
|
||||
additionalProps.class,
|
||||
cellClassName,
|
||||
),
|
||||
style: {
|
||||
...parseStyleText(additionalProps.style as any),
|
||||
...alignStyle,
|
||||
...fixedStyle,
|
||||
...cellStyle,
|
||||
},
|
||||
};
|
||||
|
||||
return (
|
||||
<Component {...componentProps}>
|
||||
{appendNode}
|
||||
{childNode}
|
||||
</Component>
|
||||
);
|
||||
};
|
||||
},
|
||||
});
|
|
@ -0,0 +1,37 @@
|
|||
import type { ColumnType } from './interface';
|
||||
import { INTERNAL_COL_DEFINE } from './utils/legacyUtil';
|
||||
|
||||
export interface ColGroupProps<RecordType> {
|
||||
colWidths: readonly (number | string)[];
|
||||
columns?: readonly ColumnType<RecordType>[];
|
||||
columCount?: number;
|
||||
}
|
||||
|
||||
function ColGroup<RecordType>({ colWidths, columns, columCount }: ColGroupProps<RecordType>) {
|
||||
const cols = [];
|
||||
const len = columCount || columns.length;
|
||||
|
||||
// Only insert col with width & additional props
|
||||
// Skip if rest col do not have any useful info
|
||||
let mustInsert = false;
|
||||
for (let i = len - 1; i >= 0; i -= 1) {
|
||||
const width = colWidths[i];
|
||||
const column = columns && columns[i];
|
||||
const additionalProps = column && column[INTERNAL_COL_DEFINE];
|
||||
|
||||
if (width || additionalProps || mustInsert) {
|
||||
cols.unshift(
|
||||
<col
|
||||
key={i}
|
||||
style={{ width: typeof width === 'number' ? `${width}px` : width }}
|
||||
{...additionalProps}
|
||||
/>,
|
||||
);
|
||||
mustInsert = true;
|
||||
}
|
||||
}
|
||||
|
||||
return <colgroup>{cols}</colgroup>;
|
||||
}
|
||||
|
||||
export default ColGroup;
|
|
@ -0,0 +1,190 @@
|
|||
import type { HeaderProps } from '../Header/Header';
|
||||
import ColGroup from '../ColGroup';
|
||||
import type { ColumnsType, ColumnType, DefaultRecordType } from '../interface';
|
||||
import type { Ref } from 'vue';
|
||||
import {
|
||||
computed,
|
||||
defineComponent,
|
||||
nextTick,
|
||||
onBeforeUnmount,
|
||||
onMounted,
|
||||
ref,
|
||||
toRef,
|
||||
watchEffect,
|
||||
} from 'vue';
|
||||
import { useInjectTable } from '../context/TableContext';
|
||||
import classNames from '../../_util/classNames';
|
||||
import addEventListenerWrap from '../../vc-util/Dom/addEventListener';
|
||||
|
||||
function useColumnWidth(colWidthsRef: Ref<readonly number[]>, columCountRef: Ref<number>) {
|
||||
return computed(() => {
|
||||
const cloneColumns: number[] = [];
|
||||
const colWidths = colWidthsRef.value;
|
||||
const columCount = columCountRef.value;
|
||||
for (let i = 0; i < columCount; i += 1) {
|
||||
const val = colWidths[i];
|
||||
if (val !== undefined) {
|
||||
cloneColumns[i] = val;
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
return cloneColumns;
|
||||
});
|
||||
}
|
||||
|
||||
export interface FixedHeaderProps<RecordType> extends HeaderProps<RecordType> {
|
||||
noData: boolean;
|
||||
maxContentScroll: boolean;
|
||||
colWidths: readonly number[];
|
||||
columCount: number;
|
||||
direction: 'ltr' | 'rtl';
|
||||
fixHeader: boolean;
|
||||
stickyTopOffset?: number;
|
||||
stickyBottomOffset?: number;
|
||||
stickyClassName?: string;
|
||||
onScroll: (info: { currentTarget: HTMLDivElement; scrollLeft?: number }) => void;
|
||||
}
|
||||
|
||||
export default defineComponent<FixedHeaderProps<DefaultRecordType>>({
|
||||
name: 'FixedHolder',
|
||||
inheritAttrs: false,
|
||||
props: [
|
||||
'columns',
|
||||
'flattenColumns',
|
||||
'stickyOffsets',
|
||||
'customHeaderRow',
|
||||
'noData',
|
||||
'maxContentScroll',
|
||||
'colWidths',
|
||||
'columCount',
|
||||
'direction',
|
||||
'fixHeader',
|
||||
'stickyTopOffset',
|
||||
'stickyBottomOffset',
|
||||
'stickyClassName',
|
||||
] as any,
|
||||
emits: ['scroll'],
|
||||
setup(props, { attrs, slots, emit }) {
|
||||
const tableContext = useInjectTable();
|
||||
const combinationScrollBarSize = computed(() =>
|
||||
tableContext.isSticky && !props.fixHeader ? 0 : tableContext.scrollbarSize,
|
||||
);
|
||||
const scrollRef = ref();
|
||||
const onWheel = (e: WheelEvent) => {
|
||||
const { currentTarget, deltaX } = e;
|
||||
if (deltaX) {
|
||||
emit('scroll', { currentTarget, scrollLeft: (currentTarget as any).scrollLeft + deltaX });
|
||||
e.preventDefault();
|
||||
}
|
||||
};
|
||||
const wheelEvent = ref();
|
||||
onMounted(() => {
|
||||
nextTick(() => {
|
||||
wheelEvent.value = addEventListenerWrap(scrollRef.value, 'wheel', onWheel);
|
||||
});
|
||||
});
|
||||
onBeforeUnmount(() => {
|
||||
wheelEvent.value?.remove();
|
||||
});
|
||||
|
||||
// Check if all flattenColumns has width
|
||||
const allFlattenColumnsWithWidth = computed(() =>
|
||||
props.flattenColumns.every(
|
||||
column => column.width && column.width !== 0 && column.width !== '0px',
|
||||
),
|
||||
);
|
||||
|
||||
const columnsWithScrollbar = ref<ColumnsType<unknown>>([]);
|
||||
const flattenColumnsWithScrollbar = ref<ColumnsType<unknown>>([]);
|
||||
|
||||
watchEffect(() => {
|
||||
// Add scrollbar column
|
||||
const lastColumn = props.flattenColumns[props.flattenColumns.length - 1];
|
||||
const ScrollBarColumn: ColumnType<unknown> & { scrollbar: true } = {
|
||||
fixed: lastColumn ? lastColumn.fixed : null,
|
||||
scrollbar: true,
|
||||
customHeaderCell: () => ({
|
||||
class: `${tableContext.prefixCls}-cell-scrollbar`,
|
||||
}),
|
||||
};
|
||||
|
||||
columnsWithScrollbar.value = combinationScrollBarSize.value
|
||||
? [...props.columns, ScrollBarColumn]
|
||||
: props.columns;
|
||||
|
||||
flattenColumnsWithScrollbar.value = combinationScrollBarSize.value
|
||||
? [...props.flattenColumns, ScrollBarColumn]
|
||||
: props.flattenColumns;
|
||||
});
|
||||
|
||||
// Calculate the sticky offsets
|
||||
const headerStickyOffsets = computed(() => {
|
||||
const { stickyOffsets, direction } = props;
|
||||
const { right, left } = stickyOffsets;
|
||||
return {
|
||||
...stickyOffsets,
|
||||
left:
|
||||
direction === 'rtl'
|
||||
? [...left.map(width => width + combinationScrollBarSize.value), 0]
|
||||
: left,
|
||||
right:
|
||||
direction === 'rtl'
|
||||
? right
|
||||
: [...right.map(width => width + combinationScrollBarSize.value), 0],
|
||||
isSticky: tableContext.isSticky,
|
||||
};
|
||||
});
|
||||
|
||||
const mergedColumnWidth = useColumnWidth(toRef(props, 'colWidths'), toRef(props, 'columCount'));
|
||||
|
||||
return () => {
|
||||
const {
|
||||
noData,
|
||||
columCount,
|
||||
stickyTopOffset,
|
||||
stickyBottomOffset,
|
||||
stickyClassName,
|
||||
maxContentScroll,
|
||||
} = props;
|
||||
const { isSticky } = tableContext;
|
||||
return (
|
||||
<div
|
||||
style={{
|
||||
overflow: 'hidden',
|
||||
...(isSticky ? { top: `${stickyTopOffset}px`, bottom: `${stickyBottomOffset}px` } : {}),
|
||||
}}
|
||||
ref={scrollRef}
|
||||
class={classNames(attrs.class, {
|
||||
[stickyClassName]: !!stickyClassName,
|
||||
})}
|
||||
>
|
||||
<table
|
||||
style={{
|
||||
tableLayout: 'fixed',
|
||||
visibility: noData || mergedColumnWidth.value ? null : 'hidden',
|
||||
}}
|
||||
>
|
||||
{(!noData || !maxContentScroll || allFlattenColumnsWithWidth.value) && (
|
||||
<ColGroup
|
||||
colWidths={
|
||||
mergedColumnWidth.value
|
||||
? [...mergedColumnWidth.value, combinationScrollBarSize.value]
|
||||
: []
|
||||
}
|
||||
columCount={columCount + 1}
|
||||
columns={flattenColumnsWithScrollbar.value}
|
||||
/>
|
||||
)}
|
||||
{slots.default?.({
|
||||
...props,
|
||||
stickyOffsets: headerStickyOffsets.value,
|
||||
columns: columnsWithScrollbar.value,
|
||||
flattenColumns: flattenColumnsWithScrollbar.value,
|
||||
})}
|
||||
</table>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
},
|
||||
});
|
|
@ -0,0 +1,57 @@
|
|||
import { defineComponent } from 'vue';
|
||||
import Cell from '../Cell';
|
||||
import { useInjectSummary } from '../context/SummaryContext';
|
||||
import { useInjectTable } from '../context/TableContext';
|
||||
import type { AlignType } from '../interface';
|
||||
import { getCellFixedInfo } from '../utils/fixUtil';
|
||||
|
||||
export interface SummaryCellProps {
|
||||
index: number;
|
||||
colSpan?: number;
|
||||
rowSpan?: number;
|
||||
align?: AlignType;
|
||||
}
|
||||
|
||||
export default defineComponent<SummaryCellProps>({
|
||||
name: 'SummaryCell',
|
||||
inheritAttrs: false,
|
||||
props: ['index', 'colSpan', 'rowSpan', 'align'] as any,
|
||||
setup(props, { attrs, slots }) {
|
||||
const tableContext = useInjectTable();
|
||||
const summaryContext = useInjectSummary();
|
||||
return () => {
|
||||
const { index, colSpan = 1, rowSpan, align } = props;
|
||||
const { prefixCls, direction } = tableContext;
|
||||
const { scrollColumnIndex, stickyOffsets, flattenColumns } = summaryContext;
|
||||
const lastIndex = index + colSpan - 1;
|
||||
const mergedColSpan = lastIndex + 1 === scrollColumnIndex ? colSpan + 1 : colSpan;
|
||||
|
||||
const fixedInfo = getCellFixedInfo(
|
||||
index,
|
||||
index + mergedColSpan - 1,
|
||||
flattenColumns,
|
||||
stickyOffsets,
|
||||
direction,
|
||||
);
|
||||
return (
|
||||
<Cell
|
||||
class={attrs.class as string}
|
||||
index={index}
|
||||
component="td"
|
||||
prefixCls={prefixCls}
|
||||
record={null}
|
||||
dataIndex={null}
|
||||
align={align}
|
||||
customRender={() => ({
|
||||
children: slots.default?.(),
|
||||
props: {
|
||||
colSpan: mergedColSpan,
|
||||
rowSpan,
|
||||
},
|
||||
})}
|
||||
{...fixedInfo}
|
||||
/>
|
||||
);
|
||||
};
|
||||
},
|
||||
});
|
|
@ -0,0 +1,8 @@
|
|||
import { defineComponent } from 'vue';
|
||||
|
||||
export default defineComponent({
|
||||
name: 'FooterRow',
|
||||
setup(_props, { slots }) {
|
||||
return () => <tr>{slots.default?.()}</tr>;
|
||||
},
|
||||
});
|
|
@ -0,0 +1,26 @@
|
|||
import { computed, defineComponent, onBeforeUnmount, watchEffect } from 'vue';
|
||||
import { useInjectTable } from '../context/TableContext';
|
||||
|
||||
export interface SummaryProps {
|
||||
fixed?: boolean | 'top' | 'bottom';
|
||||
}
|
||||
|
||||
let indexGuid = 0;
|
||||
const Summary = defineComponent<SummaryProps>({
|
||||
name: 'Summary',
|
||||
props: ['fixed'] as any,
|
||||
setup(props, { slots }) {
|
||||
const tableContext = useInjectTable();
|
||||
const uniKey = `table-summary-uni-key-${++indexGuid}`;
|
||||
const fixed = computed(() => (props.fixed as string) === '' || props.fixed);
|
||||
watchEffect(() => {
|
||||
tableContext.summaryCollect(uniKey, fixed.value);
|
||||
});
|
||||
onBeforeUnmount(() => {
|
||||
tableContext.summaryCollect(uniKey, false);
|
||||
});
|
||||
return () => slots.default?.();
|
||||
},
|
||||
});
|
||||
|
||||
export default Summary;
|
|
@ -0,0 +1,40 @@
|
|||
import Summary from './Summary';
|
||||
import SummaryRow from './Row';
|
||||
import SummaryCell from './Cell';
|
||||
import type { DefaultRecordType, StickyOffsets } from '../interface';
|
||||
import { computed, defineComponent, reactive, toRef } from 'vue';
|
||||
import type { FlattenColumns } from '../context/SummaryContext';
|
||||
import { useProvideSummary } from '../context/SummaryContext';
|
||||
import { useInjectTable } from '../context/TableContext';
|
||||
|
||||
export interface FooterProps<RecordType = DefaultRecordType> {
|
||||
stickyOffsets: StickyOffsets;
|
||||
flattenColumns: FlattenColumns<RecordType>;
|
||||
}
|
||||
|
||||
export default defineComponent({
|
||||
name: 'Footer',
|
||||
inheritAttrs: false,
|
||||
props: ['stickyOffsets', 'flattenColumns'],
|
||||
setup(props, { slots }) {
|
||||
const tableContext = useInjectTable();
|
||||
useProvideSummary(
|
||||
reactive({
|
||||
stickyOffsets: toRef(props, 'stickyOffsets'),
|
||||
flattenColumns: toRef(props, 'flattenColumns'),
|
||||
scrollColumnIndex: computed(() => {
|
||||
const lastColumnIndex = props.flattenColumns.length - 1;
|
||||
const scrollColumn = props.flattenColumns[lastColumnIndex];
|
||||
return scrollColumn?.scrollbar ? lastColumnIndex : null;
|
||||
}),
|
||||
}),
|
||||
);
|
||||
return () => {
|
||||
const { prefixCls } = tableContext;
|
||||
return <tfoot class={`${prefixCls}-summary`}>{slots.default?.()}</tfoot>;
|
||||
};
|
||||
},
|
||||
});
|
||||
|
||||
export { SummaryRow, SummaryCell };
|
||||
export const FooterComponents = Summary;
|
|
@ -0,0 +1,128 @@
|
|||
import classNames from '../../_util/classNames';
|
||||
import { computed, defineComponent } from 'vue';
|
||||
import { useInjectTable } from '../context/TableContext';
|
||||
import type {
|
||||
ColumnsType,
|
||||
CellType,
|
||||
StickyOffsets,
|
||||
ColumnType,
|
||||
GetComponentProps,
|
||||
ColumnGroupType,
|
||||
DefaultRecordType,
|
||||
} from '../interface';
|
||||
import HeaderRow from './HeaderRow';
|
||||
|
||||
function parseHeaderRows<RecordType>(
|
||||
rootColumns: ColumnsType<RecordType>,
|
||||
): CellType<RecordType>[][] {
|
||||
const rows: CellType<RecordType>[][] = [];
|
||||
|
||||
function fillRowCells(
|
||||
columns: ColumnsType<RecordType>,
|
||||
colIndex: number,
|
||||
rowIndex = 0,
|
||||
): number[] {
|
||||
// Init rows
|
||||
rows[rowIndex] = rows[rowIndex] || [];
|
||||
|
||||
let currentColIndex = colIndex;
|
||||
const colSpans: number[] = columns.filter(Boolean).map(column => {
|
||||
const cell: CellType<RecordType> = {
|
||||
key: column.key,
|
||||
class: classNames(column.className, column.class),
|
||||
// children: column.title,
|
||||
column,
|
||||
colStart: currentColIndex,
|
||||
};
|
||||
|
||||
let colSpan = 1;
|
||||
|
||||
const subColumns = (column as ColumnGroupType<RecordType>).children;
|
||||
if (subColumns && subColumns.length > 0) {
|
||||
colSpan = fillRowCells(subColumns, currentColIndex, rowIndex + 1).reduce(
|
||||
(total, count) => total + count,
|
||||
0,
|
||||
);
|
||||
cell.hasSubColumns = true;
|
||||
}
|
||||
|
||||
if ('colSpan' in column) {
|
||||
({ colSpan } = column);
|
||||
}
|
||||
|
||||
if ('rowSpan' in column) {
|
||||
cell.rowSpan = column.rowSpan;
|
||||
}
|
||||
|
||||
cell.colSpan = colSpan;
|
||||
cell.colEnd = cell.colStart + colSpan - 1;
|
||||
rows[rowIndex].push(cell);
|
||||
|
||||
currentColIndex += colSpan;
|
||||
|
||||
return colSpan;
|
||||
});
|
||||
|
||||
return colSpans;
|
||||
}
|
||||
|
||||
// Generate `rows` cell data
|
||||
fillRowCells(rootColumns, 0);
|
||||
|
||||
// Handle `rowSpan`
|
||||
const rowCount = rows.length;
|
||||
for (let rowIndex = 0; rowIndex < rowCount; rowIndex += 1) {
|
||||
rows[rowIndex].forEach(cell => {
|
||||
if (!('rowSpan' in cell) && !cell.hasSubColumns) {
|
||||
// eslint-disable-next-line no-param-reassign
|
||||
cell.rowSpan = rowCount - rowIndex;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
return rows;
|
||||
}
|
||||
|
||||
export interface HeaderProps<RecordType = DefaultRecordType> {
|
||||
columns: ColumnsType<RecordType>;
|
||||
flattenColumns: readonly ColumnType<RecordType>[];
|
||||
stickyOffsets: StickyOffsets;
|
||||
customHeaderRow: GetComponentProps<readonly ColumnType<RecordType>[]>;
|
||||
}
|
||||
|
||||
export default defineComponent<HeaderProps>({
|
||||
name: 'Header',
|
||||
inheritAttrs: false,
|
||||
props: ['columns', 'flattenColumns', 'stickyOffsets', 'customHeaderRow'] as any,
|
||||
setup(props) {
|
||||
const tableContext = useInjectTable();
|
||||
const rows = computed(() => parseHeaderRows(props.columns));
|
||||
return () => {
|
||||
const { prefixCls, getComponent } = tableContext;
|
||||
const { stickyOffsets, flattenColumns, customHeaderRow } = props;
|
||||
const WrapperComponent = getComponent(['header', 'wrapper'], 'thead');
|
||||
const trComponent = getComponent(['header', 'row'], 'tr');
|
||||
const thComponent = getComponent(['header', 'cell'], 'th');
|
||||
return (
|
||||
<WrapperComponent class={`${prefixCls}-thead`}>
|
||||
{rows.value.map((row, rowIndex) => {
|
||||
const rowNode = (
|
||||
<HeaderRow
|
||||
key={rowIndex}
|
||||
flattenColumns={flattenColumns}
|
||||
cells={row}
|
||||
stickyOffsets={stickyOffsets}
|
||||
rowComponent={trComponent}
|
||||
cellComponent={thComponent}
|
||||
customHeaderRow={customHeaderRow}
|
||||
index={rowIndex}
|
||||
/>
|
||||
);
|
||||
|
||||
return rowNode;
|
||||
})}
|
||||
</WrapperComponent>
|
||||
);
|
||||
};
|
||||
},
|
||||
});
|
|
@ -0,0 +1,98 @@
|
|||
import { defineComponent } from 'vue';
|
||||
import Cell from '../Cell';
|
||||
import { useInjectTable } from '../context/TableContext';
|
||||
import type {
|
||||
CellType,
|
||||
StickyOffsets,
|
||||
ColumnType,
|
||||
CustomizeComponent,
|
||||
GetComponentProps,
|
||||
DefaultRecordType,
|
||||
} from '../interface';
|
||||
import { getCellFixedInfo } from '../utils/fixUtil';
|
||||
import { getColumnsKey } from '../utils/valueUtil';
|
||||
|
||||
export interface RowProps<RecordType = DefaultRecordType> {
|
||||
cells: readonly CellType<RecordType>[];
|
||||
stickyOffsets: StickyOffsets;
|
||||
flattenColumns: readonly ColumnType<RecordType>[];
|
||||
rowComponent: CustomizeComponent;
|
||||
cellComponent: CustomizeComponent;
|
||||
customHeaderRow: GetComponentProps<readonly ColumnType<RecordType>[]>;
|
||||
index: number;
|
||||
}
|
||||
|
||||
export default defineComponent<RowProps>({
|
||||
name: 'HeaderRow',
|
||||
props: [
|
||||
'cells',
|
||||
'stickyOffsets',
|
||||
'flattenColumns',
|
||||
'rowComponent',
|
||||
'cellComponent',
|
||||
'index',
|
||||
'customHeaderRow',
|
||||
] as any,
|
||||
setup(props: RowProps) {
|
||||
const tableContext = useInjectTable();
|
||||
return () => {
|
||||
const { prefixCls, direction } = tableContext;
|
||||
const {
|
||||
cells,
|
||||
stickyOffsets,
|
||||
flattenColumns,
|
||||
rowComponent: RowComponent,
|
||||
cellComponent: CellComponent,
|
||||
customHeaderRow,
|
||||
index,
|
||||
} = props;
|
||||
|
||||
let rowProps;
|
||||
if (customHeaderRow) {
|
||||
rowProps = customHeaderRow(
|
||||
cells.map(cell => cell.column),
|
||||
index,
|
||||
);
|
||||
}
|
||||
|
||||
const columnsKey = getColumnsKey(cells.map(cell => cell.column));
|
||||
|
||||
return (
|
||||
<RowComponent {...rowProps}>
|
||||
{cells.map((cell: CellType, cellIndex) => {
|
||||
const { column } = cell;
|
||||
const fixedInfo = getCellFixedInfo(
|
||||
cell.colStart,
|
||||
cell.colEnd,
|
||||
flattenColumns,
|
||||
stickyOffsets,
|
||||
direction,
|
||||
);
|
||||
|
||||
let additionalProps;
|
||||
if (column && column.customHeaderCell) {
|
||||
additionalProps = cell.column.customHeaderCell(column);
|
||||
}
|
||||
|
||||
return (
|
||||
<Cell
|
||||
{...cell}
|
||||
cellType="header"
|
||||
ellipsis={column.ellipsis}
|
||||
align={column.align}
|
||||
component={CellComponent}
|
||||
prefixCls={prefixCls}
|
||||
key={columnsKey[cellIndex]}
|
||||
{...fixedInfo}
|
||||
additionalProps={additionalProps}
|
||||
rowType="header"
|
||||
column={column}
|
||||
v-slots={{ default: () => column.title }}
|
||||
/>
|
||||
);
|
||||
})}
|
||||
</RowComponent>
|
||||
);
|
||||
};
|
||||
},
|
||||
});
|
|
@ -0,0 +1,7 @@
|
|||
function Panel(_, { slots }) {
|
||||
return <div>{slots.default?.()}</div>;
|
||||
}
|
||||
|
||||
Panel.displayName = 'Panel';
|
||||
|
||||
export default Panel;
|
|
@ -0,0 +1,804 @@
|
|||
import Header from './Header/Header';
|
||||
import type {
|
||||
GetRowKey,
|
||||
ColumnsType,
|
||||
TableComponents,
|
||||
Key,
|
||||
TriggerEventHandler,
|
||||
GetComponentProps,
|
||||
PanelRender,
|
||||
TableLayout,
|
||||
RowClassName,
|
||||
CustomizeComponent,
|
||||
ColumnType,
|
||||
CustomizeScrollBody,
|
||||
TableSticky,
|
||||
ExpandedRowRender,
|
||||
RenderExpandIcon,
|
||||
TransformCellText,
|
||||
} from './interface';
|
||||
import Body from './Body';
|
||||
import useColumns from './hooks/useColumns';
|
||||
import { useLayoutState, useTimeoutLock } from './hooks/useFrame';
|
||||
import { getPathValue, mergeObject, validateValue, getColumnsKey } from './utils/valueUtil';
|
||||
import useStickyOffsets from './hooks/useStickyOffsets';
|
||||
import ColGroup from './ColGroup';
|
||||
import Panel from './Panel';
|
||||
import Footer from './Footer';
|
||||
import { findAllChildrenKeys, renderExpandIcon } from './utils/expandUtil';
|
||||
import { getCellFixedInfo } from './utils/fixUtil';
|
||||
import StickyScrollBar from './stickyScrollBar';
|
||||
import useSticky from './hooks/useSticky';
|
||||
import FixedHolder from './FixedHolder';
|
||||
import type { CSSProperties } from 'vue';
|
||||
import {
|
||||
computed,
|
||||
defineComponent,
|
||||
nextTick,
|
||||
onMounted,
|
||||
reactive,
|
||||
ref,
|
||||
toRef,
|
||||
toRefs,
|
||||
watch,
|
||||
watchEffect,
|
||||
} from 'vue';
|
||||
import { warning } from '../vc-util/warning';
|
||||
import { reactivePick } from '../_util/reactivePick';
|
||||
import useState from '../_util/hooks/useState';
|
||||
import { toPx } from '../_util/util';
|
||||
import isVisible from '../vc-util/Dom/isVisible';
|
||||
import { getTargetScrollBarSize } from '../_util/getScrollBarSize';
|
||||
import classNames from '../_util/classNames';
|
||||
import type { EventHandler } from '../_util/EventInterface';
|
||||
import VCResizeObserver from '../vc-resize-observer';
|
||||
import { useProvideTable } from './context/TableContext';
|
||||
import { useProvideBody } from './context/BodyContext';
|
||||
import { useProvideResize } from './context/ResizeContext';
|
||||
import { getDataAndAriaProps } from './utils/legacyUtil';
|
||||
|
||||
// Used for conditions cache
|
||||
const EMPTY_DATA = [];
|
||||
|
||||
// Used for customize scroll
|
||||
const EMPTY_SCROLL_TARGET = {};
|
||||
|
||||
export const INTERNAL_HOOKS = 'rc-table-internal-hook';
|
||||
|
||||
export interface TableProps<RecordType = unknown> {
|
||||
prefixCls?: string;
|
||||
data?: RecordType[];
|
||||
columns?: ColumnsType<RecordType>;
|
||||
rowKey?: string | GetRowKey<RecordType>;
|
||||
tableLayout?: TableLayout;
|
||||
|
||||
// Fixed Columns
|
||||
scroll?: { x?: number | true | string; y?: number | string };
|
||||
|
||||
rowClassName?: string | RowClassName<RecordType>;
|
||||
|
||||
// Additional Part
|
||||
title?: PanelRender<RecordType>;
|
||||
footer?: PanelRender<RecordType>;
|
||||
// summary?: (data: readonly RecordType[]) => any;
|
||||
|
||||
// Customize
|
||||
id?: string;
|
||||
showHeader?: boolean;
|
||||
components?: TableComponents<RecordType>;
|
||||
customRow?: GetComponentProps<RecordType>;
|
||||
customHeaderRow?: GetComponentProps<ColumnType<RecordType>[]>;
|
||||
// emptyText?: any;
|
||||
|
||||
direction?: 'ltr' | 'rtl';
|
||||
|
||||
// Expandable
|
||||
expandFixed?: boolean;
|
||||
expandColumnWidth?: number;
|
||||
expandedRowKeys?: Key[];
|
||||
defaultExpandedRowKeys?: Key[];
|
||||
expandedRowRender?: ExpandedRowRender<RecordType>;
|
||||
expandRowByClick?: boolean;
|
||||
expandIcon?: RenderExpandIcon<RecordType>;
|
||||
onExpand?: (expanded: boolean, record: RecordType) => void;
|
||||
onExpandedRowsChange?: (expandedKeys: Key[]) => void;
|
||||
defaultExpandAllRows?: boolean;
|
||||
indentSize?: number;
|
||||
expandIconColumnIndex?: number;
|
||||
expandedRowClassName?: RowClassName<RecordType>;
|
||||
childrenColumnName?: string;
|
||||
rowExpandable?: (record: RecordType) => boolean;
|
||||
|
||||
// =================================== Internal ===================================
|
||||
/**
|
||||
* @private Internal usage, may remove by refactor. Should always use `columns` instead.
|
||||
*
|
||||
* !!! DO NOT USE IN PRODUCTION ENVIRONMENT !!!
|
||||
*/
|
||||
internalHooks?: string;
|
||||
|
||||
/**
|
||||
* @private Internal usage, may remove by refactor. Should always use `columns` instead.
|
||||
*
|
||||
* !!! DO NOT USE IN PRODUCTION ENVIRONMENT !!!
|
||||
*/
|
||||
// Used for antd table transform column with additional column
|
||||
transformColumns?: (columns: ColumnsType<RecordType>) => ColumnsType<RecordType>;
|
||||
|
||||
/**
|
||||
* @private Internal usage, may remove by refactor.
|
||||
*
|
||||
* !!! DO NOT USE IN PRODUCTION ENVIRONMENT !!!
|
||||
*/
|
||||
internalRefs?: {
|
||||
body: HTMLDivElement;
|
||||
};
|
||||
|
||||
sticky?: boolean | TableSticky;
|
||||
|
||||
canExpandable?: boolean;
|
||||
|
||||
onUpdateInternalRefs?: (refs: Record<string, any>) => void;
|
||||
|
||||
transformCellText?: TransformCellText<RecordType>;
|
||||
}
|
||||
|
||||
export default defineComponent<TableProps>({
|
||||
name: 'Table',
|
||||
inheritAttrs: false,
|
||||
props: [
|
||||
'prefixCls',
|
||||
'data',
|
||||
'columns',
|
||||
'rowKey',
|
||||
'tableLayout',
|
||||
'scroll',
|
||||
'rowClassName',
|
||||
'title',
|
||||
'footer',
|
||||
'id',
|
||||
'showHeader',
|
||||
'components',
|
||||
'customRow',
|
||||
'customHeaderRow',
|
||||
'direction',
|
||||
'expandFixed',
|
||||
'expandColumnWidth',
|
||||
'expandedRowKeys',
|
||||
'defaultExpandedRowKeys',
|
||||
'expandedRowRender',
|
||||
'expandRowByClick',
|
||||
'expandIcon',
|
||||
'onExpand',
|
||||
'onExpandedRowsChange',
|
||||
'defaultExpandAllRows',
|
||||
'indentSize',
|
||||
'expandIconColumnIndex',
|
||||
'expandedRowClassName',
|
||||
'childrenColumnName',
|
||||
'rowExpandable',
|
||||
'sticky',
|
||||
'transformColumns',
|
||||
'internalHooks',
|
||||
'internalRefs',
|
||||
'canExpandable',
|
||||
'onUpdateInternalRefs',
|
||||
'transformCellText',
|
||||
] as any,
|
||||
slots: ['title', 'footer', 'summary', 'emptyText'],
|
||||
emits: ['expand', 'expandedRowsChange', 'updateInternalRefs'],
|
||||
setup(props, { attrs, slots, emit }) {
|
||||
const mergedData = computed(() => props.data || EMPTY_DATA);
|
||||
const hasData = computed(() => !!mergedData.value.length);
|
||||
|
||||
// ==================== Customize =====================
|
||||
const mergedComponents = computed(() =>
|
||||
mergeObject<TableComponents<any>>(props.components, {}),
|
||||
);
|
||||
|
||||
const getComponent = (path, defaultComponent?: string) =>
|
||||
getPathValue<CustomizeComponent, TableComponents<any>>(mergedComponents.value, path) ||
|
||||
defaultComponent;
|
||||
|
||||
const getRowKey = computed(() => {
|
||||
const rowKey = props.rowKey;
|
||||
if (typeof rowKey === 'function') {
|
||||
return rowKey;
|
||||
}
|
||||
return record => {
|
||||
const key = record && record[rowKey];
|
||||
|
||||
if (process.env.NODE_ENV !== 'production') {
|
||||
warning(
|
||||
key !== undefined,
|
||||
'Each record in table should have a unique `key` prop, or set `rowKey` to an unique primary key.',
|
||||
);
|
||||
}
|
||||
|
||||
return key;
|
||||
};
|
||||
});
|
||||
|
||||
// ====================== Expand ======================
|
||||
|
||||
const mergedExpandIcon = computed(() => props.expandIcon || renderExpandIcon);
|
||||
|
||||
const mergedChildrenColumnName = computed(() => props.childrenColumnName || 'children');
|
||||
|
||||
const expandableType = computed(() => {
|
||||
if (props.expandedRowRender) {
|
||||
return 'row';
|
||||
}
|
||||
/* eslint-disable no-underscore-dangle */
|
||||
/**
|
||||
* Fix https://github.com/ant-design/ant-design/issues/21154
|
||||
* This is a workaround to not to break current behavior.
|
||||
* We can remove follow code after final release.
|
||||
*
|
||||
* To other developer:
|
||||
* Do not use `__PARENT_RENDER_ICON__` in prod since we will remove this when refactor
|
||||
*/
|
||||
if (
|
||||
props.canExpandable ||
|
||||
mergedData.value.some(
|
||||
record => record && typeof record === 'object' && record[mergedChildrenColumnName.value],
|
||||
)
|
||||
) {
|
||||
return 'nest';
|
||||
}
|
||||
/* eslint-enable */
|
||||
return false;
|
||||
});
|
||||
|
||||
const innerExpandedKeys = ref([]);
|
||||
const stop = watchEffect(() => {
|
||||
if (props.defaultExpandedRowKeys) {
|
||||
innerExpandedKeys.value = props.defaultExpandedRowKeys;
|
||||
}
|
||||
if (props.defaultExpandAllRows) {
|
||||
innerExpandedKeys.value = findAllChildrenKeys(
|
||||
mergedData.value,
|
||||
getRowKey.value,
|
||||
mergedChildrenColumnName.value,
|
||||
);
|
||||
}
|
||||
});
|
||||
// defalutXxxx 仅仅第一次生效
|
||||
stop();
|
||||
|
||||
const mergedExpandedKeys = computed(
|
||||
() => new Set(props.expandedRowKeys || innerExpandedKeys.value || []),
|
||||
);
|
||||
|
||||
const onTriggerExpand: TriggerEventHandler<any> = record => {
|
||||
const key = getRowKey.value(record, mergedData.value.indexOf(record));
|
||||
|
||||
let newExpandedKeys: Key[];
|
||||
const hasKey = mergedExpandedKeys.value.has(key);
|
||||
if (hasKey) {
|
||||
mergedExpandedKeys.value.delete(key);
|
||||
newExpandedKeys = [...mergedExpandedKeys.value];
|
||||
} else {
|
||||
newExpandedKeys = [...mergedExpandedKeys.value, key];
|
||||
}
|
||||
innerExpandedKeys.value = newExpandedKeys;
|
||||
|
||||
emit('expand', !hasKey, record);
|
||||
emit('expandedRowsChange', newExpandedKeys);
|
||||
};
|
||||
|
||||
const componentWidth = ref(0);
|
||||
|
||||
const [columns, flattenColumns] = useColumns(
|
||||
{
|
||||
...toRefs(props),
|
||||
|
||||
// children,
|
||||
expandable: computed(() => !!props.expandedRowRender),
|
||||
expandedKeys: mergedExpandedKeys,
|
||||
getRowKey,
|
||||
onTriggerExpand,
|
||||
expandIcon: mergedExpandIcon,
|
||||
},
|
||||
computed(() => (props.internalHooks === INTERNAL_HOOKS ? props.transformColumns : null)),
|
||||
);
|
||||
|
||||
const columnContext = computed(() => ({
|
||||
columns: columns.value,
|
||||
flattenColumns: flattenColumns.value,
|
||||
}));
|
||||
|
||||
// ====================== Scroll ======================
|
||||
const fullTableRef = ref<HTMLDivElement>();
|
||||
const scrollHeaderRef = ref<HTMLDivElement>();
|
||||
const scrollBodyRef = ref<HTMLDivElement>();
|
||||
const scrollSummaryRef = ref<HTMLDivElement>();
|
||||
const [pingedLeft, setPingedLeft] = useState(false);
|
||||
const [pingedRight, setPingedRight] = useState(false);
|
||||
const [colsWidths, updateColsWidths] = useLayoutState(new Map<Key, number>());
|
||||
|
||||
// Convert map to number width
|
||||
const colsKeys = computed(() => getColumnsKey(flattenColumns.value));
|
||||
const colWidths = computed(() =>
|
||||
colsKeys.value.map(columnKey => colsWidths.value.get(columnKey)),
|
||||
);
|
||||
const columnCount = computed(() => flattenColumns.value.length);
|
||||
const stickyOffsets = useStickyOffsets(colWidths, columnCount, toRef(props, 'direction'));
|
||||
const fixHeader = computed(() => props.scroll && validateValue(props.scroll.y));
|
||||
const horizonScroll = computed(
|
||||
() => (props.scroll && validateValue(props.scroll.x)) || Boolean(props.expandFixed),
|
||||
);
|
||||
const fixColumn = computed(
|
||||
() => horizonScroll.value && flattenColumns.value.some(({ fixed }) => fixed),
|
||||
);
|
||||
|
||||
// Sticky
|
||||
const stickyRef = ref<{ setScrollLeft: (left: number) => void }>();
|
||||
const stickyState = useSticky(toRef(props, 'sticky'), toRef(props, 'prefixCls'));
|
||||
|
||||
const summaryFixedInfos = reactive<Record<string, boolean | string>>({});
|
||||
const fixFooter = computed(() => {
|
||||
const info = Object.values(summaryFixedInfos)[0];
|
||||
return (fixHeader.value || stickyState.value.isSticky) && info;
|
||||
});
|
||||
|
||||
const summaryCollect = (uniKey: string, fixed: boolean | string) => {
|
||||
if (fixed) {
|
||||
summaryFixedInfos[uniKey] = fixed;
|
||||
} else {
|
||||
delete summaryFixedInfos[uniKey];
|
||||
}
|
||||
};
|
||||
|
||||
// Scroll
|
||||
const scrollXStyle = ref<CSSProperties>({});
|
||||
const scrollYStyle = ref<CSSProperties>({});
|
||||
const scrollTableStyle = ref<CSSProperties>({});
|
||||
|
||||
watchEffect(() => {
|
||||
if (fixHeader.value) {
|
||||
scrollYStyle.value = {
|
||||
overflowY: 'scroll',
|
||||
maxHeight: toPx(props.scroll.y),
|
||||
};
|
||||
}
|
||||
|
||||
if (horizonScroll.value) {
|
||||
scrollXStyle.value = { overflowX: 'auto' };
|
||||
// When no vertical scrollbar, should hide it
|
||||
// https://github.com/ant-design/ant-design/pull/20705
|
||||
// https://github.com/ant-design/ant-design/issues/21879
|
||||
if (!fixHeader.value) {
|
||||
scrollYStyle.value = { overflowY: 'hidden' };
|
||||
}
|
||||
scrollTableStyle.value = {
|
||||
width: props.scroll.x === true ? 'auto' : toPx(props.scroll.x),
|
||||
minWidth: '100%',
|
||||
};
|
||||
}
|
||||
});
|
||||
|
||||
const onColumnResize = (columnKey: Key, width: number) => {
|
||||
if (isVisible(fullTableRef.value)) {
|
||||
updateColsWidths(widths => {
|
||||
if (widths.get(columnKey) !== width) {
|
||||
const newWidths = new Map(widths);
|
||||
newWidths.set(columnKey, width);
|
||||
return newWidths;
|
||||
}
|
||||
return widths;
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
const [setScrollTarget, getScrollTarget] = useTimeoutLock(null);
|
||||
|
||||
function forceScroll(scrollLeft: number, target: HTMLDivElement | ((left: number) => void)) {
|
||||
if (!target) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (typeof target === 'function') {
|
||||
target(scrollLeft);
|
||||
return;
|
||||
}
|
||||
const domTarget = (target as any).$el || target;
|
||||
if (domTarget.scrollLeft !== scrollLeft) {
|
||||
// eslint-disable-next-line no-param-reassign
|
||||
domTarget.scrollLeft = scrollLeft;
|
||||
}
|
||||
}
|
||||
|
||||
const onScroll: EventHandler = ({
|
||||
currentTarget,
|
||||
scrollLeft,
|
||||
}: {
|
||||
currentTarget: HTMLElement;
|
||||
scrollLeft?: number;
|
||||
}) => {
|
||||
const isRTL = props.direction === 'rtl';
|
||||
const mergedScrollLeft =
|
||||
typeof scrollLeft === 'number' ? scrollLeft : currentTarget.scrollLeft;
|
||||
|
||||
const compareTarget = currentTarget || EMPTY_SCROLL_TARGET;
|
||||
if (!getScrollTarget() || getScrollTarget() === compareTarget) {
|
||||
setScrollTarget(compareTarget);
|
||||
|
||||
forceScroll(mergedScrollLeft, scrollHeaderRef.value);
|
||||
forceScroll(mergedScrollLeft, scrollBodyRef.value);
|
||||
forceScroll(mergedScrollLeft, scrollSummaryRef.value);
|
||||
forceScroll(mergedScrollLeft, stickyRef.value?.setScrollLeft);
|
||||
}
|
||||
|
||||
if (currentTarget) {
|
||||
const { scrollWidth, clientWidth } = currentTarget;
|
||||
if (isRTL) {
|
||||
setPingedLeft(-mergedScrollLeft < scrollWidth - clientWidth);
|
||||
setPingedRight(-mergedScrollLeft > 0);
|
||||
} else {
|
||||
setPingedLeft(mergedScrollLeft > 0);
|
||||
setPingedRight(mergedScrollLeft < scrollWidth - clientWidth);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const triggerOnScroll = () => {
|
||||
if (scrollBodyRef.value) {
|
||||
onScroll({ currentTarget: scrollBodyRef.value });
|
||||
}
|
||||
};
|
||||
|
||||
const onFullTableResize = ({ width }) => {
|
||||
if (width !== componentWidth.value) {
|
||||
triggerOnScroll();
|
||||
componentWidth.value = fullTableRef.value ? fullTableRef.value.offsetWidth : width;
|
||||
}
|
||||
};
|
||||
|
||||
watch([horizonScroll, () => props.data, () => props.columns], () => {
|
||||
if (horizonScroll.value) {
|
||||
triggerOnScroll();
|
||||
}
|
||||
});
|
||||
|
||||
const [scrollbarSize, setScrollbarSize] = useState(0);
|
||||
|
||||
onMounted(() => {
|
||||
nextTick(() => {
|
||||
triggerOnScroll();
|
||||
setScrollbarSize(getTargetScrollBarSize(scrollBodyRef.value).width);
|
||||
});
|
||||
});
|
||||
|
||||
watchEffect(
|
||||
() => {
|
||||
if (props.internalHooks === INTERNAL_HOOKS && props.internalRefs) {
|
||||
props.onUpdateInternalRefs({
|
||||
body: scrollBodyRef.value
|
||||
? (scrollBodyRef.value as any).$el || scrollBodyRef.value
|
||||
: null,
|
||||
});
|
||||
}
|
||||
},
|
||||
{ flush: 'post' },
|
||||
);
|
||||
|
||||
// Table layout
|
||||
const mergedTableLayout = computed(() => {
|
||||
if (props.tableLayout) {
|
||||
return props.tableLayout;
|
||||
}
|
||||
// https://github.com/ant-design/ant-design/issues/25227
|
||||
// When scroll.x is max-content, no need to fix table layout
|
||||
// it's width should stretch out to fit content
|
||||
if (fixColumn.value) {
|
||||
return props.scroll.x === 'max-content' ? 'auto' : 'fixed';
|
||||
}
|
||||
if (
|
||||
fixHeader.value ||
|
||||
stickyState.value.isSticky ||
|
||||
flattenColumns.value.some(({ ellipsis }) => ellipsis)
|
||||
) {
|
||||
return 'fixed';
|
||||
}
|
||||
return 'auto';
|
||||
});
|
||||
|
||||
const emptyNode = () => {
|
||||
return hasData.value ? null : slots.emptyText?.() || 'No Data';
|
||||
};
|
||||
useProvideTable(
|
||||
reactive({
|
||||
...reactivePick(props, 'prefixCls', 'direction', 'transformCellText'),
|
||||
getComponent,
|
||||
scrollbarSize,
|
||||
fixedInfoList: computed(() =>
|
||||
flattenColumns.value.map((_, colIndex) =>
|
||||
getCellFixedInfo(
|
||||
colIndex,
|
||||
colIndex,
|
||||
flattenColumns.value,
|
||||
stickyOffsets.value,
|
||||
props.direction,
|
||||
),
|
||||
),
|
||||
),
|
||||
isSticky: computed(() => stickyState.value.isSticky),
|
||||
summaryCollect,
|
||||
}),
|
||||
);
|
||||
|
||||
useProvideBody(
|
||||
reactive({
|
||||
...reactivePick(
|
||||
props,
|
||||
'rowClassName',
|
||||
'expandedRowClassName',
|
||||
'expandRowByClick',
|
||||
'expandedRowRender',
|
||||
'expandIconColumnIndex',
|
||||
'indentSize',
|
||||
),
|
||||
columns,
|
||||
flattenColumns,
|
||||
tableLayout: mergedTableLayout,
|
||||
componentWidth,
|
||||
fixHeader,
|
||||
fixColumn,
|
||||
horizonScroll,
|
||||
expandIcon: mergedExpandIcon,
|
||||
expandableType,
|
||||
onTriggerExpand,
|
||||
}),
|
||||
);
|
||||
|
||||
useProvideResize({
|
||||
onColumnResize,
|
||||
});
|
||||
|
||||
// Body
|
||||
const bodyTable = () => (
|
||||
<Body
|
||||
data={mergedData.value}
|
||||
measureColumnWidth={fixHeader.value || horizonScroll.value || stickyState.value.isSticky}
|
||||
expandedKeys={mergedExpandedKeys.value}
|
||||
rowExpandable={props.rowExpandable}
|
||||
getRowKey={getRowKey.value}
|
||||
customRow={props.customRow}
|
||||
childrenColumnName={mergedChildrenColumnName.value}
|
||||
v-slots={{ emptyNode }}
|
||||
/>
|
||||
);
|
||||
|
||||
const bodyColGroup = () => (
|
||||
<ColGroup
|
||||
colWidths={flattenColumns.value.map(({ width }) => width)}
|
||||
columns={flattenColumns.value}
|
||||
/>
|
||||
);
|
||||
return () => {
|
||||
const {
|
||||
prefixCls,
|
||||
scroll,
|
||||
tableLayout,
|
||||
direction,
|
||||
|
||||
// Additional Part
|
||||
title = slots.title,
|
||||
footer = slots.footer,
|
||||
|
||||
// Customize
|
||||
id,
|
||||
showHeader,
|
||||
customHeaderRow,
|
||||
} = props;
|
||||
const { isSticky, offsetHeader, offsetSummary, offsetScroll, stickyClassName, container } =
|
||||
stickyState.value;
|
||||
const TableComponent = getComponent(['table'], 'table');
|
||||
const customizeScrollBody = getComponent(['body']) as unknown as CustomizeScrollBody<any>;
|
||||
const summaryNode = slots.summary?.({ pageData: mergedData.value });
|
||||
|
||||
let groupTableNode = () => null;
|
||||
|
||||
// Header props
|
||||
const headerProps = {
|
||||
colWidths: colWidths.value,
|
||||
columCount: flattenColumns.value.length,
|
||||
stickyOffsets: stickyOffsets.value,
|
||||
customHeaderRow,
|
||||
fixHeader: fixHeader.value,
|
||||
scroll,
|
||||
};
|
||||
|
||||
if (
|
||||
process.env.NODE_ENV !== 'production' &&
|
||||
typeof customizeScrollBody === 'function' &&
|
||||
hasData.value &&
|
||||
!fixHeader.value
|
||||
) {
|
||||
warning(false, '`components.body` with render props is only work on `scroll.y`.');
|
||||
}
|
||||
if (fixHeader.value || isSticky) {
|
||||
// >>>>>> Fixed Header
|
||||
let bodyContent = () => null;
|
||||
|
||||
if (typeof customizeScrollBody === 'function') {
|
||||
bodyContent = () =>
|
||||
customizeScrollBody(mergedData.value, {
|
||||
scrollbarSize: scrollbarSize.value,
|
||||
ref: scrollBodyRef,
|
||||
onScroll,
|
||||
});
|
||||
|
||||
headerProps.colWidths = flattenColumns.value.map(({ width }, index) => {
|
||||
const colWidth =
|
||||
index === columns.value.length - 1 ? (width as number) - scrollbarSize.value : width;
|
||||
if (typeof colWidth === 'number' && !Number.isNaN(colWidth)) {
|
||||
return colWidth;
|
||||
}
|
||||
warning(
|
||||
false,
|
||||
'When use `components.body` with render props. Each column should have a fixed `width` value.',
|
||||
);
|
||||
|
||||
return 0;
|
||||
}) as number[];
|
||||
} else {
|
||||
bodyContent = () => (
|
||||
<div
|
||||
style={{
|
||||
...scrollXStyle.value,
|
||||
...scrollYStyle.value,
|
||||
}}
|
||||
onScroll={onScroll}
|
||||
ref={scrollBodyRef}
|
||||
class={classNames(`${prefixCls}-body`)}
|
||||
>
|
||||
<TableComponent
|
||||
style={{
|
||||
...scrollTableStyle.value,
|
||||
tableLayout: mergedTableLayout.value,
|
||||
}}
|
||||
>
|
||||
{bodyColGroup()}
|
||||
{bodyTable()}
|
||||
{!fixFooter.value && summaryNode && (
|
||||
<Footer stickyOffsets={stickyOffsets} flattenColumns={flattenColumns.value}>
|
||||
{summaryNode}
|
||||
</Footer>
|
||||
)}
|
||||
</TableComponent>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
// Fixed holder share the props
|
||||
const fixedHolderProps = {
|
||||
noData: !mergedData.value.length,
|
||||
maxContentScroll: horizonScroll.value && scroll.x === 'max-content',
|
||||
...headerProps,
|
||||
...columnContext.value,
|
||||
direction,
|
||||
stickyClassName,
|
||||
onScroll,
|
||||
};
|
||||
|
||||
groupTableNode = () => (
|
||||
<>
|
||||
{/* Header Table */}
|
||||
{showHeader !== false && (
|
||||
<FixedHolder
|
||||
{...fixedHolderProps}
|
||||
stickyTopOffset={offsetHeader}
|
||||
class={`${prefixCls}-header`}
|
||||
ref={scrollHeaderRef}
|
||||
v-slots={{
|
||||
default: fixedHolderPassProps => (
|
||||
<>
|
||||
<Header {...fixedHolderPassProps} />
|
||||
{fixFooter.value === 'top' && (
|
||||
<Footer {...fixedHolderPassProps}>{summaryNode}</Footer>
|
||||
)}
|
||||
</>
|
||||
),
|
||||
}}
|
||||
></FixedHolder>
|
||||
)}
|
||||
|
||||
{/* Body Table */}
|
||||
{bodyContent()}
|
||||
|
||||
{/* Summary Table */}
|
||||
{fixFooter.value && fixFooter.value !== 'top' && (
|
||||
<FixedHolder
|
||||
{...fixedHolderProps}
|
||||
stickyBottomOffset={offsetSummary}
|
||||
class={`${prefixCls}-summary`}
|
||||
ref={scrollSummaryRef}
|
||||
v-slots={{
|
||||
default: fixedHolderPassProps => (
|
||||
<Footer {...fixedHolderPassProps}>{summaryNode}</Footer>
|
||||
),
|
||||
}}
|
||||
></FixedHolder>
|
||||
)}
|
||||
|
||||
{isSticky && (
|
||||
<StickyScrollBar
|
||||
ref={stickyRef}
|
||||
offsetScroll={offsetScroll}
|
||||
scrollBodyRef={scrollBodyRef}
|
||||
onScroll={onScroll}
|
||||
container={container}
|
||||
/>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
} else {
|
||||
// >>>>>> Unique table
|
||||
groupTableNode = () => (
|
||||
<div
|
||||
style={{
|
||||
...scrollXStyle.value,
|
||||
...scrollYStyle.value,
|
||||
}}
|
||||
class={classNames(`${prefixCls}-content`)}
|
||||
onScroll={onScroll}
|
||||
ref={scrollBodyRef}
|
||||
>
|
||||
<TableComponent
|
||||
style={{ ...scrollTableStyle.value, tableLayout: mergedTableLayout.value }}
|
||||
>
|
||||
{bodyColGroup()}
|
||||
{showHeader !== false && <Header {...headerProps} {...columnContext.value} />}
|
||||
{bodyTable()}
|
||||
{summaryNode && (
|
||||
<Footer stickyOffsets={stickyOffsets.value} flattenColumns={flattenColumns.value}>
|
||||
{summaryNode}
|
||||
</Footer>
|
||||
)}
|
||||
</TableComponent>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
const ariaProps = getDataAndAriaProps(attrs);
|
||||
const fullTable = () => (
|
||||
<div
|
||||
{...ariaProps}
|
||||
class={classNames(prefixCls, {
|
||||
[`${prefixCls}-rtl`]: direction === 'rtl',
|
||||
[`${prefixCls}-ping-left`]: pingedLeft.value,
|
||||
[`${prefixCls}-ping-right`]: pingedRight.value,
|
||||
[`${prefixCls}-layout-fixed`]: tableLayout === 'fixed',
|
||||
[`${prefixCls}-fixed-header`]: fixHeader.value,
|
||||
/** No used but for compatible */
|
||||
[`${prefixCls}-fixed-column`]: fixColumn.value,
|
||||
[`${prefixCls}-scroll-horizontal`]: horizonScroll.value,
|
||||
[`${prefixCls}-has-fix-left`]: flattenColumns.value[0] && flattenColumns.value[0].fixed,
|
||||
[`${prefixCls}-has-fix-right`]:
|
||||
flattenColumns.value[columnCount.value - 1] &&
|
||||
flattenColumns.value[columnCount.value - 1].fixed === 'right',
|
||||
[attrs.class as string]: attrs.class,
|
||||
})}
|
||||
style={attrs.style}
|
||||
id={id}
|
||||
ref={fullTableRef}
|
||||
>
|
||||
{title && <Panel class={`${prefixCls}-title`}>{title(mergedData.value)}</Panel>}
|
||||
<div class={`${prefixCls}-container`}>{groupTableNode()}</div>
|
||||
{footer && <Panel class={`${prefixCls}-footer`}>{footer(mergedData.value)}</Panel>}
|
||||
</div>
|
||||
);
|
||||
|
||||
if (horizonScroll.value) {
|
||||
return (
|
||||
<VCResizeObserver
|
||||
onResize={onFullTableResize}
|
||||
v-slots={{ default: fullTable }}
|
||||
></VCResizeObserver>
|
||||
);
|
||||
}
|
||||
return fullTable();
|
||||
};
|
||||
},
|
||||
});
|
|
@ -1,60 +0,0 @@
|
|||
.move-enter,
|
||||
.move-appear {
|
||||
opacity: 0;
|
||||
animation-timing-function: cubic-bezier(0.215, 0.61, 0.355, 1);
|
||||
animation-duration: 2.5s;
|
||||
animation-fill-mode: both;
|
||||
animation-play-state: paused;
|
||||
}
|
||||
|
||||
.move-leave {
|
||||
animation-timing-function: cubic-bezier(0.55, 0.055, 0.675, 0.19);
|
||||
animation-duration: 0.5s;
|
||||
animation-fill-mode: both;
|
||||
animation-play-state: paused;
|
||||
}
|
||||
|
||||
.move-enter.move-enter-active,
|
||||
.move-appear.move-enter-active {
|
||||
animation-name: moveLeftIn;
|
||||
animation-play-state: running;
|
||||
}
|
||||
|
||||
.move-leave.move-leave-active {
|
||||
animation-name: moveRightOut;
|
||||
animation-play-state: running;
|
||||
}
|
||||
|
||||
@keyframes moveLeftIn {
|
||||
0% {
|
||||
transform-origin: 0 0;
|
||||
transform: translateX(30px);
|
||||
opacity: 0;
|
||||
background: #fff6de;
|
||||
}
|
||||
20% {
|
||||
transform-origin: 0 0;
|
||||
transform: translateX(0);
|
||||
opacity: 1;
|
||||
}
|
||||
80% {
|
||||
background: #fff6de;
|
||||
}
|
||||
100% {
|
||||
background: transparent;
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes moveRightOut {
|
||||
0% {
|
||||
transform-origin: 0 0;
|
||||
transform: translateX(0);
|
||||
opacity: 1;
|
||||
}
|
||||
100% {
|
||||
transform-origin: 0 0;
|
||||
transform: translateX(-30px);
|
||||
opacity: 0;
|
||||
}
|
||||
}
|
|
@ -1,12 +0,0 @@
|
|||
@tablePrefixCls: rc-table;
|
||||
@table-border-color: #e9e9e9;
|
||||
|
||||
.@{tablePrefixCls}.bordered {
|
||||
table {
|
||||
border-collapse: collapse;
|
||||
}
|
||||
th,
|
||||
td {
|
||||
border: 1px solid @table-border-color;
|
||||
}
|
||||
}
|
|
@ -1,233 +0,0 @@
|
|||
@tablePrefixCls: rc-table;
|
||||
@text-color : #666;
|
||||
@font-size-base : 12px;
|
||||
@line-height: 1.5;
|
||||
@table-border-color: #e9e9e9;
|
||||
@table-head-background-color: #f7f7f7;
|
||||
@vertical-padding: 16px;
|
||||
@horizontal-padding: 8px;
|
||||
|
||||
.@{tablePrefixCls} {
|
||||
font-size: @font-size-base;
|
||||
color: @text-color;
|
||||
transition: opacity 0.3s ease;
|
||||
position: relative;
|
||||
line-height: @line-height;
|
||||
overflow: hidden;
|
||||
|
||||
.@{tablePrefixCls}-scroll {
|
||||
overflow: auto;
|
||||
table {
|
||||
width: auto;
|
||||
min-width: 100%;
|
||||
}
|
||||
}
|
||||
|
||||
.@{tablePrefixCls}-header {
|
||||
overflow: hidden;
|
||||
background: @table-head-background-color;
|
||||
}
|
||||
|
||||
&-fixed-header &-body {
|
||||
background: #fff;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
&-fixed-header &-body-inner {
|
||||
height: 100%;
|
||||
overflow: scroll;
|
||||
}
|
||||
|
||||
&-fixed-header &-scroll &-header {
|
||||
overflow-x: scroll;
|
||||
padding-bottom: 20px;
|
||||
margin-bottom: -20px;
|
||||
overflow-y: scroll;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
// https://github.com/ant-design/ant-design/issues/10828
|
||||
&-fixed-columns-in-body {
|
||||
visibility: hidden;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.@{tablePrefixCls}-title {
|
||||
padding: @vertical-padding @horizontal-padding;
|
||||
border-top: 1px solid @table-border-color;
|
||||
}
|
||||
|
||||
.@{tablePrefixCls}-content {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.@{tablePrefixCls}-footer {
|
||||
padding: @vertical-padding @horizontal-padding;
|
||||
border-bottom: 1px solid @table-border-color;
|
||||
}
|
||||
|
||||
.@{tablePrefixCls}-placeholder {
|
||||
padding: 16px 8px;
|
||||
background: #fff;
|
||||
border-bottom: 1px solid @table-border-color;
|
||||
text-align: center;
|
||||
position: relative;
|
||||
&-fixed-columns {
|
||||
position: absolute;
|
||||
bottom: 0;
|
||||
width: 100%;
|
||||
background: transparent;
|
||||
pointer-events: none;
|
||||
}
|
||||
}
|
||||
|
||||
table {
|
||||
width: 100%;
|
||||
border-collapse: separate;
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
th {
|
||||
background: @table-head-background-color;
|
||||
font-weight: bold;
|
||||
transition: background 0.3s ease;
|
||||
}
|
||||
|
||||
td {
|
||||
border-bottom: 1px solid @table-border-color;
|
||||
&:empty:after {
|
||||
content: '.'; // empty cell placeholder
|
||||
visibility: hidden;
|
||||
}
|
||||
}
|
||||
|
||||
tr {
|
||||
transition: all 0.3s ease;
|
||||
&:hover {
|
||||
background: #eaf8fe;
|
||||
}
|
||||
&.@{tablePrefixCls}-row-hover {
|
||||
background: #eaf8fe;
|
||||
}
|
||||
}
|
||||
|
||||
th,
|
||||
td {
|
||||
padding: @vertical-padding @horizontal-padding;
|
||||
white-space: nowrap;
|
||||
}
|
||||
}
|
||||
|
||||
.@{tablePrefixCls} {
|
||||
&-expand-icon-col {
|
||||
width: 34px;
|
||||
}
|
||||
&-row,
|
||||
&-expanded-row {
|
||||
&-expand-icon {
|
||||
cursor: pointer;
|
||||
display: inline-block;
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
text-align: center;
|
||||
line-height: 16px;
|
||||
border: 1px solid @table-border-color;
|
||||
user-select: none;
|
||||
background: #fff;
|
||||
}
|
||||
&-spaced {
|
||||
visibility: hidden;
|
||||
}
|
||||
&-spaced:after {
|
||||
content: '.';
|
||||
}
|
||||
|
||||
&-expanded:after {
|
||||
content: '-';
|
||||
}
|
||||
|
||||
&-collapsed:after {
|
||||
content: '+';
|
||||
}
|
||||
}
|
||||
tr&-expanded-row {
|
||||
background: #f7f7f7;
|
||||
&:hover {
|
||||
background: #f7f7f7;
|
||||
}
|
||||
}
|
||||
&-column-hidden {
|
||||
display: none;
|
||||
}
|
||||
&-prev-columns-page,
|
||||
&-next-columns-page {
|
||||
cursor: pointer;
|
||||
color: #666;
|
||||
z-index: 1;
|
||||
&:hover {
|
||||
color: #2db7f5;
|
||||
}
|
||||
&-disabled {
|
||||
cursor: not-allowed;
|
||||
color: #999;
|
||||
&:hover {
|
||||
color: #999;
|
||||
}
|
||||
}
|
||||
}
|
||||
&-prev-columns-page {
|
||||
margin-right: 8px;
|
||||
&:before {
|
||||
content: '<';
|
||||
}
|
||||
}
|
||||
&-next-columns-page {
|
||||
float: right;
|
||||
&:before {
|
||||
content: '>';
|
||||
}
|
||||
}
|
||||
|
||||
&-fixed-left,
|
||||
&-fixed-right {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
overflow: hidden;
|
||||
table {
|
||||
width: auto;
|
||||
background: #fff;
|
||||
}
|
||||
}
|
||||
|
||||
&-fixed-left {
|
||||
left: 0;
|
||||
box-shadow: 4px 0 4px rgba(100, 100, 100, 0.1);
|
||||
& .@{tablePrefixCls}-body-inner {
|
||||
margin-right: -20px;
|
||||
padding-right: 20px;
|
||||
}
|
||||
.@{tablePrefixCls}-fixed-header & .@{tablePrefixCls}-body-inner {
|
||||
padding-right: 0;
|
||||
}
|
||||
}
|
||||
|
||||
&-fixed-right {
|
||||
right: 0;
|
||||
box-shadow: -4px 0 4px rgba(100, 100, 100, 0.1);
|
||||
|
||||
// hide expand row content in right fixed Table
|
||||
// https://github.com/ant-design/ant-design/issues/1898
|
||||
.@{tablePrefixCls}-expanded-row {
|
||||
color: transparent;
|
||||
pointer-events: none;
|
||||
}
|
||||
}
|
||||
|
||||
&&-scroll-position-left &-fixed-left {
|
||||
box-shadow: none;
|
||||
}
|
||||
|
||||
&&-scroll-position-right &-fixed-right {
|
||||
box-shadow: none;
|
||||
}
|
||||
}
|
|
@ -1,220 +0,0 @@
|
|||
/*! normalize.css v3.0.1 | MIT License | git.io/normalize */
|
||||
|
||||
html {
|
||||
font-family: sans-serif;
|
||||
-ms-text-size-adjust: 100%;
|
||||
-webkit-text-size-adjust: 100%
|
||||
}
|
||||
|
||||
body {
|
||||
margin: 0
|
||||
}
|
||||
|
||||
article,
|
||||
aside,
|
||||
details,
|
||||
figcaption,
|
||||
figure,
|
||||
footer,
|
||||
header,
|
||||
hgroup,
|
||||
main,
|
||||
nav,
|
||||
section,
|
||||
summary {
|
||||
display: block
|
||||
}
|
||||
|
||||
audio,
|
||||
canvas,
|
||||
progress,
|
||||
video {
|
||||
display: inline-block;
|
||||
vertical-align: baseline
|
||||
}
|
||||
|
||||
audio:not([controls]) {
|
||||
display: none;
|
||||
height: 0
|
||||
}
|
||||
|
||||
[hidden],
|
||||
template {
|
||||
display: none
|
||||
}
|
||||
|
||||
a {
|
||||
background: 0 0
|
||||
}
|
||||
|
||||
a:active,
|
||||
a:hover {
|
||||
outline: 0
|
||||
}
|
||||
|
||||
abbr[title] {
|
||||
border-bottom: 1px dotted
|
||||
}
|
||||
|
||||
b,
|
||||
strong {
|
||||
font-weight: 700
|
||||
}
|
||||
|
||||
dfn {
|
||||
font-style: italic
|
||||
}
|
||||
|
||||
h1 {
|
||||
font-size: 2em;
|
||||
margin: .67em 0
|
||||
}
|
||||
|
||||
mark {
|
||||
background: #ff0;
|
||||
color: #000
|
||||
}
|
||||
|
||||
small {
|
||||
font-size: 80%
|
||||
}
|
||||
|
||||
sub,
|
||||
sup {
|
||||
font-size: 75%;
|
||||
line-height: 0;
|
||||
position: relative;
|
||||
vertical-align: baseline
|
||||
}
|
||||
|
||||
sup {
|
||||
top: -.5em
|
||||
}
|
||||
|
||||
sub {
|
||||
bottom: -.25em
|
||||
}
|
||||
|
||||
img {
|
||||
border: 0
|
||||
}
|
||||
|
||||
svg:not(:root) {
|
||||
overflow: hidden
|
||||
}
|
||||
|
||||
figure {
|
||||
margin: 1em 40px
|
||||
}
|
||||
|
||||
hr {
|
||||
-moz-box-sizing: content-box;
|
||||
box-sizing: content-box;
|
||||
height: 0
|
||||
}
|
||||
|
||||
pre {
|
||||
overflow: auto
|
||||
}
|
||||
|
||||
code,
|
||||
kbd,
|
||||
pre,
|
||||
samp {
|
||||
font-family: monospace, monospace;
|
||||
font-size: 1em
|
||||
}
|
||||
|
||||
button,
|
||||
input,
|
||||
optgroup,
|
||||
select,
|
||||
textarea {
|
||||
color: inherit;
|
||||
font: inherit;
|
||||
margin: 0
|
||||
}
|
||||
|
||||
button {
|
||||
overflow: visible
|
||||
}
|
||||
|
||||
button,
|
||||
select {
|
||||
text-transform: none
|
||||
}
|
||||
|
||||
button,
|
||||
html input[type=button],
|
||||
input[type=reset],
|
||||
input[type=submit] {
|
||||
-webkit-appearance: button;
|
||||
cursor: pointer
|
||||
}
|
||||
|
||||
button[disabled],
|
||||
html input[disabled] {
|
||||
cursor: default
|
||||
}
|
||||
|
||||
button::-moz-focus-inner,
|
||||
input::-moz-focus-inner {
|
||||
border: 0;
|
||||
padding: 0
|
||||
}
|
||||
|
||||
input {
|
||||
line-height: normal
|
||||
}
|
||||
|
||||
input[type=checkbox],
|
||||
input[type=radio] {
|
||||
box-sizing: border-box;
|
||||
padding: 0
|
||||
}
|
||||
|
||||
input[type=number]::-webkit-inner-spin-button,
|
||||
input[type=number]::-webkit-outer-spin-button {
|
||||
height: auto
|
||||
}
|
||||
|
||||
input[type=search] {
|
||||
-webkit-appearance: textfield;
|
||||
-moz-box-sizing: content-box;
|
||||
-webkit-box-sizing: content-box;
|
||||
box-sizing: content-box
|
||||
}
|
||||
|
||||
input[type=search]::-webkit-search-cancel-button,
|
||||
input[type=search]::-webkit-search-decoration {
|
||||
-webkit-appearance: none
|
||||
}
|
||||
|
||||
fieldset {
|
||||
border: 1px solid silver;
|
||||
margin: 0 2px;
|
||||
padding: .35em .625em .75em
|
||||
}
|
||||
|
||||
legend {
|
||||
border: 0;
|
||||
padding: 0
|
||||
}
|
||||
|
||||
textarea {
|
||||
overflow: auto
|
||||
}
|
||||
|
||||
optgroup {
|
||||
font-weight: 700
|
||||
}
|
||||
|
||||
table {
|
||||
border-collapse: collapse;
|
||||
border-spacing: 0
|
||||
}
|
||||
|
||||
td,
|
||||
th {
|
||||
padding: 0
|
||||
}
|
|
@ -0,0 +1,44 @@
|
|||
import type {
|
||||
ColumnType,
|
||||
DefaultRecordType,
|
||||
ColumnsType,
|
||||
TableLayout,
|
||||
RenderExpandIcon,
|
||||
ExpandableType,
|
||||
RowClassName,
|
||||
TriggerEventHandler,
|
||||
ExpandedRowRender,
|
||||
} from '../interface';
|
||||
import type { InjectionKey } from 'vue';
|
||||
import { inject, provide } from 'vue';
|
||||
|
||||
export interface BodyContextProps<RecordType = DefaultRecordType> {
|
||||
rowClassName: string | RowClassName<RecordType>;
|
||||
expandedRowClassName: RowClassName<RecordType>;
|
||||
|
||||
columns: ColumnsType<RecordType>;
|
||||
flattenColumns: readonly ColumnType<RecordType>[];
|
||||
|
||||
componentWidth: number;
|
||||
tableLayout: TableLayout;
|
||||
fixHeader: boolean;
|
||||
fixColumn: boolean;
|
||||
horizonScroll: boolean;
|
||||
|
||||
indentSize: number;
|
||||
expandableType: ExpandableType;
|
||||
expandRowByClick: boolean;
|
||||
expandedRowRender: ExpandedRowRender<RecordType>;
|
||||
expandIcon: RenderExpandIcon<RecordType>;
|
||||
onTriggerExpand: TriggerEventHandler<RecordType>;
|
||||
expandIconColumnIndex: number;
|
||||
}
|
||||
export const BodyContextKey: InjectionKey<BodyContextProps> = Symbol('BodyContextProps');
|
||||
|
||||
export const useProvideBody = (props: BodyContextProps) => {
|
||||
provide(BodyContextKey, props);
|
||||
};
|
||||
|
||||
export const useInjectBody = () => {
|
||||
return inject(BodyContextKey, {} as BodyContextProps);
|
||||
};
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue