refactor:transfer (#6247)
* refactor:transfer * merge v4 branch & fix theme interface conflict * docs:update & refactor: transfer typepull/6263/head^2
parent
bbfb3cef7d
commit
68d295d7ef
|
@ -41,7 +41,7 @@ import './table/style';
|
|||
// import './progress/style';
|
||||
import './timeline/style';
|
||||
import './input-number/style';
|
||||
import './transfer/style';
|
||||
// import './transfer/style';
|
||||
import './tree/style';
|
||||
import './upload/style';
|
||||
// import './layout/style';
|
||||
|
|
|
@ -42,7 +42,7 @@ import type { ComponentToken as SpinComponentToken } from '../../spin/style';
|
|||
import type { ComponentToken as TagComponentToken } from '../../tag/style';
|
||||
// import type { ComponentToken as TimelineComponentToken } from '../../timeline/style';
|
||||
import type { ComponentToken as TooltipComponentToken } from '../../tooltip/style';
|
||||
// import type { ComponentToken as TransferComponentToken } from '../../transfer/style';
|
||||
import type { ComponentToken as TransferComponentToken } from '../../transfer/style';
|
||||
import type { ComponentToken as TypographyComponentToken } from '../../typography/style';
|
||||
// import type { ComponentToken as UploadComponentToken } from '../../upload/style';
|
||||
// import type { ComponentToken as TourComponentToken } from '../../tour/style';
|
||||
|
@ -100,7 +100,7 @@ export interface ComponentTokenMap {
|
|||
TreeSelect?: {};
|
||||
Typography?: TypographyComponentToken;
|
||||
// Timeline?: TimelineComponentToken;
|
||||
// Transfer?: TransferComponentToken;
|
||||
Transfer?: TransferComponentToken;
|
||||
// Tabs?: TabsComponentToken;
|
||||
// Calendar?: CalendarComponentToken;
|
||||
// Steps?: StepsComponentToken;
|
||||
|
|
|
@ -5,13 +5,14 @@ import ListItem from './ListItem';
|
|||
import Pagination from '../pagination';
|
||||
import PropTypes from '../_util/vue-types';
|
||||
import type { TransferItem } from '.';
|
||||
import { booleanType } from '../_util/type';
|
||||
|
||||
export const transferListBodyProps = {
|
||||
prefixCls: String,
|
||||
filteredRenderItems: PropTypes.array.def([]),
|
||||
selectedKeys: PropTypes.array,
|
||||
disabled: { type: Boolean, default: undefined },
|
||||
showRemove: { type: Boolean, default: undefined },
|
||||
disabled: booleanType(),
|
||||
showRemove: booleanType(),
|
||||
pagination: PropTypes.any,
|
||||
onItemSelect: Function,
|
||||
onScroll: Function,
|
||||
|
|
|
@ -8,6 +8,7 @@ import TransButton from '../_util/transButton';
|
|||
import LocaleReceiver from '../locale-provider/LocaleReceiver';
|
||||
import type { ExtractPropTypes } from 'vue';
|
||||
import { defineComponent } from 'vue';
|
||||
import { booleanType } from '../_util/type';
|
||||
|
||||
function noop() {}
|
||||
|
||||
|
@ -15,10 +16,10 @@ export const transferListItemProps = {
|
|||
renderedText: PropTypes.any,
|
||||
renderedEl: PropTypes.any,
|
||||
item: PropTypes.any,
|
||||
checked: { type: Boolean, default: undefined },
|
||||
checked: booleanType(),
|
||||
prefixCls: String,
|
||||
disabled: { type: Boolean, default: undefined },
|
||||
showRemove: { type: Boolean, default: undefined },
|
||||
disabled: booleanType(),
|
||||
showRemove: booleanType(),
|
||||
onClick: Function,
|
||||
onRemove: Function,
|
||||
};
|
||||
|
|
|
@ -0,0 +1,102 @@
|
|||
<docs>
|
||||
---
|
||||
order: 4
|
||||
title:
|
||||
zh-CN: 自定义全选文字
|
||||
en-US: Custom Select All Labels
|
||||
---
|
||||
|
||||
|
||||
## zh-CN
|
||||
|
||||
自定义穿梭框全选按钮的文字。
|
||||
|
||||
## en-US
|
||||
|
||||
Custom the labels for select all checkboxs.
|
||||
|
||||
</docs>
|
||||
|
||||
<template>
|
||||
<div>
|
||||
<a-transfer
|
||||
v-model:target-keys="targetKeys"
|
||||
v-model:selected-keys="selectedKeys"
|
||||
:data-source="mockData"
|
||||
:titles="['Source', 'Target']"
|
||||
:render="item => item.title"
|
||||
:select-all-labels="selectAllLabels"
|
||||
:disabled="disabled"
|
||||
@change="handleChange"
|
||||
@selectChange="handleSelectChange"
|
||||
@scroll="handleScroll"
|
||||
/>
|
||||
<a-switch
|
||||
v-model:checked="disabled"
|
||||
un-checked-children="enabled"
|
||||
checked-children="disabled"
|
||||
style="margin-top: 16px"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
<script lang="ts">
|
||||
import { defineComponent, ref } from 'vue';
|
||||
import type { SelectAllLabel } from 'ant-design-vue/es/transfer';
|
||||
|
||||
interface MockData {
|
||||
key: string;
|
||||
title: string;
|
||||
description: string;
|
||||
disabled: boolean;
|
||||
}
|
||||
const mockData: MockData[] = [];
|
||||
for (let i = 0; i < 20; i++) {
|
||||
mockData.push({
|
||||
key: i.toString(),
|
||||
title: `content${i + 1}`,
|
||||
description: `description of content${i + 1}`,
|
||||
disabled: i % 3 < 1,
|
||||
});
|
||||
}
|
||||
|
||||
const oriTargetKeys = mockData.filter(item => +item.key % 3 > 1).map(item => item.key);
|
||||
export default defineComponent({
|
||||
data() {
|
||||
const disabled = ref<boolean>(false);
|
||||
|
||||
const targetKeys = ref<string[]>(oriTargetKeys);
|
||||
|
||||
const selectedKeys = ref<string[]>(['1', '4']);
|
||||
|
||||
const handleChange = (nextTargetKeys: string[], direction: string, moveKeys: string[]) => {
|
||||
console.log('targetKeys: ', nextTargetKeys);
|
||||
console.log('direction: ', direction);
|
||||
console.log('moveKeys: ', moveKeys);
|
||||
};
|
||||
const handleSelectChange = (sourceSelectedKeys: string[], targetSelectedKeys: string[]) => {
|
||||
console.log('sourceSelectedKeys: ', sourceSelectedKeys);
|
||||
console.log('targetSelectedKeys: ', targetSelectedKeys);
|
||||
};
|
||||
const handleScroll = (direction: string, e: Event) => {
|
||||
console.log('direction:', direction);
|
||||
console.log('target:', e.target);
|
||||
};
|
||||
|
||||
const selectAllLabels: SelectAllLabel[] = [
|
||||
'Select All',
|
||||
({ selectedCount, totalCount }) => `${selectedCount}/${totalCount}`,
|
||||
];
|
||||
|
||||
return {
|
||||
mockData,
|
||||
targetKeys,
|
||||
selectedKeys,
|
||||
disabled,
|
||||
selectAllLabels,
|
||||
handleChange,
|
||||
handleSelectChange,
|
||||
handleScroll,
|
||||
};
|
||||
},
|
||||
});
|
||||
</script>
|
|
@ -9,6 +9,7 @@
|
|||
<table-transfer />
|
||||
<tree-transfer />
|
||||
<statusVue />
|
||||
<custom-select-all-labels></custom-select-all-labels>
|
||||
</demo-sort>
|
||||
</template>
|
||||
<script lang="ts">
|
||||
|
@ -17,6 +18,7 @@ import Oneway from './oneway.vue';
|
|||
import Search from './search.vue';
|
||||
import Advanced from './advanced.vue';
|
||||
import CustomItem from './custom-item.vue';
|
||||
import CustomSelectAllLabels from './custom-select-all-labels.vue';
|
||||
import TableTransfer from './table-transfer.vue';
|
||||
import TreeTransfer from './tree-transfer.vue';
|
||||
import Pagination from './pagination.vue';
|
||||
|
@ -35,6 +37,7 @@ export default defineComponent({
|
|||
Search,
|
||||
Advanced,
|
||||
CustomItem,
|
||||
CustomSelectAllLabels,
|
||||
Pagination,
|
||||
TableTransfer,
|
||||
TreeTransfer,
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
category: Components
|
||||
type: Data Entry
|
||||
title: Transfer
|
||||
cover: https://gw.alipayobjects.com/zos/alicdn/QAXskNI4G/Transfer.svg
|
||||
cover: https://mdn.alipayobjects.com/huamei_7uahnr/afts/img/A*EAApQ5ephigAAAAAAAAAAAAADrJ8AQ/original
|
||||
---
|
||||
|
||||
Alert component for feedback.
|
||||
|
@ -31,6 +31,7 @@ One or more elements can be selected from either column, one click on the proper
|
|||
| operationStyle | A custom CSS style used for rendering the operations column | CSSProperties | - | 3.0.0 |
|
||||
| pagination | Use pagination. Not work in render props | boolean \| { pageSize: number, simple: boolean, showSizeChanger?: boolean, showLessItems?: boolean } | false | 3.0.0 |
|
||||
| render | The function to generate the item shown on a column. Based on an record (element of the dataSource array), this function should return a element which is generated from that record. Also, it can return a plain object with `value` and `label`, `label` is a element and `value` is for title | Function(record) \| slot | | |
|
||||
| selectAllLabels | A set of customized labels for select all checkboxes on the header | VueNode \| ((info: { selectedCount: number; totalCount: number }) => VueNode); | - | 3.0.0 |
|
||||
| selectedKeys(v-model) | A set of keys of selected items. | string\[] | \[] | |
|
||||
| showSearch | If included, a search box is shown on each column. | boolean | false | |
|
||||
| showSelectAll | Show select all checkbox on the header | boolean | true | |
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import type { CSSProperties, ExtractPropTypes, PropType } from 'vue';
|
||||
import type { CSSProperties, ExtractPropTypes } from 'vue';
|
||||
import { computed, watchEffect, defineComponent, ref, watch, toRaw } from 'vue';
|
||||
import PropTypes from '../_util/vue-types';
|
||||
import { getPropsSlot } from '../_util/props-util';
|
||||
|
@ -8,7 +8,15 @@ import Operation from './operation';
|
|||
import LocaleReceiver from '../locale-provider/LocaleReceiver';
|
||||
import defaultLocale from '../locale/en_US';
|
||||
import type { VueNode } from '../_util/type';
|
||||
import { withInstall } from '../_util/type';
|
||||
import {
|
||||
withInstall,
|
||||
stringType,
|
||||
arrayType,
|
||||
someType,
|
||||
booleanType,
|
||||
objectType,
|
||||
functionType,
|
||||
} from '../_util/type';
|
||||
import useConfigInject from '../config-provider/hooks/useConfigInject';
|
||||
import type { TransferListBodyProps } from './ListBody';
|
||||
import type { PaginationType } from './interface';
|
||||
|
@ -17,6 +25,9 @@ import type { RenderEmptyHandler } from '../config-provider/renderEmpty';
|
|||
import type { InputStatus } from '../_util/statusUtils';
|
||||
import { getStatusClassNames, getMergedStatus } from '../_util/statusUtils';
|
||||
|
||||
// CSSINJS
|
||||
import useStyle from './style';
|
||||
|
||||
export type { TransferListProps } from './list';
|
||||
export type { TransferOperationProps } from './operation';
|
||||
export type { TransferSearchProps } from './search';
|
||||
|
@ -69,40 +80,41 @@ export interface TransferLocale {
|
|||
export const transferProps = () => ({
|
||||
id: String,
|
||||
prefixCls: String,
|
||||
dataSource: { type: Array as PropType<TransferItem[]>, default: [] },
|
||||
disabled: { type: Boolean, default: undefined },
|
||||
targetKeys: { type: Array as PropType<string[]>, default: undefined },
|
||||
selectedKeys: { type: Array as PropType<string[]>, default: undefined },
|
||||
render: { type: Function as PropType<TransferRender<TransferItem>> },
|
||||
listStyle: {
|
||||
type: [Function, Object] as PropType<((style: ListStyle) => CSSProperties) | CSSProperties>,
|
||||
default: () => ({}),
|
||||
},
|
||||
operationStyle: { type: Object as PropType<CSSProperties>, default: undefined as CSSProperties },
|
||||
titles: { type: Array as PropType<string[]> },
|
||||
operations: { type: Array as PropType<string[]> },
|
||||
showSearch: { type: Boolean, default: false },
|
||||
filterOption: { type: Function as PropType<(inputValue: string, item: TransferItem) => boolean> },
|
||||
dataSource: arrayType<TransferItem[]>([]),
|
||||
disabled: booleanType(),
|
||||
targetKeys: arrayType<string[]>(),
|
||||
selectedKeys: arrayType<string[]>(),
|
||||
render: functionType<TransferRender<TransferItem>>(),
|
||||
listStyle: someType<((style: ListStyle) => CSSProperties) | CSSProperties>(
|
||||
[Function, Object],
|
||||
() => ({}),
|
||||
),
|
||||
operationStyle: objectType<CSSProperties>(undefined as CSSProperties),
|
||||
titles: arrayType<string[]>(),
|
||||
operations: arrayType<string[]>(),
|
||||
showSearch: booleanType(false),
|
||||
filterOption: functionType<(inputValue: string, item: TransferItem) => boolean>(),
|
||||
searchPlaceholder: String,
|
||||
notFoundContent: PropTypes.any,
|
||||
locale: { type: Object as PropType<Partial<TransferLocale>>, default: () => ({}) },
|
||||
rowKey: { type: Function as PropType<(record: TransferItem) => string> },
|
||||
showSelectAll: { type: Boolean, default: undefined },
|
||||
selectAllLabels: { type: Array as PropType<SelectAllLabel[]> },
|
||||
children: { type: Function as PropType<(props: TransferListBodyProps) => VueNode> },
|
||||
oneWay: { type: Boolean, default: undefined },
|
||||
pagination: { type: [Object, Boolean] as PropType<PaginationType>, default: undefined },
|
||||
status: String as PropType<InputStatus>,
|
||||
onChange: Function as PropType<
|
||||
(targetKeys: string[], direction: TransferDirection, moveKeys: string[]) => void
|
||||
>,
|
||||
onSelectChange: Function as PropType<
|
||||
locale: objectType(),
|
||||
rowKey: functionType<(record: TransferItem) => string>(),
|
||||
showSelectAll: booleanType(),
|
||||
selectAllLabels: arrayType<SelectAllLabel[]>(),
|
||||
children: functionType<(props: TransferListBodyProps) => VueNode>(),
|
||||
oneWay: booleanType(),
|
||||
pagination: someType<PaginationType>([Object, Boolean]),
|
||||
status: stringType<InputStatus>(),
|
||||
onChange:
|
||||
functionType<
|
||||
(targetKeys: string[], direction: TransferDirection, moveKeys: string[]) => void
|
||||
>(),
|
||||
onSelectChange: functionType<
|
||||
(sourceSelectedKeys: string[], targetSelectedKeys: string[]) => void
|
||||
>,
|
||||
onSearch: Function as PropType<(direction: TransferDirection, value: string) => void>,
|
||||
onScroll: Function as PropType<(direction: TransferDirection, e: UIEvent) => void>,
|
||||
'onUpdate:targetKeys': Function as PropType<(keys: string[]) => void>,
|
||||
'onUpdate:selectedKeys': Function as PropType<(keys: string[]) => void>,
|
||||
onSearch: functionType<(direction: TransferDirection, value: string) => void>(),
|
||||
onScroll: functionType<(direction: TransferDirection, e: UIEvent) => void>(),
|
||||
'onUpdate:targetKeys': functionType<(keys: string[]) => void>(),
|
||||
'onUpdate:selectedKeys': functionType<(keys: string[]) => void>(),
|
||||
});
|
||||
|
||||
export type TransferProps = Partial<ExtractPropTypes<ReturnType<typeof transferProps>>>;
|
||||
|
@ -125,6 +137,10 @@ const Transfer = defineComponent({
|
|||
// emits: ['update:targetKeys', 'update:selectedKeys', 'change', 'search', 'scroll', 'selectChange'],
|
||||
setup(props, { emit, attrs, slots, expose }) {
|
||||
const { configProvider, prefixCls, direction } = useConfigInject('transfer', props);
|
||||
|
||||
// style
|
||||
const [wrapSSR, hashId] = useStyle(prefixCls);
|
||||
|
||||
const sourceSelectedKeys = ref([]);
|
||||
const targetSelectedKeys = ref([]);
|
||||
|
||||
|
@ -349,6 +365,7 @@ const Transfer = defineComponent({
|
|||
[`${prefixCls.value}-rtl`]: direction.value === 'rtl',
|
||||
},
|
||||
getStatusClassNames(prefixCls.value, mergedStatus.value, formItemInputContext.hasFeedback),
|
||||
hashId.value,
|
||||
);
|
||||
const titles = props.titles;
|
||||
const leftTitle =
|
||||
|
@ -356,7 +373,7 @@ const Transfer = defineComponent({
|
|||
const rightTitle =
|
||||
(titles && titles[1]) ?? slots.rightTitle?.() ?? (locale.titles || ['', ''])[1];
|
||||
return (
|
||||
<div class={cls} style={style as CSSProperties} id={id}>
|
||||
<div {...attrs} class={cls} style={style as CSSProperties} id={id}>
|
||||
<List
|
||||
key="leftList"
|
||||
prefixCls={`${prefixCls.value}-list`}
|
||||
|
@ -422,13 +439,14 @@ const Transfer = defineComponent({
|
|||
</div>
|
||||
);
|
||||
};
|
||||
return () => (
|
||||
<LocaleReceiver
|
||||
componentName="Transfer"
|
||||
defaultLocale={defaultLocale.Transfer}
|
||||
children={renderTransfer}
|
||||
/>
|
||||
);
|
||||
return () =>
|
||||
wrapSSR(
|
||||
<LocaleReceiver
|
||||
componentName="Transfer"
|
||||
defaultLocale={defaultLocale.Transfer}
|
||||
children={renderTransfer}
|
||||
/>,
|
||||
);
|
||||
},
|
||||
});
|
||||
|
||||
|
|
|
@ -3,7 +3,7 @@ category: Components
|
|||
type: 数据录入
|
||||
title: Transfer
|
||||
subtitle: 穿梭框
|
||||
cover: https://gw.alipayobjects.com/zos/alicdn/QAXskNI4G/Transfer.svg
|
||||
cover: https://mdn.alipayobjects.com/huamei_7uahnr/afts/img/A*EAApQ5ephigAAAAAAAAAAAAADrJ8AQ/original
|
||||
---
|
||||
|
||||
双栏穿梭选择框。
|
||||
|
@ -32,6 +32,7 @@ cover: https://gw.alipayobjects.com/zos/alicdn/QAXskNI4G/Transfer.svg
|
|||
| operationStyle | 操作栏的自定义样式 | CSSProperties | - | 3.0.0 |
|
||||
| pagination | 使用分页样式,自定义渲染列表下无效 | boolean \| { pageSize: number, simple: boolean, showSizeChanger?: boolean, showLessItems?: boolean } | flase | 3.0.0 |
|
||||
| render | 每行数据渲染函数,该函数的入参为 `dataSource` 中的项,返回值为 element。或者返回一个普通对象,其中 `label` 字段为 element,`value` 字段为 title | Function(record)\| slot | | |
|
||||
| selectAllLabels | 自定义顶部多选框标题的集合 | VueNode \| ((info: { selectedCount: number; totalCount: number }) => VueNode); | - | 3.0.0 |
|
||||
| selectedKeys(v-model) | 设置哪些项应该被选中 | string\[] | \[] | |
|
||||
| showSearch | 是否显示搜索框 | boolean | false | |
|
||||
| showSelectAll | 是否展示全选勾选框 | boolean | true | |
|
||||
|
|
|
@ -7,10 +7,11 @@ import Menu from '../menu';
|
|||
import Dropdown from '../dropdown';
|
||||
import Search from './search';
|
||||
import ListBody from './ListBody';
|
||||
import type { VNode, VNodeTypes, ExtractPropTypes, PropType, CSSProperties } from 'vue';
|
||||
import type { VNode, VNodeTypes, ExtractPropTypes, CSSProperties } from 'vue';
|
||||
import { watchEffect, computed, defineComponent, ref } from 'vue';
|
||||
import type { RadioChangeEvent } from '../radio/interface';
|
||||
import type { TransferDirection, TransferItem } from './index';
|
||||
import { stringType, arrayType, booleanType } from '../_util/type';
|
||||
|
||||
const defaultRender = () => null;
|
||||
|
||||
|
@ -28,22 +29,22 @@ function getEnabledItemKeys<RecordType extends TransferItem>(items: RecordType[]
|
|||
|
||||
export const transferListProps = {
|
||||
prefixCls: String,
|
||||
dataSource: { type: Array as PropType<TransferItem[]>, default: [] },
|
||||
dataSource: arrayType<TransferItem[]>([]),
|
||||
filter: String,
|
||||
filterOption: Function,
|
||||
checkedKeys: PropTypes.arrayOf(PropTypes.string),
|
||||
handleFilter: Function,
|
||||
handleClear: Function,
|
||||
renderItem: Function,
|
||||
showSearch: { type: Boolean, default: false },
|
||||
showSearch: booleanType(false),
|
||||
searchPlaceholder: String,
|
||||
notFoundContent: PropTypes.any,
|
||||
itemUnit: String,
|
||||
itemsUnit: String,
|
||||
renderList: PropTypes.any,
|
||||
disabled: { type: Boolean, default: undefined },
|
||||
direction: String as PropType<TransferDirection>,
|
||||
showSelectAll: { type: Boolean, default: undefined },
|
||||
disabled: booleanType(),
|
||||
direction: stringType<TransferDirection>(),
|
||||
showSelectAll: booleanType(),
|
||||
remove: String,
|
||||
selectAll: String,
|
||||
selectCurrent: String,
|
||||
|
@ -51,7 +52,7 @@ export const transferListProps = {
|
|||
removeAll: String,
|
||||
removeCurrent: String,
|
||||
selectAllLabel: PropTypes.any,
|
||||
showRemove: { type: Boolean, default: undefined },
|
||||
showRemove: booleanType(),
|
||||
pagination: PropTypes.any,
|
||||
onItemSelect: Function,
|
||||
onItemSelectAll: Function,
|
||||
|
|
|
@ -1,52 +0,0 @@
|
|||
@import '../../style/themes/index';
|
||||
|
||||
@transfer-prefix-cls: ~'@{ant-prefix}-transfer';
|
||||
|
||||
@table-prefix-cls: ~'@{ant-prefix}-table';
|
||||
@input-prefix-cls: ~'@{ant-prefix}-input';
|
||||
|
||||
.@{transfer-prefix-cls}-customize-list {
|
||||
.@{transfer-prefix-cls}-list {
|
||||
flex: 1 1 50%;
|
||||
width: auto;
|
||||
height: auto;
|
||||
min-height: @transfer-list-height;
|
||||
}
|
||||
|
||||
// =================== Hook Components ===================
|
||||
.@{table-prefix-cls}-wrapper {
|
||||
.@{table-prefix-cls}-small {
|
||||
border: 0;
|
||||
border-radius: 0;
|
||||
|
||||
.@{table-prefix-cls}-selection-column {
|
||||
width: 40px;
|
||||
min-width: 40px;
|
||||
}
|
||||
|
||||
> .@{table-prefix-cls}-content {
|
||||
// Header background color
|
||||
> .@{table-prefix-cls}-body > table > .@{table-prefix-cls}-thead > tr > th {
|
||||
background: @table-header-bg;
|
||||
}
|
||||
|
||||
.@{table-prefix-cls}-row:last-child td {
|
||||
border-bottom: @border-width-base @border-style-base @border-color-split;
|
||||
}
|
||||
}
|
||||
|
||||
.@{table-prefix-cls}-body {
|
||||
margin: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.@{table-prefix-cls}-pagination.@{ant-prefix}-pagination {
|
||||
margin: 16px 0 4px;
|
||||
}
|
||||
}
|
||||
.@{input-prefix-cls} {
|
||||
&[disabled] {
|
||||
background-color: transparent;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,221 +0,0 @@
|
|||
@import '../../style/themes/index';
|
||||
@import '../../style/mixins/index';
|
||||
@import '../../checkbox/style/mixin';
|
||||
@import './customize';
|
||||
@import './status';
|
||||
|
||||
@transfer-prefix-cls: ~'@{ant-prefix}-transfer';
|
||||
|
||||
@transfer-header-vertical-padding: ceil(
|
||||
((@transfer-header-height - 1px - @font-size-base * @line-height-base) / 2)
|
||||
);
|
||||
|
||||
.@{transfer-prefix-cls} {
|
||||
.reset-component();
|
||||
|
||||
position: relative;
|
||||
display: flex;
|
||||
align-items: stretch;
|
||||
|
||||
&-disabled {
|
||||
.@{transfer-prefix-cls}-list {
|
||||
background: @transfer-disabled-bg;
|
||||
}
|
||||
}
|
||||
|
||||
&-list {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
width: 180px;
|
||||
height: @transfer-list-height;
|
||||
border: @border-width-base @border-style-base @border-color-base;
|
||||
border-radius: @border-radius-base;
|
||||
|
||||
&-with-pagination {
|
||||
width: 250px;
|
||||
height: auto;
|
||||
}
|
||||
|
||||
&-search {
|
||||
.anticon-search {
|
||||
color: @disabled-color;
|
||||
}
|
||||
}
|
||||
|
||||
&-header {
|
||||
display: flex;
|
||||
flex: none;
|
||||
align-items: center;
|
||||
height: @transfer-header-height;
|
||||
// border-top is on the transfer dom. We should minus 1px for this
|
||||
padding: (@transfer-header-vertical-padding - 1px) @control-padding-horizontal
|
||||
@transfer-header-vertical-padding;
|
||||
color: @text-color;
|
||||
background: @component-background;
|
||||
border-bottom: @border-width-base @border-style-base @border-color-split;
|
||||
border-radius: @border-radius-base @border-radius-base 0 0;
|
||||
|
||||
> *:not(:last-child) {
|
||||
margin-right: 4px;
|
||||
}
|
||||
|
||||
> * {
|
||||
flex: none;
|
||||
}
|
||||
|
||||
&-title {
|
||||
flex: auto;
|
||||
overflow: hidden;
|
||||
white-space: nowrap;
|
||||
text-align: right;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
|
||||
&-dropdown {
|
||||
font-size: 10px;
|
||||
transform: translateY(10%);
|
||||
cursor: pointer;
|
||||
|
||||
&[disabled] {
|
||||
cursor: not-allowed;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&-body {
|
||||
display: flex;
|
||||
flex: auto;
|
||||
flex-direction: column;
|
||||
overflow: hidden;
|
||||
font-size: @font-size-base;
|
||||
|
||||
&-search-wrapper {
|
||||
position: relative;
|
||||
flex: none;
|
||||
padding: @padding-sm;
|
||||
}
|
||||
}
|
||||
|
||||
&-content {
|
||||
flex: auto;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
overflow: auto;
|
||||
list-style: none;
|
||||
|
||||
&-item {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
min-height: @transfer-item-height;
|
||||
padding: @transfer-item-padding-vertical @control-padding-horizontal;
|
||||
line-height: @transfer-item-height - 2 * @transfer-item-padding-vertical;
|
||||
transition: all 0.3s;
|
||||
|
||||
> *:not(:last-child) {
|
||||
margin-right: 8px;
|
||||
}
|
||||
|
||||
> * {
|
||||
flex: none;
|
||||
}
|
||||
|
||||
&-text {
|
||||
flex: auto;
|
||||
overflow: hidden;
|
||||
white-space: nowrap;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
|
||||
&-remove {
|
||||
.operation-unit();
|
||||
position: relative;
|
||||
color: @border-color-base;
|
||||
|
||||
&::after {
|
||||
position: absolute;
|
||||
top: -@transfer-item-padding-vertical;
|
||||
right: -50%;
|
||||
bottom: -@transfer-item-padding-vertical;
|
||||
left: -50%;
|
||||
content: '';
|
||||
}
|
||||
|
||||
&:hover {
|
||||
color: @link-hover-color;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&-item:not(&-item-disabled) {
|
||||
&:hover {
|
||||
background-color: @transfer-item-hover-bg;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
&.@{transfer-prefix-cls}-list-content-item-checked:hover {
|
||||
background-color: @transfer-item-selected-hover-bg;
|
||||
}
|
||||
}
|
||||
|
||||
// Do not change hover style when `oneWay` mode
|
||||
&-show-remove &-item:not(&-item-disabled):hover {
|
||||
background: transparent;
|
||||
cursor: default;
|
||||
}
|
||||
|
||||
&-item-checked {
|
||||
background-color: @item-active-bg;
|
||||
}
|
||||
|
||||
&-item-disabled {
|
||||
color: @btn-disable-color;
|
||||
cursor: not-allowed;
|
||||
}
|
||||
}
|
||||
|
||||
&-pagination {
|
||||
padding: @padding-xs 0;
|
||||
text-align: right;
|
||||
border-top: @border-width-base @border-style-base @border-color-split;
|
||||
}
|
||||
|
||||
&-body-not-found {
|
||||
flex: none;
|
||||
width: 100%;
|
||||
margin: auto 0;
|
||||
color: @disabled-color;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
&-footer {
|
||||
border-top: @border-width-base @border-style-base @border-color-split;
|
||||
}
|
||||
}
|
||||
|
||||
&-operation {
|
||||
display: flex;
|
||||
flex: none;
|
||||
flex-direction: column;
|
||||
align-self: center;
|
||||
margin: 0 8px;
|
||||
vertical-align: middle;
|
||||
|
||||
.@{ant-prefix}-btn {
|
||||
display: block;
|
||||
|
||||
&:first-child {
|
||||
margin-bottom: 4px;
|
||||
}
|
||||
|
||||
.@{iconfont-css-prefix} {
|
||||
font-size: 12px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.@{ant-prefix}-empty-image {
|
||||
max-height: (@transfer-header-height / 2) - 22;
|
||||
}
|
||||
}
|
||||
|
||||
@import './rtl';
|
|
@ -1,13 +1,373 @@
|
|||
import '../../style/index.less';
|
||||
import './index.less';
|
||||
import type { CSSObject } from '../../_util/cssinjs';
|
||||
|
||||
// style dependencies
|
||||
import '../../empty/style';
|
||||
import '../../checkbox/style';
|
||||
import '../../button/style';
|
||||
import '../../input/style';
|
||||
import '../../menu/style';
|
||||
import '../../dropdown/style';
|
||||
import '../../pagination/style';
|
||||
import type { FullToken, GenerateStyle } from '../../theme/internal';
|
||||
import { genComponentStyleHook, mergeToken } from '../../theme/internal';
|
||||
import { resetComponent, resetIcon, textEllipsis } from '../../_style';
|
||||
|
||||
// deps-lint-skip: form
|
||||
export interface ComponentToken {
|
||||
listWidth: number;
|
||||
listWidthLG: number;
|
||||
listHeight: number;
|
||||
}
|
||||
|
||||
interface TransferToken extends FullToken<'Transfer'> {
|
||||
transferItemHeight: number;
|
||||
transferHeaderVerticalPadding: number;
|
||||
transferItemPaddingVertical: number;
|
||||
transferHeaderHeight: number;
|
||||
}
|
||||
|
||||
const genTransferCustomizeStyle: GenerateStyle<TransferToken> = (
|
||||
token: TransferToken,
|
||||
): CSSObject => {
|
||||
const { antCls, componentCls, listHeight, controlHeightLG, marginXXS, margin } = token;
|
||||
|
||||
const tableCls = `${antCls}-table`;
|
||||
const inputCls = `${antCls}-input`;
|
||||
|
||||
return {
|
||||
[`${componentCls}-customize-list`]: {
|
||||
[`${componentCls}-list`]: {
|
||||
flex: '1 1 50%',
|
||||
width: 'auto',
|
||||
height: 'auto',
|
||||
minHeight: listHeight,
|
||||
},
|
||||
|
||||
// =================== Hook Components ===================
|
||||
[`${tableCls}-wrapper`]: {
|
||||
[`${tableCls}-small`]: {
|
||||
border: 0,
|
||||
borderRadius: 0,
|
||||
|
||||
[`${tableCls}-selection-column`]: {
|
||||
width: controlHeightLG,
|
||||
minWidth: controlHeightLG,
|
||||
},
|
||||
},
|
||||
|
||||
[`${tableCls}-pagination${tableCls}-pagination`]: {
|
||||
margin: `${margin}px 0 ${marginXXS}px`,
|
||||
},
|
||||
},
|
||||
|
||||
[`${inputCls}[disabled]`]: {
|
||||
backgroundColor: 'transparent',
|
||||
},
|
||||
},
|
||||
};
|
||||
};
|
||||
|
||||
const genTransferStatusColor = (token: TransferToken, color: string): CSSObject => {
|
||||
const { componentCls, colorBorder } = token;
|
||||
return {
|
||||
[`${componentCls}-list`]: {
|
||||
borderColor: color,
|
||||
|
||||
'&-search:not([disabled])': {
|
||||
borderColor: colorBorder,
|
||||
},
|
||||
},
|
||||
};
|
||||
};
|
||||
|
||||
const genTransferStatusStyle: GenerateStyle<TransferToken> = (token: TransferToken): CSSObject => {
|
||||
const { componentCls } = token;
|
||||
return {
|
||||
[`${componentCls}-status-error`]: {
|
||||
...genTransferStatusColor(token, token.colorError),
|
||||
},
|
||||
[`${componentCls}-status-warning`]: {
|
||||
...genTransferStatusColor(token, token.colorWarning),
|
||||
},
|
||||
};
|
||||
};
|
||||
|
||||
const genTransferListStyle: GenerateStyle<TransferToken> = (token: TransferToken): CSSObject => {
|
||||
const {
|
||||
componentCls,
|
||||
colorBorder,
|
||||
colorSplit,
|
||||
lineWidth,
|
||||
transferItemHeight,
|
||||
transferHeaderHeight,
|
||||
transferHeaderVerticalPadding,
|
||||
transferItemPaddingVertical,
|
||||
controlItemBgActive,
|
||||
controlItemBgActiveHover,
|
||||
colorTextDisabled,
|
||||
listHeight,
|
||||
listWidth,
|
||||
listWidthLG,
|
||||
fontSizeIcon,
|
||||
marginXS,
|
||||
paddingSM,
|
||||
lineType,
|
||||
iconCls,
|
||||
motionDurationSlow,
|
||||
} = token;
|
||||
|
||||
return {
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
width: listWidth,
|
||||
height: listHeight,
|
||||
border: `${lineWidth}px ${lineType} ${colorBorder}`,
|
||||
borderRadius: token.borderRadiusLG,
|
||||
|
||||
'&-with-pagination': {
|
||||
width: listWidthLG,
|
||||
height: 'auto',
|
||||
},
|
||||
|
||||
'&-search': {
|
||||
[`${iconCls}-search`]: {
|
||||
color: colorTextDisabled,
|
||||
},
|
||||
},
|
||||
|
||||
'&-header': {
|
||||
display: 'flex',
|
||||
flex: 'none',
|
||||
alignItems: 'center',
|
||||
height: transferHeaderHeight,
|
||||
// border-top is on the transfer dom. We should minus 1px for this
|
||||
padding: `${
|
||||
transferHeaderVerticalPadding - lineWidth
|
||||
}px ${paddingSM}px ${transferHeaderVerticalPadding}px`,
|
||||
color: token.colorText,
|
||||
background: token.colorBgContainer,
|
||||
borderBottom: `${lineWidth}px ${lineType} ${colorSplit}`,
|
||||
borderRadius: `${token.borderRadiusLG}px ${token.borderRadiusLG}px 0 0`,
|
||||
|
||||
'> *:not(:last-child)': {
|
||||
marginInlineEnd: 4, // This is magic and fixed number, DO NOT use token since it may change.
|
||||
},
|
||||
|
||||
'> *': {
|
||||
flex: 'none',
|
||||
},
|
||||
|
||||
'&-title': {
|
||||
...textEllipsis,
|
||||
flex: 'auto',
|
||||
textAlign: 'end',
|
||||
},
|
||||
|
||||
'&-dropdown': {
|
||||
...resetIcon(),
|
||||
|
||||
fontSize: fontSizeIcon,
|
||||
transform: 'translateY(10%)',
|
||||
cursor: 'pointer',
|
||||
|
||||
'&[disabled]': {
|
||||
cursor: 'not-allowed',
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
'&-body': {
|
||||
display: 'flex',
|
||||
flex: 'auto',
|
||||
flexDirection: 'column',
|
||||
overflow: 'hidden',
|
||||
fontSize: token.fontSize,
|
||||
|
||||
'&-search-wrapper': {
|
||||
position: 'relative',
|
||||
flex: 'none',
|
||||
padding: paddingSM,
|
||||
},
|
||||
},
|
||||
|
||||
'&-content': {
|
||||
flex: 'auto',
|
||||
margin: 0,
|
||||
padding: 0,
|
||||
overflow: 'auto',
|
||||
listStyle: 'none',
|
||||
|
||||
'&-item': {
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
minHeight: transferItemHeight,
|
||||
padding: `${transferItemPaddingVertical}px ${paddingSM}px`,
|
||||
transition: `all ${motionDurationSlow}`,
|
||||
|
||||
'> *:not(:last-child)': {
|
||||
marginInlineEnd: marginXS,
|
||||
},
|
||||
|
||||
'> *': {
|
||||
flex: 'none',
|
||||
},
|
||||
|
||||
'&-text': {
|
||||
...textEllipsis,
|
||||
flex: 'auto',
|
||||
},
|
||||
|
||||
'&-remove': {
|
||||
position: 'relative',
|
||||
color: colorBorder,
|
||||
|
||||
cursor: 'pointer',
|
||||
transition: `all ${motionDurationSlow}`,
|
||||
|
||||
'&:hover': {
|
||||
color: token.colorLinkHover,
|
||||
},
|
||||
|
||||
'&::after': {
|
||||
position: 'absolute',
|
||||
insert: `-${transferItemPaddingVertical}px -50%`,
|
||||
content: '""',
|
||||
},
|
||||
},
|
||||
|
||||
[`&:not(${componentCls}-list-content-item-disabled)`]: {
|
||||
'&:hover': {
|
||||
backgroundColor: token.controlItemBgHover,
|
||||
cursor: 'pointer',
|
||||
},
|
||||
|
||||
[`&${componentCls}-list-content-item-checked:hover`]: {
|
||||
backgroundColor: controlItemBgActiveHover,
|
||||
},
|
||||
},
|
||||
|
||||
'&-checked': {
|
||||
backgroundColor: controlItemBgActive,
|
||||
},
|
||||
|
||||
'&-disabled': {
|
||||
color: colorTextDisabled,
|
||||
cursor: 'not-allowed',
|
||||
},
|
||||
},
|
||||
|
||||
// Do not change hover style when `oneWay` mode
|
||||
[`&-show-remove ${componentCls}-list-content-item:not(${componentCls}-list-content-item-disabled):hover`]:
|
||||
{
|
||||
background: 'transparent',
|
||||
cursor: 'default',
|
||||
},
|
||||
},
|
||||
|
||||
'&-pagination': {
|
||||
padding: `${token.paddingXS}px 0`,
|
||||
textAlign: 'end',
|
||||
borderTop: `${lineWidth}px ${lineType} ${colorSplit}`,
|
||||
},
|
||||
|
||||
'&-body-not-found': {
|
||||
flex: 'none',
|
||||
width: '100%',
|
||||
margin: 'auto 0',
|
||||
color: colorTextDisabled,
|
||||
textAlign: 'center',
|
||||
},
|
||||
|
||||
'&-footer': {
|
||||
borderTop: `${lineWidth}px ${lineType} ${colorSplit}`,
|
||||
},
|
||||
};
|
||||
};
|
||||
|
||||
const genTransferStyle: GenerateStyle<TransferToken> = (token: TransferToken): CSSObject => {
|
||||
const {
|
||||
antCls,
|
||||
iconCls,
|
||||
componentCls,
|
||||
transferHeaderHeight,
|
||||
marginXS,
|
||||
marginXXS,
|
||||
fontSizeIcon,
|
||||
fontSize,
|
||||
lineHeight,
|
||||
} = token;
|
||||
|
||||
return {
|
||||
[componentCls]: {
|
||||
...resetComponent(token),
|
||||
|
||||
position: 'relative',
|
||||
display: 'flex',
|
||||
alignItems: 'stretch',
|
||||
|
||||
[`${componentCls}-disabled`]: {
|
||||
[`${componentCls}-list`]: {
|
||||
background: token.colorBgContainerDisabled,
|
||||
},
|
||||
},
|
||||
|
||||
[`${componentCls}-list`]: genTransferListStyle(token),
|
||||
|
||||
[`${componentCls}-operation`]: {
|
||||
display: 'flex',
|
||||
flex: 'none',
|
||||
flexDirection: 'column',
|
||||
alignSelf: 'center',
|
||||
margin: `0 ${marginXS}px`,
|
||||
verticalAlign: 'middle',
|
||||
|
||||
[`${antCls}-btn`]: {
|
||||
display: 'block',
|
||||
|
||||
'&:first-child': {
|
||||
marginBottom: marginXXS,
|
||||
},
|
||||
|
||||
[iconCls]: {
|
||||
fontSize: fontSizeIcon,
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
[`${antCls}-empty-image`]: {
|
||||
maxHeight: transferHeaderHeight / 2 - Math.round(fontSize * lineHeight),
|
||||
},
|
||||
},
|
||||
};
|
||||
};
|
||||
|
||||
const genTransferRTLStyle: GenerateStyle<TransferToken> = (token: TransferToken): CSSObject => {
|
||||
const { componentCls } = token;
|
||||
return {
|
||||
[`${componentCls}-rtl`]: {
|
||||
direction: 'rtl',
|
||||
},
|
||||
};
|
||||
};
|
||||
|
||||
// ============================== Export ==============================
|
||||
export default genComponentStyleHook(
|
||||
'Transfer',
|
||||
token => {
|
||||
const { fontSize, lineHeight, lineWidth, controlHeightLG, controlHeight } = token;
|
||||
|
||||
const fontHeight = Math.round(fontSize * lineHeight);
|
||||
const transferHeaderHeight = controlHeightLG;
|
||||
const transferItemHeight = controlHeight;
|
||||
|
||||
const transferToken = mergeToken<TransferToken>(token, {
|
||||
transferItemHeight,
|
||||
transferHeaderHeight,
|
||||
transferHeaderVerticalPadding: Math.ceil((transferHeaderHeight - lineWidth - fontHeight) / 2),
|
||||
transferItemPaddingVertical: (transferItemHeight - fontHeight) / 2,
|
||||
});
|
||||
|
||||
return [
|
||||
genTransferStyle(transferToken),
|
||||
genTransferCustomizeStyle(transferToken),
|
||||
genTransferStatusStyle(transferToken),
|
||||
genTransferRTLStyle(transferToken),
|
||||
];
|
||||
},
|
||||
{
|
||||
listWidth: 180,
|
||||
listHeight: 200,
|
||||
listWidthLG: 250,
|
||||
},
|
||||
);
|
||||
|
|
|
@ -1,71 +0,0 @@
|
|||
@import '../../style/themes/index';
|
||||
@import '../../style/mixins/index';
|
||||
@import '../../checkbox/style/mixin';
|
||||
|
||||
@transfer-prefix-cls: ~'@{ant-prefix}-transfer';
|
||||
|
||||
.@{transfer-prefix-cls} {
|
||||
&-rtl {
|
||||
direction: rtl;
|
||||
}
|
||||
|
||||
&-list {
|
||||
&-search {
|
||||
.@{transfer-prefix-cls}-rtl & {
|
||||
padding-right: @control-padding-horizontal-sm;
|
||||
padding-left: 24px;
|
||||
}
|
||||
|
||||
&-action {
|
||||
.@{transfer-prefix-cls}-rtl & {
|
||||
right: auto;
|
||||
left: 12px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&-header {
|
||||
> *:not(:last-child) {
|
||||
.@{transfer-prefix-cls}-rtl & {
|
||||
margin-right: 0;
|
||||
margin-left: 4px;
|
||||
}
|
||||
}
|
||||
|
||||
.@{transfer-prefix-cls}-rtl & {
|
||||
right: 0;
|
||||
left: auto;
|
||||
}
|
||||
|
||||
&-title {
|
||||
.@{transfer-prefix-cls}-rtl & {
|
||||
text-align: left;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&-content {
|
||||
&-item {
|
||||
> *:not(:last-child) {
|
||||
.@{transfer-prefix-cls}-rtl & {
|
||||
margin-right: 0;
|
||||
margin-left: 8px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&-pagination {
|
||||
.@{transfer-prefix-cls}-rtl & {
|
||||
text-align: left;
|
||||
}
|
||||
}
|
||||
|
||||
&-footer {
|
||||
.@{transfer-prefix-cls}-rtl & {
|
||||
right: 0;
|
||||
left: auto;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,31 +0,0 @@
|
|||
@import '../../input/style/mixin';
|
||||
|
||||
@transfer-prefix-cls: ~'@{ant-prefix}-transfer';
|
||||
|
||||
.transfer-status-color(@color) {
|
||||
.@{transfer-prefix-cls}-list {
|
||||
border-color: @color;
|
||||
|
||||
&-search:not([disabled]) {
|
||||
border-color: @input-border-color;
|
||||
|
||||
&:hover {
|
||||
.hover();
|
||||
}
|
||||
|
||||
&:focus {
|
||||
.active();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.@{transfer-prefix-cls} {
|
||||
&-status-error {
|
||||
.transfer-status-color(@error-color);
|
||||
}
|
||||
|
||||
&-status-warning {
|
||||
.transfer-status-color(@warning-color);
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue