mirror of
https://github.com/ant-design/ant-design.git
synced 2025-01-18 14:13:37 +08:00
feat: Transfer support oneWay
(#24041)
* use flex * show clear btn * support one way * add dropdown * add pagination * support pagination * use flex * operation stretch * pagination works * update selection logic * no need to show checkbox on pagination * fix tree demo * support invert selection * remove current pages * update style * update snapshot * clean up * update test case * update test case * update snapshot * fix lint * add deps * update doc * update hover style * update hover checked style * adjust demo & active region * fix lint * update snapshot
This commit is contained in:
parent
41dd9560c9
commit
b0e528d14c
@ -9,6 +9,7 @@ interface TransButtonProps extends React.HTMLAttributes<HTMLDivElement> {
|
||||
onClick?: (e?: React.MouseEvent<HTMLDivElement>) => void;
|
||||
noStyle?: boolean;
|
||||
autoFocus?: boolean;
|
||||
disabled?: boolean;
|
||||
}
|
||||
|
||||
const inlineStyle: React.CSSProperties = {
|
||||
@ -63,7 +64,24 @@ class TransButton extends React.Component<TransButtonProps> {
|
||||
}
|
||||
|
||||
render() {
|
||||
const { style, noStyle, ...restProps } = this.props;
|
||||
const { style, noStyle, disabled, ...restProps } = this.props;
|
||||
|
||||
let mergedStyle: React.CSSProperties = {};
|
||||
|
||||
if (!noStyle) {
|
||||
mergedStyle = {
|
||||
...inlineStyle,
|
||||
};
|
||||
}
|
||||
|
||||
if (disabled) {
|
||||
mergedStyle.pointerEvents = 'none';
|
||||
}
|
||||
|
||||
mergedStyle = {
|
||||
...mergedStyle,
|
||||
...style,
|
||||
};
|
||||
|
||||
return (
|
||||
<div
|
||||
@ -73,7 +91,7 @@ class TransButton extends React.Component<TransButtonProps> {
|
||||
{...restProps}
|
||||
onKeyDown={this.onKeyDown}
|
||||
onKeyUp={this.onKeyUp}
|
||||
style={{ ...(!noStyle ? inlineStyle : null), ...style }}
|
||||
style={mergedStyle}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
@ -34579,16 +34579,34 @@ exports[`ConfigProvider components Transfer configProvider 1`] = `
|
||||
/>
|
||||
</span>
|
||||
</label>
|
||||
<span
|
||||
aria-label="down"
|
||||
class="anticon anticon-down config-dropdown-trigger config-transfer-list-header-dropdown"
|
||||
role="img"
|
||||
>
|
||||
<svg
|
||||
aria-hidden="true"
|
||||
class=""
|
||||
data-icon="down"
|
||||
fill="currentColor"
|
||||
focusable="false"
|
||||
height="1em"
|
||||
viewBox="64 64 896 896"
|
||||
width="1em"
|
||||
>
|
||||
<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"
|
||||
/>
|
||||
</svg>
|
||||
</span>
|
||||
<span
|
||||
class="config-transfer-list-header-selected"
|
||||
>
|
||||
<span>
|
||||
0 item
|
||||
</span>
|
||||
<span
|
||||
class="config-transfer-list-header-title"
|
||||
/>
|
||||
0 item
|
||||
</span>
|
||||
<span
|
||||
class="config-transfer-list-header-title"
|
||||
/>
|
||||
</div>
|
||||
<div
|
||||
class="config-transfer-list-body"
|
||||
@ -34722,16 +34740,34 @@ exports[`ConfigProvider components Transfer configProvider 1`] = `
|
||||
/>
|
||||
</span>
|
||||
</label>
|
||||
<span
|
||||
aria-label="down"
|
||||
class="anticon anticon-down config-dropdown-trigger config-transfer-list-header-dropdown"
|
||||
role="img"
|
||||
>
|
||||
<svg
|
||||
aria-hidden="true"
|
||||
class=""
|
||||
data-icon="down"
|
||||
fill="currentColor"
|
||||
focusable="false"
|
||||
height="1em"
|
||||
viewBox="64 64 896 896"
|
||||
width="1em"
|
||||
>
|
||||
<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"
|
||||
/>
|
||||
</svg>
|
||||
</span>
|
||||
<span
|
||||
class="config-transfer-list-header-selected"
|
||||
>
|
||||
<span>
|
||||
0 item
|
||||
</span>
|
||||
<span
|
||||
class="config-transfer-list-header-title"
|
||||
/>
|
||||
0 item
|
||||
</span>
|
||||
<span
|
||||
class="config-transfer-list-header-title"
|
||||
/>
|
||||
</div>
|
||||
<div
|
||||
class="config-transfer-list-body"
|
||||
@ -34816,16 +34852,34 @@ exports[`ConfigProvider components Transfer configProvider componentSize large 1
|
||||
/>
|
||||
</span>
|
||||
</label>
|
||||
<span
|
||||
aria-label="down"
|
||||
class="anticon anticon-down config-dropdown-trigger config-transfer-list-header-dropdown"
|
||||
role="img"
|
||||
>
|
||||
<svg
|
||||
aria-hidden="true"
|
||||
class=""
|
||||
data-icon="down"
|
||||
fill="currentColor"
|
||||
focusable="false"
|
||||
height="1em"
|
||||
viewBox="64 64 896 896"
|
||||
width="1em"
|
||||
>
|
||||
<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"
|
||||
/>
|
||||
</svg>
|
||||
</span>
|
||||
<span
|
||||
class="config-transfer-list-header-selected"
|
||||
>
|
||||
<span>
|
||||
0 item
|
||||
</span>
|
||||
<span
|
||||
class="config-transfer-list-header-title"
|
||||
/>
|
||||
0 item
|
||||
</span>
|
||||
<span
|
||||
class="config-transfer-list-header-title"
|
||||
/>
|
||||
</div>
|
||||
<div
|
||||
class="config-transfer-list-body"
|
||||
@ -34959,16 +35013,34 @@ exports[`ConfigProvider components Transfer configProvider componentSize large 1
|
||||
/>
|
||||
</span>
|
||||
</label>
|
||||
<span
|
||||
aria-label="down"
|
||||
class="anticon anticon-down config-dropdown-trigger config-transfer-list-header-dropdown"
|
||||
role="img"
|
||||
>
|
||||
<svg
|
||||
aria-hidden="true"
|
||||
class=""
|
||||
data-icon="down"
|
||||
fill="currentColor"
|
||||
focusable="false"
|
||||
height="1em"
|
||||
viewBox="64 64 896 896"
|
||||
width="1em"
|
||||
>
|
||||
<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"
|
||||
/>
|
||||
</svg>
|
||||
</span>
|
||||
<span
|
||||
class="config-transfer-list-header-selected"
|
||||
>
|
||||
<span>
|
||||
0 item
|
||||
</span>
|
||||
<span
|
||||
class="config-transfer-list-header-title"
|
||||
/>
|
||||
0 item
|
||||
</span>
|
||||
<span
|
||||
class="config-transfer-list-header-title"
|
||||
/>
|
||||
</div>
|
||||
<div
|
||||
class="config-transfer-list-body"
|
||||
@ -35053,16 +35125,34 @@ exports[`ConfigProvider components Transfer configProvider componentSize middle
|
||||
/>
|
||||
</span>
|
||||
</label>
|
||||
<span
|
||||
aria-label="down"
|
||||
class="anticon anticon-down config-dropdown-trigger config-transfer-list-header-dropdown"
|
||||
role="img"
|
||||
>
|
||||
<svg
|
||||
aria-hidden="true"
|
||||
class=""
|
||||
data-icon="down"
|
||||
fill="currentColor"
|
||||
focusable="false"
|
||||
height="1em"
|
||||
viewBox="64 64 896 896"
|
||||
width="1em"
|
||||
>
|
||||
<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"
|
||||
/>
|
||||
</svg>
|
||||
</span>
|
||||
<span
|
||||
class="config-transfer-list-header-selected"
|
||||
>
|
||||
<span>
|
||||
0 item
|
||||
</span>
|
||||
<span
|
||||
class="config-transfer-list-header-title"
|
||||
/>
|
||||
0 item
|
||||
</span>
|
||||
<span
|
||||
class="config-transfer-list-header-title"
|
||||
/>
|
||||
</div>
|
||||
<div
|
||||
class="config-transfer-list-body"
|
||||
@ -35196,16 +35286,34 @@ exports[`ConfigProvider components Transfer configProvider componentSize middle
|
||||
/>
|
||||
</span>
|
||||
</label>
|
||||
<span
|
||||
aria-label="down"
|
||||
class="anticon anticon-down config-dropdown-trigger config-transfer-list-header-dropdown"
|
||||
role="img"
|
||||
>
|
||||
<svg
|
||||
aria-hidden="true"
|
||||
class=""
|
||||
data-icon="down"
|
||||
fill="currentColor"
|
||||
focusable="false"
|
||||
height="1em"
|
||||
viewBox="64 64 896 896"
|
||||
width="1em"
|
||||
>
|
||||
<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"
|
||||
/>
|
||||
</svg>
|
||||
</span>
|
||||
<span
|
||||
class="config-transfer-list-header-selected"
|
||||
>
|
||||
<span>
|
||||
0 item
|
||||
</span>
|
||||
<span
|
||||
class="config-transfer-list-header-title"
|
||||
/>
|
||||
0 item
|
||||
</span>
|
||||
<span
|
||||
class="config-transfer-list-header-title"
|
||||
/>
|
||||
</div>
|
||||
<div
|
||||
class="config-transfer-list-body"
|
||||
@ -35290,16 +35398,34 @@ exports[`ConfigProvider components Transfer configProvider virtual and dropdownM
|
||||
/>
|
||||
</span>
|
||||
</label>
|
||||
<span
|
||||
aria-label="down"
|
||||
class="anticon anticon-down ant-dropdown-trigger ant-transfer-list-header-dropdown"
|
||||
role="img"
|
||||
>
|
||||
<svg
|
||||
aria-hidden="true"
|
||||
class=""
|
||||
data-icon="down"
|
||||
fill="currentColor"
|
||||
focusable="false"
|
||||
height="1em"
|
||||
viewBox="64 64 896 896"
|
||||
width="1em"
|
||||
>
|
||||
<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"
|
||||
/>
|
||||
</svg>
|
||||
</span>
|
||||
<span
|
||||
class="ant-transfer-list-header-selected"
|
||||
>
|
||||
<span>
|
||||
0 item
|
||||
</span>
|
||||
<span
|
||||
class="ant-transfer-list-header-title"
|
||||
/>
|
||||
0 item
|
||||
</span>
|
||||
<span
|
||||
class="ant-transfer-list-header-title"
|
||||
/>
|
||||
</div>
|
||||
<div
|
||||
class="ant-transfer-list-body"
|
||||
@ -35433,16 +35559,34 @@ exports[`ConfigProvider components Transfer configProvider virtual and dropdownM
|
||||
/>
|
||||
</span>
|
||||
</label>
|
||||
<span
|
||||
aria-label="down"
|
||||
class="anticon anticon-down ant-dropdown-trigger ant-transfer-list-header-dropdown"
|
||||
role="img"
|
||||
>
|
||||
<svg
|
||||
aria-hidden="true"
|
||||
class=""
|
||||
data-icon="down"
|
||||
fill="currentColor"
|
||||
focusable="false"
|
||||
height="1em"
|
||||
viewBox="64 64 896 896"
|
||||
width="1em"
|
||||
>
|
||||
<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"
|
||||
/>
|
||||
</svg>
|
||||
</span>
|
||||
<span
|
||||
class="ant-transfer-list-header-selected"
|
||||
>
|
||||
<span>
|
||||
0 item
|
||||
</span>
|
||||
<span
|
||||
class="ant-transfer-list-header-title"
|
||||
/>
|
||||
0 item
|
||||
</span>
|
||||
<span
|
||||
class="ant-transfer-list-header-title"
|
||||
/>
|
||||
</div>
|
||||
<div
|
||||
class="ant-transfer-list-body"
|
||||
@ -35527,16 +35671,34 @@ exports[`ConfigProvider components Transfer normal 1`] = `
|
||||
/>
|
||||
</span>
|
||||
</label>
|
||||
<span
|
||||
aria-label="down"
|
||||
class="anticon anticon-down ant-dropdown-trigger ant-transfer-list-header-dropdown"
|
||||
role="img"
|
||||
>
|
||||
<svg
|
||||
aria-hidden="true"
|
||||
class=""
|
||||
data-icon="down"
|
||||
fill="currentColor"
|
||||
focusable="false"
|
||||
height="1em"
|
||||
viewBox="64 64 896 896"
|
||||
width="1em"
|
||||
>
|
||||
<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"
|
||||
/>
|
||||
</svg>
|
||||
</span>
|
||||
<span
|
||||
class="ant-transfer-list-header-selected"
|
||||
>
|
||||
<span>
|
||||
0 item
|
||||
</span>
|
||||
<span
|
||||
class="ant-transfer-list-header-title"
|
||||
/>
|
||||
0 item
|
||||
</span>
|
||||
<span
|
||||
class="ant-transfer-list-header-title"
|
||||
/>
|
||||
</div>
|
||||
<div
|
||||
class="ant-transfer-list-body"
|
||||
@ -35670,16 +35832,34 @@ exports[`ConfigProvider components Transfer normal 1`] = `
|
||||
/>
|
||||
</span>
|
||||
</label>
|
||||
<span
|
||||
aria-label="down"
|
||||
class="anticon anticon-down ant-dropdown-trigger ant-transfer-list-header-dropdown"
|
||||
role="img"
|
||||
>
|
||||
<svg
|
||||
aria-hidden="true"
|
||||
class=""
|
||||
data-icon="down"
|
||||
fill="currentColor"
|
||||
focusable="false"
|
||||
height="1em"
|
||||
viewBox="64 64 896 896"
|
||||
width="1em"
|
||||
>
|
||||
<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"
|
||||
/>
|
||||
</svg>
|
||||
</span>
|
||||
<span
|
||||
class="ant-transfer-list-header-selected"
|
||||
>
|
||||
<span>
|
||||
0 item
|
||||
</span>
|
||||
<span
|
||||
class="ant-transfer-list-header-title"
|
||||
/>
|
||||
0 item
|
||||
</span>
|
||||
<span
|
||||
class="ant-transfer-list-header-title"
|
||||
/>
|
||||
</div>
|
||||
<div
|
||||
class="ant-transfer-list-body"
|
||||
@ -35764,16 +35944,34 @@ exports[`ConfigProvider components Transfer prefixCls 1`] = `
|
||||
/>
|
||||
</span>
|
||||
</label>
|
||||
<span
|
||||
aria-label="down"
|
||||
class="anticon anticon-down ant-dropdown-trigger prefix-Transfer-list-header-dropdown"
|
||||
role="img"
|
||||
>
|
||||
<svg
|
||||
aria-hidden="true"
|
||||
class=""
|
||||
data-icon="down"
|
||||
fill="currentColor"
|
||||
focusable="false"
|
||||
height="1em"
|
||||
viewBox="64 64 896 896"
|
||||
width="1em"
|
||||
>
|
||||
<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"
|
||||
/>
|
||||
</svg>
|
||||
</span>
|
||||
<span
|
||||
class="prefix-Transfer-list-header-selected"
|
||||
>
|
||||
<span>
|
||||
0 item
|
||||
</span>
|
||||
<span
|
||||
class="prefix-Transfer-list-header-title"
|
||||
/>
|
||||
0 item
|
||||
</span>
|
||||
<span
|
||||
class="prefix-Transfer-list-header-title"
|
||||
/>
|
||||
</div>
|
||||
<div
|
||||
class="prefix-Transfer-list-body"
|
||||
@ -35907,16 +36105,34 @@ exports[`ConfigProvider components Transfer prefixCls 1`] = `
|
||||
/>
|
||||
</span>
|
||||
</label>
|
||||
<span
|
||||
aria-label="down"
|
||||
class="anticon anticon-down ant-dropdown-trigger prefix-Transfer-list-header-dropdown"
|
||||
role="img"
|
||||
>
|
||||
<svg
|
||||
aria-hidden="true"
|
||||
class=""
|
||||
data-icon="down"
|
||||
fill="currentColor"
|
||||
focusable="false"
|
||||
height="1em"
|
||||
viewBox="64 64 896 896"
|
||||
width="1em"
|
||||
>
|
||||
<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"
|
||||
/>
|
||||
</svg>
|
||||
</span>
|
||||
<span
|
||||
class="prefix-Transfer-list-header-selected"
|
||||
>
|
||||
<span>
|
||||
0 item
|
||||
</span>
|
||||
<span
|
||||
class="prefix-Transfer-list-header-title"
|
||||
/>
|
||||
0 item
|
||||
</span>
|
||||
<span
|
||||
class="prefix-Transfer-list-header-title"
|
||||
/>
|
||||
</div>
|
||||
<div
|
||||
class="prefix-Transfer-list-body"
|
||||
|
@ -283,16 +283,34 @@ exports[`renders ./components/empty/demo/config-provider.md correctly 1`] = `
|
||||
/>
|
||||
</span>
|
||||
</label>
|
||||
<span
|
||||
aria-label="down"
|
||||
class="anticon anticon-down ant-dropdown-trigger ant-transfer-list-header-dropdown"
|
||||
role="img"
|
||||
>
|
||||
<svg
|
||||
aria-hidden="true"
|
||||
class=""
|
||||
data-icon="down"
|
||||
fill="currentColor"
|
||||
focusable="false"
|
||||
height="1em"
|
||||
viewBox="64 64 896 896"
|
||||
width="1em"
|
||||
>
|
||||
<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"
|
||||
/>
|
||||
</svg>
|
||||
</span>
|
||||
<span
|
||||
class="ant-transfer-list-header-selected"
|
||||
>
|
||||
<span>
|
||||
0 item
|
||||
</span>
|
||||
<span
|
||||
class="ant-transfer-list-header-title"
|
||||
/>
|
||||
0 item
|
||||
</span>
|
||||
<span
|
||||
class="ant-transfer-list-header-title"
|
||||
/>
|
||||
</div>
|
||||
<div
|
||||
class="ant-transfer-list-body"
|
||||
@ -426,16 +444,34 @@ exports[`renders ./components/empty/demo/config-provider.md correctly 1`] = `
|
||||
/>
|
||||
</span>
|
||||
</label>
|
||||
<span
|
||||
aria-label="down"
|
||||
class="anticon anticon-down ant-dropdown-trigger ant-transfer-list-header-dropdown"
|
||||
role="img"
|
||||
>
|
||||
<svg
|
||||
aria-hidden="true"
|
||||
class=""
|
||||
data-icon="down"
|
||||
fill="currentColor"
|
||||
focusable="false"
|
||||
height="1em"
|
||||
viewBox="64 64 896 896"
|
||||
width="1em"
|
||||
>
|
||||
<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"
|
||||
/>
|
||||
</svg>
|
||||
</span>
|
||||
<span
|
||||
class="ant-transfer-list-header-selected"
|
||||
>
|
||||
<span>
|
||||
0 item
|
||||
</span>
|
||||
<span
|
||||
class="ant-transfer-list-header-title"
|
||||
/>
|
||||
0 item
|
||||
</span>
|
||||
<span
|
||||
class="ant-transfer-list-header-title"
|
||||
/>
|
||||
</div>
|
||||
<div
|
||||
class="ant-transfer-list-body"
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -44,6 +44,12 @@ const localeValues: Locale = {
|
||||
searchPlaceholder: 'Search here',
|
||||
itemUnit: 'item',
|
||||
itemsUnit: 'items',
|
||||
remove: 'Remove',
|
||||
selectCurrent: 'Select current page',
|
||||
removeCurrent: 'Remove current page',
|
||||
selectAll: 'Select all data',
|
||||
removeAll: 'Remove all data',
|
||||
selectInvert: 'Invert current page',
|
||||
},
|
||||
Upload: {
|
||||
uploading: 'Uploading...',
|
||||
|
@ -44,6 +44,12 @@ const localeValues: Locale = {
|
||||
searchPlaceholder: '请输入搜索内容',
|
||||
itemUnit: '项',
|
||||
itemsUnit: '项',
|
||||
remove: '删除',
|
||||
selectCurrent: '全选当页',
|
||||
removeCurrent: '删除当页',
|
||||
selectAll: '全选所有',
|
||||
removeAll: '删除全部',
|
||||
selectInvert: '反选当页',
|
||||
},
|
||||
Upload: {
|
||||
uploading: '文件上传中',
|
||||
|
161
components/transfer/ListBody.tsx
Normal file
161
components/transfer/ListBody.tsx
Normal file
@ -0,0 +1,161 @@
|
||||
import * as React from 'react';
|
||||
import classNames from 'classnames';
|
||||
import { ElementOf, Omit, tuple } from '../_util/type';
|
||||
import Pagination from '../pagination';
|
||||
import { TransferItem } from '.';
|
||||
import { TransferListProps, RenderedItem } from './list';
|
||||
import ListItem from './ListItem';
|
||||
import { PaginationType } from './interface';
|
||||
|
||||
export const OmitProps = tuple('handleFilter', 'handleClear', 'checkedKeys');
|
||||
export type OmitProp = ElementOf<typeof OmitProps>;
|
||||
type PartialTransferListProps = Omit<TransferListProps, OmitProp>;
|
||||
|
||||
export interface TransferListBodyProps extends PartialTransferListProps {
|
||||
filteredItems: TransferItem[];
|
||||
filteredRenderItems: RenderedItem[];
|
||||
selectedKeys: string[];
|
||||
}
|
||||
|
||||
function parsePagination(pagination?: PaginationType) {
|
||||
if (!pagination) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const defaultPagination = {
|
||||
pageSize: 10,
|
||||
};
|
||||
|
||||
if (typeof pagination === 'object') {
|
||||
return {
|
||||
...defaultPagination,
|
||||
...pagination,
|
||||
};
|
||||
}
|
||||
|
||||
return defaultPagination;
|
||||
}
|
||||
|
||||
interface TransferListBodyState {
|
||||
current: number;
|
||||
}
|
||||
|
||||
class ListBody extends React.Component<TransferListBodyProps, TransferListBodyState> {
|
||||
state = {
|
||||
current: 1,
|
||||
};
|
||||
|
||||
static getDerivedStateFromProps(
|
||||
{ filteredRenderItems, pagination }: TransferListBodyProps,
|
||||
{ current }: TransferListBodyState,
|
||||
) {
|
||||
const mergedPagination = parsePagination(pagination);
|
||||
if (mergedPagination) {
|
||||
// Calculate the page number
|
||||
const maxPageCount = Math.ceil(filteredRenderItems.length / mergedPagination.pageSize);
|
||||
|
||||
if (current > maxPageCount) {
|
||||
return { current: maxPageCount };
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
onItemSelect = (item: TransferItem) => {
|
||||
const { onItemSelect, selectedKeys } = this.props;
|
||||
const checked = selectedKeys.indexOf(item.key) >= 0;
|
||||
onItemSelect(item.key, !checked);
|
||||
};
|
||||
|
||||
onItemRemove = (item: TransferItem) => {
|
||||
const { onItemRemove } = this.props;
|
||||
onItemRemove?.([item.key]);
|
||||
};
|
||||
|
||||
onPageChange = (current: number) => {
|
||||
this.setState({ current });
|
||||
};
|
||||
|
||||
getItems = () => {
|
||||
const { current } = this.state;
|
||||
const { pagination, filteredRenderItems } = this.props;
|
||||
|
||||
const mergedPagination = parsePagination(pagination);
|
||||
|
||||
let displayItems = filteredRenderItems;
|
||||
|
||||
if (mergedPagination) {
|
||||
displayItems = filteredRenderItems.slice(
|
||||
(current - 1) * mergedPagination.pageSize,
|
||||
current * mergedPagination.pageSize,
|
||||
);
|
||||
}
|
||||
|
||||
return displayItems;
|
||||
};
|
||||
|
||||
render() {
|
||||
const { current } = this.state;
|
||||
const {
|
||||
prefixCls,
|
||||
onScroll,
|
||||
filteredRenderItems,
|
||||
selectedKeys,
|
||||
disabled: globalDisabled,
|
||||
showRemove,
|
||||
pagination,
|
||||
} = this.props;
|
||||
|
||||
const mergedPagination = parsePagination(pagination);
|
||||
let paginationNode: React.ReactNode = null;
|
||||
|
||||
if (mergedPagination) {
|
||||
paginationNode = (
|
||||
<Pagination
|
||||
simple
|
||||
className={`${prefixCls}-pagination`}
|
||||
total={filteredRenderItems.length}
|
||||
pageSize={mergedPagination.pageSize}
|
||||
current={current}
|
||||
onChange={this.onPageChange}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
<ul
|
||||
className={classNames(`${prefixCls}-content`, {
|
||||
[`${prefixCls}-content-show-remove`]: showRemove,
|
||||
})}
|
||||
onScroll={onScroll}
|
||||
>
|
||||
{this.getItems().map(({ renderedEl, renderedText, item }: RenderedItem) => {
|
||||
const { disabled } = item;
|
||||
const checked = selectedKeys.indexOf(item.key) >= 0;
|
||||
|
||||
return (
|
||||
<ListItem
|
||||
disabled={globalDisabled || disabled}
|
||||
key={item.key}
|
||||
item={item}
|
||||
renderedText={renderedText}
|
||||
renderedEl={renderedEl}
|
||||
checked={checked}
|
||||
prefixCls={prefixCls}
|
||||
onClick={this.onItemSelect}
|
||||
onRemove={this.onItemRemove}
|
||||
showRemove={showRemove}
|
||||
/>
|
||||
);
|
||||
})}
|
||||
</ul>
|
||||
|
||||
{paginationNode}
|
||||
</>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default ListBody;
|
@ -1,7 +1,11 @@
|
||||
import * as React from 'react';
|
||||
import classNames from 'classnames';
|
||||
import { TransferItem } from '.';
|
||||
import { DeleteOutlined } from '@ant-design/icons';
|
||||
import { TransferItem, TransferLocale } from '.';
|
||||
import defaultLocale from '../locale/default';
|
||||
import Checkbox from '../checkbox';
|
||||
import TransButton from '../_util/transButton';
|
||||
import LocaleReceiver from '../locale-provider/LocaleReceiver';
|
||||
|
||||
type ListItemProps = {
|
||||
renderedText?: string | number;
|
||||
@ -10,11 +14,23 @@ type ListItemProps = {
|
||||
checked?: boolean;
|
||||
prefixCls: string;
|
||||
onClick: (item: TransferItem) => void;
|
||||
onRemove?: (item: TransferItem) => void;
|
||||
item: TransferItem;
|
||||
showRemove?: boolean;
|
||||
};
|
||||
|
||||
const ListItem = (props: ListItemProps) => {
|
||||
const { renderedText, renderedEl, item, checked, disabled, prefixCls, onClick } = props;
|
||||
const {
|
||||
renderedText,
|
||||
renderedEl,
|
||||
item,
|
||||
checked,
|
||||
disabled,
|
||||
prefixCls,
|
||||
onClick,
|
||||
onRemove,
|
||||
showRemove,
|
||||
} = props;
|
||||
|
||||
const className = classNames({
|
||||
[`${prefixCls}-content-item`]: true,
|
||||
@ -27,18 +43,42 @@ const ListItem = (props: ListItemProps) => {
|
||||
title = String(renderedText);
|
||||
}
|
||||
|
||||
const listItem = (
|
||||
<li
|
||||
className={className}
|
||||
title={title}
|
||||
onClick={disabled || item.disabled ? undefined : () => onClick(item)}
|
||||
>
|
||||
<Checkbox checked={checked} disabled={disabled || item.disabled} />
|
||||
<span className={`${prefixCls}-content-item-text`}>{renderedEl}</span>
|
||||
</li>
|
||||
);
|
||||
return (
|
||||
<LocaleReceiver componentName="Transfer" defaultLocale={defaultLocale.Transfer}>
|
||||
{(transferLocale: TransferLocale) => {
|
||||
const liProps: React.HTMLAttributes<HTMLLIElement> = { className, title };
|
||||
const labelNode = <span className={`${prefixCls}-content-item-text`}>{renderedEl}</span>;
|
||||
|
||||
return listItem;
|
||||
// Show remove
|
||||
if (showRemove) {
|
||||
return (
|
||||
<li {...liProps}>
|
||||
{labelNode}
|
||||
<TransButton
|
||||
disabled={disabled || item.disabled}
|
||||
className={`${prefixCls}-content-item-remove`}
|
||||
aria-label={transferLocale.remove}
|
||||
onClick={() => {
|
||||
onRemove?.(item);
|
||||
}}
|
||||
>
|
||||
<DeleteOutlined />
|
||||
</TransButton>
|
||||
</li>
|
||||
);
|
||||
}
|
||||
|
||||
// Default click to select
|
||||
liProps.onClick = disabled || item.disabled ? undefined : () => onClick(item);
|
||||
return (
|
||||
<li {...liProps}>
|
||||
<Checkbox checked={checked} disabled={disabled || item.disabled} />
|
||||
{labelNode}
|
||||
</li>
|
||||
);
|
||||
}}
|
||||
</LocaleReceiver>
|
||||
);
|
||||
};
|
||||
|
||||
export default React.memo(ListItem);
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -25,16 +25,34 @@ exports[`Transfer rtl render component should be rendered correctly in RTL direc
|
||||
/>
|
||||
</span>
|
||||
</label>
|
||||
<span
|
||||
aria-label="down"
|
||||
class="anticon anticon-down ant-dropdown-trigger ant-dropdown-rtl ant-transfer-list-header-dropdown"
|
||||
role="img"
|
||||
>
|
||||
<svg
|
||||
aria-hidden="true"
|
||||
class=""
|
||||
data-icon="down"
|
||||
fill="currentColor"
|
||||
focusable="false"
|
||||
height="1em"
|
||||
viewBox="64 64 896 896"
|
||||
width="1em"
|
||||
>
|
||||
<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"
|
||||
/>
|
||||
</svg>
|
||||
</span>
|
||||
<span
|
||||
class="ant-transfer-list-header-selected"
|
||||
>
|
||||
<span>
|
||||
0 item
|
||||
</span>
|
||||
<span
|
||||
class="ant-transfer-list-header-title"
|
||||
/>
|
||||
0 item
|
||||
</span>
|
||||
<span
|
||||
class="ant-transfer-list-header-title"
|
||||
/>
|
||||
</div>
|
||||
<div
|
||||
class="ant-transfer-list-body"
|
||||
@ -168,16 +186,34 @@ exports[`Transfer rtl render component should be rendered correctly in RTL direc
|
||||
/>
|
||||
</span>
|
||||
</label>
|
||||
<span
|
||||
aria-label="down"
|
||||
class="anticon anticon-down ant-dropdown-trigger ant-dropdown-rtl ant-transfer-list-header-dropdown"
|
||||
role="img"
|
||||
>
|
||||
<svg
|
||||
aria-hidden="true"
|
||||
class=""
|
||||
data-icon="down"
|
||||
fill="currentColor"
|
||||
focusable="false"
|
||||
height="1em"
|
||||
viewBox="64 64 896 896"
|
||||
width="1em"
|
||||
>
|
||||
<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"
|
||||
/>
|
||||
</svg>
|
||||
</span>
|
||||
<span
|
||||
class="ant-transfer-list-header-selected"
|
||||
>
|
||||
<span>
|
||||
0 item
|
||||
</span>
|
||||
<span
|
||||
class="ant-transfer-list-header-title"
|
||||
/>
|
||||
0 item
|
||||
</span>
|
||||
<span
|
||||
class="ant-transfer-list-header-title"
|
||||
/>
|
||||
</div>
|
||||
<div
|
||||
class="ant-transfer-list-body"
|
||||
@ -263,16 +299,34 @@ exports[`Transfer should render correctly 1`] = `
|
||||
/>
|
||||
</span>
|
||||
</label>
|
||||
<span
|
||||
aria-label="down"
|
||||
class="anticon anticon-down ant-dropdown-trigger ant-transfer-list-header-dropdown"
|
||||
role="img"
|
||||
>
|
||||
<svg
|
||||
aria-hidden="true"
|
||||
class=""
|
||||
data-icon="down"
|
||||
fill="currentColor"
|
||||
focusable="false"
|
||||
height="1em"
|
||||
viewBox="64 64 896 896"
|
||||
width="1em"
|
||||
>
|
||||
<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"
|
||||
/>
|
||||
</svg>
|
||||
</span>
|
||||
<span
|
||||
class="ant-transfer-list-header-selected"
|
||||
>
|
||||
<span>
|
||||
1/2 items
|
||||
</span>
|
||||
<span
|
||||
class="ant-transfer-list-header-title"
|
||||
/>
|
||||
1/2 items
|
||||
</span>
|
||||
<span
|
||||
class="ant-transfer-list-header-title"
|
||||
/>
|
||||
</div>
|
||||
<div
|
||||
class="ant-transfer-list-body"
|
||||
@ -405,16 +459,34 @@ exports[`Transfer should render correctly 1`] = `
|
||||
/>
|
||||
</span>
|
||||
</label>
|
||||
<span
|
||||
aria-label="down"
|
||||
class="anticon anticon-down ant-dropdown-trigger ant-transfer-list-header-dropdown"
|
||||
role="img"
|
||||
>
|
||||
<svg
|
||||
aria-hidden="true"
|
||||
class=""
|
||||
data-icon="down"
|
||||
fill="currentColor"
|
||||
focusable="false"
|
||||
height="1em"
|
||||
viewBox="64 64 896 896"
|
||||
width="1em"
|
||||
>
|
||||
<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"
|
||||
/>
|
||||
</svg>
|
||||
</span>
|
||||
<span
|
||||
class="ant-transfer-list-header-selected"
|
||||
>
|
||||
<span>
|
||||
1 item
|
||||
</span>
|
||||
<span
|
||||
class="ant-transfer-list-header-title"
|
||||
/>
|
||||
1 item
|
||||
</span>
|
||||
<span
|
||||
class="ant-transfer-list-header-title"
|
||||
/>
|
||||
</div>
|
||||
<div
|
||||
class="ant-transfer-list-body"
|
||||
@ -450,7 +522,7 @@ exports[`Transfer should render correctly 1`] = `
|
||||
</div>
|
||||
`;
|
||||
|
||||
exports[`Transfer should show sorted targetkey 1`] = `
|
||||
exports[`Transfer should show sorted targetKey 1`] = `
|
||||
<div
|
||||
class="ant-transfer"
|
||||
>
|
||||
@ -475,16 +547,34 @@ exports[`Transfer should show sorted targetkey 1`] = `
|
||||
/>
|
||||
</span>
|
||||
</label>
|
||||
<span
|
||||
aria-label="down"
|
||||
class="anticon anticon-down ant-dropdown-trigger ant-transfer-list-header-dropdown"
|
||||
role="img"
|
||||
>
|
||||
<svg
|
||||
aria-hidden="true"
|
||||
class=""
|
||||
data-icon="down"
|
||||
fill="currentColor"
|
||||
focusable="false"
|
||||
height="1em"
|
||||
viewBox="64 64 896 896"
|
||||
width="1em"
|
||||
>
|
||||
<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"
|
||||
/>
|
||||
</svg>
|
||||
</span>
|
||||
<span
|
||||
class="ant-transfer-list-header-selected"
|
||||
>
|
||||
<span>
|
||||
1 item
|
||||
</span>
|
||||
<span
|
||||
class="ant-transfer-list-header-title"
|
||||
/>
|
||||
1 item
|
||||
</span>
|
||||
<span
|
||||
class="ant-transfer-list-header-title"
|
||||
/>
|
||||
</div>
|
||||
<div
|
||||
class="ant-transfer-list-body"
|
||||
@ -597,16 +687,34 @@ exports[`Transfer should show sorted targetkey 1`] = `
|
||||
/>
|
||||
</span>
|
||||
</label>
|
||||
<span
|
||||
aria-label="down"
|
||||
class="anticon anticon-down ant-dropdown-trigger ant-transfer-list-header-dropdown"
|
||||
role="img"
|
||||
>
|
||||
<svg
|
||||
aria-hidden="true"
|
||||
class=""
|
||||
data-icon="down"
|
||||
fill="currentColor"
|
||||
focusable="false"
|
||||
height="1em"
|
||||
viewBox="64 64 896 896"
|
||||
width="1em"
|
||||
>
|
||||
<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"
|
||||
/>
|
||||
</svg>
|
||||
</span>
|
||||
<span
|
||||
class="ant-transfer-list-header-selected"
|
||||
>
|
||||
<span>
|
||||
2 items
|
||||
</span>
|
||||
<span
|
||||
class="ant-transfer-list-header-title"
|
||||
/>
|
||||
2 items
|
||||
</span>
|
||||
<span
|
||||
class="ant-transfer-list-header-title"
|
||||
/>
|
||||
</div>
|
||||
<div
|
||||
class="ant-transfer-list-body"
|
||||
@ -691,7 +799,13 @@ exports[`Transfer should support render value and label in item 1`] = `
|
||||
Object {
|
||||
"itemUnit": "item",
|
||||
"itemsUnit": "items",
|
||||
"remove": "Remove",
|
||||
"removeAll": "Remove all data",
|
||||
"removeCurrent": "Remove current page",
|
||||
"searchPlaceholder": "Search here",
|
||||
"selectAll": "Select all data",
|
||||
"selectCurrent": "Select current page",
|
||||
"selectInvert": "Invert current page",
|
||||
"titles": Array [
|
||||
"",
|
||||
"",
|
||||
@ -726,8 +840,14 @@ exports[`Transfer should support render value and label in item 1`] = `
|
||||
onItemSelectAll={[Function]}
|
||||
onScroll={[Function]}
|
||||
prefixCls="ant-transfer-list"
|
||||
remove="Remove"
|
||||
removeAll="Remove all data"
|
||||
removeCurrent="Remove current page"
|
||||
render={[Function]}
|
||||
searchPlaceholder="Search here"
|
||||
selectAll="Select all data"
|
||||
selectCurrent="Select current page"
|
||||
selectInvert="Invert current page"
|
||||
showSearch={false}
|
||||
titleText=""
|
||||
titles={
|
||||
@ -781,18 +901,275 @@ exports[`Transfer should support render value and label in item 1`] = `
|
||||
</Checkbox>
|
||||
</label>
|
||||
</Checkbox>
|
||||
<Dropdown
|
||||
className="ant-transfer-list-header-dropdown"
|
||||
mouseEnterDelay={0.15}
|
||||
mouseLeaveDelay={0.1}
|
||||
overlay={
|
||||
<Menu>
|
||||
<MenuItem
|
||||
onClick={[Function]}
|
||||
>
|
||||
Select all data
|
||||
</MenuItem>
|
||||
<MenuItem
|
||||
onClick={[Function]}
|
||||
>
|
||||
Invert current page
|
||||
</MenuItem>
|
||||
</Menu>
|
||||
}
|
||||
>
|
||||
<ForwardRef(Dropdown)
|
||||
className="ant-transfer-list-header-dropdown"
|
||||
mouseEnterDelay={0.15}
|
||||
mouseLeaveDelay={0.1}
|
||||
overlay={[Function]}
|
||||
overlayClassName=""
|
||||
placement="bottomLeft"
|
||||
prefixCls="ant-dropdown"
|
||||
transitionName="slide-up"
|
||||
>
|
||||
<Trigger
|
||||
action={
|
||||
Array [
|
||||
"hover",
|
||||
]
|
||||
}
|
||||
afterPopupVisibleChange={[Function]}
|
||||
blurDelay={0.15}
|
||||
builtinPlacements={
|
||||
Object {
|
||||
"bottomCenter": Object {
|
||||
"offset": Array [
|
||||
0,
|
||||
4,
|
||||
],
|
||||
"overflow": Object {
|
||||
"adjustX": 1,
|
||||
"adjustY": 1,
|
||||
},
|
||||
"points": Array [
|
||||
"tc",
|
||||
"bc",
|
||||
],
|
||||
"targetOffset": Array [
|
||||
0,
|
||||
0,
|
||||
],
|
||||
},
|
||||
"bottomLeft": Object {
|
||||
"offset": Array [
|
||||
0,
|
||||
4,
|
||||
],
|
||||
"overflow": Object {
|
||||
"adjustX": 1,
|
||||
"adjustY": 1,
|
||||
},
|
||||
"points": Array [
|
||||
"tl",
|
||||
"bl",
|
||||
],
|
||||
"targetOffset": Array [
|
||||
0,
|
||||
0,
|
||||
],
|
||||
},
|
||||
"bottomRight": Object {
|
||||
"offset": Array [
|
||||
0,
|
||||
4,
|
||||
],
|
||||
"overflow": Object {
|
||||
"adjustX": 1,
|
||||
"adjustY": 1,
|
||||
},
|
||||
"points": Array [
|
||||
"tr",
|
||||
"br",
|
||||
],
|
||||
"targetOffset": Array [
|
||||
0,
|
||||
0,
|
||||
],
|
||||
},
|
||||
"topCenter": Object {
|
||||
"offset": Array [
|
||||
0,
|
||||
-4,
|
||||
],
|
||||
"overflow": Object {
|
||||
"adjustX": 1,
|
||||
"adjustY": 1,
|
||||
},
|
||||
"points": Array [
|
||||
"bc",
|
||||
"tc",
|
||||
],
|
||||
"targetOffset": Array [
|
||||
0,
|
||||
0,
|
||||
],
|
||||
},
|
||||
"topLeft": Object {
|
||||
"offset": Array [
|
||||
0,
|
||||
-4,
|
||||
],
|
||||
"overflow": Object {
|
||||
"adjustX": 1,
|
||||
"adjustY": 1,
|
||||
},
|
||||
"points": Array [
|
||||
"bl",
|
||||
"tl",
|
||||
],
|
||||
"targetOffset": Array [
|
||||
0,
|
||||
0,
|
||||
],
|
||||
},
|
||||
"topRight": Object {
|
||||
"offset": Array [
|
||||
0,
|
||||
-4,
|
||||
],
|
||||
"overflow": Object {
|
||||
"adjustX": 1,
|
||||
"adjustY": 1,
|
||||
},
|
||||
"points": Array [
|
||||
"br",
|
||||
"tr",
|
||||
],
|
||||
"targetOffset": Array [
|
||||
0,
|
||||
0,
|
||||
],
|
||||
},
|
||||
}
|
||||
}
|
||||
className="ant-transfer-list-header-dropdown"
|
||||
defaultPopupVisible={false}
|
||||
destroyPopupOnHide={false}
|
||||
focusDelay={0}
|
||||
getDocument={[Function]}
|
||||
getPopupClassNameFromAlign={[Function]}
|
||||
hideAction={Array []}
|
||||
mask={false}
|
||||
maskClosable={true}
|
||||
mouseEnterDelay={0.15}
|
||||
mouseLeaveDelay={0.1}
|
||||
onPopupAlign={[Function]}
|
||||
onPopupVisibleChange={[Function]}
|
||||
overlay={[Function]}
|
||||
popup={[Function]}
|
||||
popupAlign={Object {}}
|
||||
popupClassName=""
|
||||
popupPlacement="bottomLeft"
|
||||
popupStyle={Object {}}
|
||||
popupTransitionName="slide-up"
|
||||
prefixCls="ant-dropdown"
|
||||
showAction={Array []}
|
||||
stretch="minWidth"
|
||||
>
|
||||
<ForwardRef(DownOutlined)
|
||||
className="ant-dropdown-trigger ant-transfer-list-header-dropdown"
|
||||
key="trigger"
|
||||
onMouseEnter={[Function]}
|
||||
onMouseLeave={[Function]}
|
||||
>
|
||||
<AntdIcon
|
||||
className="ant-dropdown-trigger ant-transfer-list-header-dropdown"
|
||||
icon={
|
||||
Object {
|
||||
"icon": Object {
|
||||
"attrs": Object {
|
||||
"focusable": "false",
|
||||
"viewBox": "64 64 896 896",
|
||||
},
|
||||
"children": Array [
|
||||
Object {
|
||||
"attrs": Object {
|
||||
"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",
|
||||
},
|
||||
"tag": "path",
|
||||
},
|
||||
],
|
||||
"tag": "svg",
|
||||
},
|
||||
"name": "down",
|
||||
"theme": "outlined",
|
||||
}
|
||||
}
|
||||
onMouseEnter={[Function]}
|
||||
onMouseLeave={[Function]}
|
||||
>
|
||||
<span
|
||||
aria-label="down"
|
||||
className="anticon anticon-down ant-dropdown-trigger ant-transfer-list-header-dropdown"
|
||||
onMouseEnter={[Function]}
|
||||
onMouseLeave={[Function]}
|
||||
role="img"
|
||||
>
|
||||
<IconReact
|
||||
className=""
|
||||
icon={
|
||||
Object {
|
||||
"icon": Object {
|
||||
"attrs": Object {
|
||||
"focusable": "false",
|
||||
"viewBox": "64 64 896 896",
|
||||
},
|
||||
"children": Array [
|
||||
Object {
|
||||
"attrs": Object {
|
||||
"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",
|
||||
},
|
||||
"tag": "path",
|
||||
},
|
||||
],
|
||||
"tag": "svg",
|
||||
},
|
||||
"name": "down",
|
||||
"theme": "outlined",
|
||||
}
|
||||
}
|
||||
>
|
||||
<svg
|
||||
aria-hidden="true"
|
||||
className=""
|
||||
data-icon="down"
|
||||
fill="currentColor"
|
||||
focusable="false"
|
||||
height="1em"
|
||||
key="svg-down"
|
||||
viewBox="64 64 896 896"
|
||||
width="1em"
|
||||
>
|
||||
<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"
|
||||
key="svg-down-svg-0"
|
||||
/>
|
||||
</svg>
|
||||
</IconReact>
|
||||
</span>
|
||||
</AntdIcon>
|
||||
</ForwardRef(DownOutlined)>
|
||||
</Trigger>
|
||||
</ForwardRef(Dropdown)>
|
||||
</Dropdown>
|
||||
<span
|
||||
className="ant-transfer-list-header-selected"
|
||||
>
|
||||
<span>
|
||||
1
|
||||
|
||||
item
|
||||
</span>
|
||||
<span
|
||||
className="ant-transfer-list-header-title"
|
||||
/>
|
||||
1
|
||||
|
||||
item
|
||||
</span>
|
||||
<span
|
||||
className="ant-transfer-list-header-title"
|
||||
/>
|
||||
</div>
|
||||
<div
|
||||
className="ant-transfer-list-body"
|
||||
@ -838,8 +1215,14 @@ exports[`Transfer should support render value and label in item 1`] = `
|
||||
onItemSelectAll={[Function]}
|
||||
onScroll={[Function]}
|
||||
prefixCls="ant-transfer-list"
|
||||
remove="Remove"
|
||||
removeAll="Remove all data"
|
||||
removeCurrent="Remove current page"
|
||||
render={[Function]}
|
||||
searchPlaceholder="Search here"
|
||||
selectAll="Select all data"
|
||||
selectCurrent="Select current page"
|
||||
selectInvert="Invert current page"
|
||||
selectedKeys={Array []}
|
||||
showSearch={false}
|
||||
titleText=""
|
||||
@ -864,58 +1247,80 @@ exports[`Transfer should support render value and label in item 1`] = `
|
||||
}
|
||||
key="a"
|
||||
onClick={[Function]}
|
||||
onRemove={[Function]}
|
||||
prefixCls="ant-transfer-list"
|
||||
renderedEl="label"
|
||||
renderedText="title value"
|
||||
>
|
||||
<li
|
||||
className="ant-transfer-list-content-item"
|
||||
onClick={[Function]}
|
||||
title="title value"
|
||||
<LocaleReceiver
|
||||
componentName="Transfer"
|
||||
defaultLocale={
|
||||
Object {
|
||||
"itemUnit": "item",
|
||||
"itemsUnit": "items",
|
||||
"remove": "Remove",
|
||||
"removeAll": "Remove all data",
|
||||
"removeCurrent": "Remove current page",
|
||||
"searchPlaceholder": "Search here",
|
||||
"selectAll": "Select all data",
|
||||
"selectCurrent": "Select current page",
|
||||
"selectInvert": "Invert current page",
|
||||
"titles": Array [
|
||||
"",
|
||||
"",
|
||||
],
|
||||
}
|
||||
}
|
||||
>
|
||||
<Checkbox
|
||||
checked={false}
|
||||
indeterminate={false}
|
||||
<li
|
||||
className="ant-transfer-list-content-item"
|
||||
onClick={[Function]}
|
||||
title="title value"
|
||||
>
|
||||
<label
|
||||
className="ant-checkbox-wrapper"
|
||||
<Checkbox
|
||||
checked={false}
|
||||
indeterminate={false}
|
||||
>
|
||||
<Checkbox
|
||||
checked={false}
|
||||
className=""
|
||||
defaultChecked={false}
|
||||
onBlur={[Function]}
|
||||
onChange={[Function]}
|
||||
onFocus={[Function]}
|
||||
prefixCls="ant-checkbox"
|
||||
style={Object {}}
|
||||
type="checkbox"
|
||||
<label
|
||||
className="ant-checkbox-wrapper"
|
||||
>
|
||||
<span
|
||||
className="ant-checkbox"
|
||||
<Checkbox
|
||||
checked={false}
|
||||
className=""
|
||||
defaultChecked={false}
|
||||
onBlur={[Function]}
|
||||
onChange={[Function]}
|
||||
onFocus={[Function]}
|
||||
prefixCls="ant-checkbox"
|
||||
style={Object {}}
|
||||
type="checkbox"
|
||||
>
|
||||
<input
|
||||
checked={false}
|
||||
className="ant-checkbox-input"
|
||||
onBlur={[Function]}
|
||||
onChange={[Function]}
|
||||
onFocus={[Function]}
|
||||
type="checkbox"
|
||||
/>
|
||||
<span
|
||||
className="ant-checkbox-inner"
|
||||
/>
|
||||
</span>
|
||||
</Checkbox>
|
||||
</label>
|
||||
</Checkbox>
|
||||
<span
|
||||
className="ant-transfer-list-content-item-text"
|
||||
>
|
||||
label
|
||||
</span>
|
||||
</li>
|
||||
className="ant-checkbox"
|
||||
style={Object {}}
|
||||
>
|
||||
<input
|
||||
checked={false}
|
||||
className="ant-checkbox-input"
|
||||
onBlur={[Function]}
|
||||
onChange={[Function]}
|
||||
onFocus={[Function]}
|
||||
type="checkbox"
|
||||
/>
|
||||
<span
|
||||
className="ant-checkbox-inner"
|
||||
/>
|
||||
</span>
|
||||
</Checkbox>
|
||||
</label>
|
||||
</Checkbox>
|
||||
<span
|
||||
className="ant-transfer-list-content-item-text"
|
||||
>
|
||||
label
|
||||
</span>
|
||||
</li>
|
||||
</LocaleReceiver>
|
||||
</Memo(ListItem)>
|
||||
</ul>
|
||||
</ListBody>
|
||||
@ -1135,12 +1540,19 @@ exports[`Transfer should support render value and label in item 1`] = `
|
||||
[Function]
|
||||
</Context.Consumer>
|
||||
}
|
||||
onItemRemove={[Function]}
|
||||
onItemSelect={[Function]}
|
||||
onItemSelectAll={[Function]}
|
||||
onScroll={[Function]}
|
||||
prefixCls="ant-transfer-list"
|
||||
remove="Remove"
|
||||
removeAll="Remove all data"
|
||||
removeCurrent="Remove current page"
|
||||
render={[Function]}
|
||||
searchPlaceholder="Search here"
|
||||
selectAll="Select all data"
|
||||
selectCurrent="Select current page"
|
||||
selectInvert="Invert current page"
|
||||
showSearch={false}
|
||||
titleText=""
|
||||
titles={
|
||||
@ -1194,18 +1606,275 @@ exports[`Transfer should support render value and label in item 1`] = `
|
||||
</Checkbox>
|
||||
</label>
|
||||
</Checkbox>
|
||||
<Dropdown
|
||||
className="ant-transfer-list-header-dropdown"
|
||||
mouseEnterDelay={0.15}
|
||||
mouseLeaveDelay={0.1}
|
||||
overlay={
|
||||
<Menu>
|
||||
<MenuItem
|
||||
onClick={[Function]}
|
||||
>
|
||||
Select all data
|
||||
</MenuItem>
|
||||
<MenuItem
|
||||
onClick={[Function]}
|
||||
>
|
||||
Invert current page
|
||||
</MenuItem>
|
||||
</Menu>
|
||||
}
|
||||
>
|
||||
<ForwardRef(Dropdown)
|
||||
className="ant-transfer-list-header-dropdown"
|
||||
mouseEnterDelay={0.15}
|
||||
mouseLeaveDelay={0.1}
|
||||
overlay={[Function]}
|
||||
overlayClassName=""
|
||||
placement="bottomLeft"
|
||||
prefixCls="ant-dropdown"
|
||||
transitionName="slide-up"
|
||||
>
|
||||
<Trigger
|
||||
action={
|
||||
Array [
|
||||
"hover",
|
||||
]
|
||||
}
|
||||
afterPopupVisibleChange={[Function]}
|
||||
blurDelay={0.15}
|
||||
builtinPlacements={
|
||||
Object {
|
||||
"bottomCenter": Object {
|
||||
"offset": Array [
|
||||
0,
|
||||
4,
|
||||
],
|
||||
"overflow": Object {
|
||||
"adjustX": 1,
|
||||
"adjustY": 1,
|
||||
},
|
||||
"points": Array [
|
||||
"tc",
|
||||
"bc",
|
||||
],
|
||||
"targetOffset": Array [
|
||||
0,
|
||||
0,
|
||||
],
|
||||
},
|
||||
"bottomLeft": Object {
|
||||
"offset": Array [
|
||||
0,
|
||||
4,
|
||||
],
|
||||
"overflow": Object {
|
||||
"adjustX": 1,
|
||||
"adjustY": 1,
|
||||
},
|
||||
"points": Array [
|
||||
"tl",
|
||||
"bl",
|
||||
],
|
||||
"targetOffset": Array [
|
||||
0,
|
||||
0,
|
||||
],
|
||||
},
|
||||
"bottomRight": Object {
|
||||
"offset": Array [
|
||||
0,
|
||||
4,
|
||||
],
|
||||
"overflow": Object {
|
||||
"adjustX": 1,
|
||||
"adjustY": 1,
|
||||
},
|
||||
"points": Array [
|
||||
"tr",
|
||||
"br",
|
||||
],
|
||||
"targetOffset": Array [
|
||||
0,
|
||||
0,
|
||||
],
|
||||
},
|
||||
"topCenter": Object {
|
||||
"offset": Array [
|
||||
0,
|
||||
-4,
|
||||
],
|
||||
"overflow": Object {
|
||||
"adjustX": 1,
|
||||
"adjustY": 1,
|
||||
},
|
||||
"points": Array [
|
||||
"bc",
|
||||
"tc",
|
||||
],
|
||||
"targetOffset": Array [
|
||||
0,
|
||||
0,
|
||||
],
|
||||
},
|
||||
"topLeft": Object {
|
||||
"offset": Array [
|
||||
0,
|
||||
-4,
|
||||
],
|
||||
"overflow": Object {
|
||||
"adjustX": 1,
|
||||
"adjustY": 1,
|
||||
},
|
||||
"points": Array [
|
||||
"bl",
|
||||
"tl",
|
||||
],
|
||||
"targetOffset": Array [
|
||||
0,
|
||||
0,
|
||||
],
|
||||
},
|
||||
"topRight": Object {
|
||||
"offset": Array [
|
||||
0,
|
||||
-4,
|
||||
],
|
||||
"overflow": Object {
|
||||
"adjustX": 1,
|
||||
"adjustY": 1,
|
||||
},
|
||||
"points": Array [
|
||||
"br",
|
||||
"tr",
|
||||
],
|
||||
"targetOffset": Array [
|
||||
0,
|
||||
0,
|
||||
],
|
||||
},
|
||||
}
|
||||
}
|
||||
className="ant-transfer-list-header-dropdown"
|
||||
defaultPopupVisible={false}
|
||||
destroyPopupOnHide={false}
|
||||
focusDelay={0}
|
||||
getDocument={[Function]}
|
||||
getPopupClassNameFromAlign={[Function]}
|
||||
hideAction={Array []}
|
||||
mask={false}
|
||||
maskClosable={true}
|
||||
mouseEnterDelay={0.15}
|
||||
mouseLeaveDelay={0.1}
|
||||
onPopupAlign={[Function]}
|
||||
onPopupVisibleChange={[Function]}
|
||||
overlay={[Function]}
|
||||
popup={[Function]}
|
||||
popupAlign={Object {}}
|
||||
popupClassName=""
|
||||
popupPlacement="bottomLeft"
|
||||
popupStyle={Object {}}
|
||||
popupTransitionName="slide-up"
|
||||
prefixCls="ant-dropdown"
|
||||
showAction={Array []}
|
||||
stretch="minWidth"
|
||||
>
|
||||
<ForwardRef(DownOutlined)
|
||||
className="ant-dropdown-trigger ant-transfer-list-header-dropdown"
|
||||
key="trigger"
|
||||
onMouseEnter={[Function]}
|
||||
onMouseLeave={[Function]}
|
||||
>
|
||||
<AntdIcon
|
||||
className="ant-dropdown-trigger ant-transfer-list-header-dropdown"
|
||||
icon={
|
||||
Object {
|
||||
"icon": Object {
|
||||
"attrs": Object {
|
||||
"focusable": "false",
|
||||
"viewBox": "64 64 896 896",
|
||||
},
|
||||
"children": Array [
|
||||
Object {
|
||||
"attrs": Object {
|
||||
"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",
|
||||
},
|
||||
"tag": "path",
|
||||
},
|
||||
],
|
||||
"tag": "svg",
|
||||
},
|
||||
"name": "down",
|
||||
"theme": "outlined",
|
||||
}
|
||||
}
|
||||
onMouseEnter={[Function]}
|
||||
onMouseLeave={[Function]}
|
||||
>
|
||||
<span
|
||||
aria-label="down"
|
||||
className="anticon anticon-down ant-dropdown-trigger ant-transfer-list-header-dropdown"
|
||||
onMouseEnter={[Function]}
|
||||
onMouseLeave={[Function]}
|
||||
role="img"
|
||||
>
|
||||
<IconReact
|
||||
className=""
|
||||
icon={
|
||||
Object {
|
||||
"icon": Object {
|
||||
"attrs": Object {
|
||||
"focusable": "false",
|
||||
"viewBox": "64 64 896 896",
|
||||
},
|
||||
"children": Array [
|
||||
Object {
|
||||
"attrs": Object {
|
||||
"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",
|
||||
},
|
||||
"tag": "path",
|
||||
},
|
||||
],
|
||||
"tag": "svg",
|
||||
},
|
||||
"name": "down",
|
||||
"theme": "outlined",
|
||||
}
|
||||
}
|
||||
>
|
||||
<svg
|
||||
aria-hidden="true"
|
||||
className=""
|
||||
data-icon="down"
|
||||
fill="currentColor"
|
||||
focusable="false"
|
||||
height="1em"
|
||||
key="svg-down"
|
||||
viewBox="64 64 896 896"
|
||||
width="1em"
|
||||
>
|
||||
<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"
|
||||
key="svg-down-svg-0"
|
||||
/>
|
||||
</svg>
|
||||
</IconReact>
|
||||
</span>
|
||||
</AntdIcon>
|
||||
</ForwardRef(DownOutlined)>
|
||||
</Trigger>
|
||||
</ForwardRef(Dropdown)>
|
||||
</Dropdown>
|
||||
<span
|
||||
className="ant-transfer-list-header-selected"
|
||||
>
|
||||
<span>
|
||||
0
|
||||
|
||||
item
|
||||
</span>
|
||||
<span
|
||||
className="ant-transfer-list-header-title"
|
||||
/>
|
||||
0
|
||||
|
||||
item
|
||||
</span>
|
||||
<span
|
||||
className="ant-transfer-list-header-title"
|
||||
/>
|
||||
</div>
|
||||
<div
|
||||
className="ant-transfer-list-body"
|
||||
|
@ -23,16 +23,34 @@ exports[`Transfer.List should render correctly 1`] = `
|
||||
/>
|
||||
</span>
|
||||
</label>
|
||||
<span
|
||||
aria-label="down"
|
||||
class="anticon anticon-down ant-dropdown-trigger ant-transfer-list-header-dropdown"
|
||||
role="img"
|
||||
>
|
||||
<svg
|
||||
aria-hidden="true"
|
||||
class=""
|
||||
data-icon="down"
|
||||
fill="currentColor"
|
||||
focusable="false"
|
||||
height="1em"
|
||||
viewBox="64 64 896 896"
|
||||
width="1em"
|
||||
>
|
||||
<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"
|
||||
/>
|
||||
</svg>
|
||||
</span>
|
||||
<span
|
||||
class="ant-transfer-list-header-selected"
|
||||
>
|
||||
<span>
|
||||
1/3
|
||||
</span>
|
||||
<span
|
||||
class="ant-transfer-list-header-title"
|
||||
/>
|
||||
1/3
|
||||
</span>
|
||||
<span
|
||||
class="ant-transfer-list-header-title"
|
||||
/>
|
||||
</div>
|
||||
<div
|
||||
class="ant-transfer-list-body"
|
||||
|
@ -13,7 +13,7 @@ describe('Transfer.Customize', () => {
|
||||
errorSpy.mockRestore();
|
||||
});
|
||||
|
||||
it('props#body doesnot work anymore', () => {
|
||||
it('props#body does not work anymore', () => {
|
||||
const body = jest.fn();
|
||||
mount(<Transfer body={body} />);
|
||||
|
||||
@ -49,4 +49,16 @@ describe('Transfer.Customize', () => {
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
it('warning if use `pagination`', () => {
|
||||
mount(
|
||||
<Transfer dataSource={[]} pagination>
|
||||
{() => null}
|
||||
</Transfer>,
|
||||
);
|
||||
|
||||
expect(errorSpy).toHaveBeenCalledWith(
|
||||
'Warning: [antd: Transfer] `pagination` not support customize render list.',
|
||||
);
|
||||
});
|
||||
});
|
||||
|
110
components/transfer/__tests__/dropdown.test.js
Normal file
110
components/transfer/__tests__/dropdown.test.js
Normal file
@ -0,0 +1,110 @@
|
||||
/* eslint no-use-before-define: "off" */
|
||||
import React from 'react';
|
||||
import { act } from 'react-dom/test-utils';
|
||||
import { mount } from 'enzyme';
|
||||
import Transfer from '..';
|
||||
|
||||
const listProps = {
|
||||
dataSource: [
|
||||
{
|
||||
key: 'a',
|
||||
title: 'a',
|
||||
disabled: true,
|
||||
},
|
||||
{
|
||||
key: 'b',
|
||||
title: 'b',
|
||||
},
|
||||
{
|
||||
key: 'c',
|
||||
title: 'c',
|
||||
},
|
||||
{
|
||||
key: 'd',
|
||||
title: 'd',
|
||||
},
|
||||
{
|
||||
key: 'e',
|
||||
title: 'e',
|
||||
},
|
||||
],
|
||||
selectedKeys: ['b'],
|
||||
targetKeys: [],
|
||||
pagination: { pageSize: 4 },
|
||||
};
|
||||
|
||||
describe('Transfer.Dropdown', () => {
|
||||
function clickItem(wrapper, index) {
|
||||
wrapper.find('li.ant-dropdown-menu-item').at(index).simulate('click');
|
||||
}
|
||||
|
||||
it('select all', () => {
|
||||
jest.useFakeTimers();
|
||||
|
||||
const onSelectChange = jest.fn();
|
||||
const wrapper = mount(<Transfer {...listProps} onSelectChange={onSelectChange} />);
|
||||
wrapper.find('.ant-transfer-list-header-dropdown').first().simulate('mouseenter');
|
||||
act(() => {
|
||||
jest.runAllTimers();
|
||||
});
|
||||
wrapper.update();
|
||||
|
||||
clickItem(wrapper.find('.ant-dropdown-menu').first(), 0);
|
||||
expect(onSelectChange).toHaveBeenCalledWith(['b', 'c', 'd', 'e'], []);
|
||||
|
||||
jest.useRealTimers();
|
||||
});
|
||||
|
||||
it('select current page', () => {
|
||||
jest.useFakeTimers();
|
||||
|
||||
const onSelectChange = jest.fn();
|
||||
const wrapper = mount(<Transfer {...listProps} onSelectChange={onSelectChange} />);
|
||||
wrapper.find('.ant-transfer-list-header-dropdown').first().simulate('mouseenter');
|
||||
act(() => {
|
||||
jest.runAllTimers();
|
||||
});
|
||||
wrapper.update();
|
||||
|
||||
clickItem(wrapper.find('.ant-dropdown-menu').first(), 1);
|
||||
expect(onSelectChange).toHaveBeenCalledWith(['b', 'c', 'd'], []);
|
||||
|
||||
jest.useRealTimers();
|
||||
});
|
||||
|
||||
it('select invert', () => {
|
||||
jest.useFakeTimers();
|
||||
|
||||
const onSelectChange = jest.fn();
|
||||
const wrapper = mount(<Transfer {...listProps} onSelectChange={onSelectChange} />);
|
||||
wrapper.find('.ant-transfer-list-header-dropdown').first().simulate('mouseenter');
|
||||
act(() => {
|
||||
jest.runAllTimers();
|
||||
});
|
||||
wrapper.update();
|
||||
|
||||
clickItem(wrapper.find('.ant-dropdown-menu').first(), 2);
|
||||
expect(onSelectChange).toHaveBeenCalledWith(['c', 'd'], []);
|
||||
|
||||
jest.useRealTimers();
|
||||
});
|
||||
|
||||
it('oneWay to remove', () => {
|
||||
jest.useFakeTimers();
|
||||
|
||||
const onChange = jest.fn();
|
||||
const wrapper = mount(
|
||||
<Transfer {...listProps} targetKeys={['b', 'c']} oneWay onChange={onChange} />,
|
||||
);
|
||||
wrapper.find('.ant-transfer-list-header-dropdown').last().simulate('mouseenter');
|
||||
act(() => {
|
||||
jest.runAllTimers();
|
||||
});
|
||||
wrapper.update();
|
||||
|
||||
clickItem(wrapper.find('.ant-dropdown-menu').first(), 0);
|
||||
expect(onChange).toHaveBeenCalledWith([], 'left', ['b', 'c']);
|
||||
|
||||
jest.useRealTimers();
|
||||
});
|
||||
});
|
@ -104,11 +104,7 @@ describe('Transfer', () => {
|
||||
it('should move selected keys to corresponding list', () => {
|
||||
const handleChange = jest.fn();
|
||||
const wrapper = mount(<Transfer {...listCommonProps} onChange={handleChange} />);
|
||||
wrapper
|
||||
.find(TransferOperation)
|
||||
.find(Button)
|
||||
.at(0)
|
||||
.simulate('click'); // move selected keys to right list
|
||||
wrapper.find(TransferOperation).find(Button).at(0).simulate('click'); // move selected keys to right list
|
||||
expect(handleChange).toHaveBeenCalledWith(['a', 'b'], 'right', ['a']);
|
||||
});
|
||||
|
||||
@ -122,22 +118,14 @@ describe('Transfer', () => {
|
||||
onChange={handleChange}
|
||||
/>,
|
||||
);
|
||||
wrapper
|
||||
.find(TransferOperation)
|
||||
.find(Button)
|
||||
.at(1)
|
||||
.simulate('click'); // move selected keys to left list
|
||||
wrapper.find(TransferOperation).find(Button).at(1).simulate('click'); // move selected keys to left list
|
||||
expect(handleChange).toHaveBeenCalledWith([], 'left', ['a']);
|
||||
});
|
||||
|
||||
it('should move selected keys expect disabled to corresponding list', () => {
|
||||
const handleChange = jest.fn();
|
||||
const wrapper = mount(<Transfer {...listDisabledProps} onChange={handleChange} />);
|
||||
wrapper
|
||||
.find(TransferOperation)
|
||||
.find(Button)
|
||||
.at(0)
|
||||
.simulate('click'); // move selected keys to right list
|
||||
wrapper.find(TransferOperation).find(Button).at(0).simulate('click'); // move selected keys to right list
|
||||
expect(handleChange).toHaveBeenCalledWith(['b'], 'right', ['b']);
|
||||
});
|
||||
|
||||
@ -211,20 +199,14 @@ describe('Transfer', () => {
|
||||
.at(0)
|
||||
.find('input')
|
||||
.simulate('change', { target: { value: 'a' } });
|
||||
expect(
|
||||
wrapper
|
||||
.find(TransferList)
|
||||
.at(0)
|
||||
.find(TransferItem)
|
||||
.find(Checkbox),
|
||||
).toHaveLength(1);
|
||||
expect(wrapper.find(TransferList).at(0).find(TransferItem).find(Checkbox)).toHaveLength(1);
|
||||
});
|
||||
|
||||
const headerText = wrapper =>
|
||||
wrapper
|
||||
.find(TransferList)
|
||||
.at(0)
|
||||
.find('.ant-transfer-list-header-selected > span')
|
||||
.find('.ant-transfer-list-header-selected')
|
||||
.at(0)
|
||||
.first()
|
||||
.text()
|
||||
@ -259,21 +241,11 @@ describe('Transfer', () => {
|
||||
expect(headerText(wrapper)).toEqual('0 Person');
|
||||
|
||||
expect(
|
||||
wrapper
|
||||
.find(TransferList)
|
||||
.at(0)
|
||||
.find('.ant-transfer-list-search')
|
||||
.at(0)
|
||||
.prop('placeholder'),
|
||||
wrapper.find(TransferList).at(0).find('.ant-transfer-list-search').at(0).prop('placeholder'),
|
||||
).toEqual('Search');
|
||||
|
||||
expect(
|
||||
wrapper
|
||||
.find(TransferList)
|
||||
.at(0)
|
||||
.find('.ant-transfer-list-body-not-found')
|
||||
.at(0)
|
||||
.text(),
|
||||
wrapper.find(TransferList).at(0).find('.ant-transfer-list-body-not-found').at(0).text(),
|
||||
).toEqual('Nothing');
|
||||
});
|
||||
|
||||
@ -292,21 +264,11 @@ describe('Transfer', () => {
|
||||
);
|
||||
|
||||
expect(
|
||||
wrapper
|
||||
.find(TransferList)
|
||||
.at(0)
|
||||
.find('.ant-transfer-list-search')
|
||||
.at(0)
|
||||
.prop('placeholder'),
|
||||
wrapper.find(TransferList).at(0).find('.ant-transfer-list-search').at(0).prop('placeholder'),
|
||||
).toEqual('new2');
|
||||
|
||||
expect(
|
||||
wrapper
|
||||
.find(TransferList)
|
||||
.at(0)
|
||||
.find('.ant-transfer-list-body-not-found')
|
||||
.at(0)
|
||||
.text(),
|
||||
wrapper.find(TransferList).at(0).find('.ant-transfer-list-body-not-found').at(0).text(),
|
||||
).toEqual('new1');
|
||||
|
||||
expect(consoleErrorSpy).not.toHaveBeenCalledWith(
|
||||
@ -378,11 +340,7 @@ describe('Transfer', () => {
|
||||
.find('.ant-transfer-list-header input[type="checkbox"]')
|
||||
.filterWhere(n => !n.prop('checked'))
|
||||
.simulate('change');
|
||||
wrapper
|
||||
.find(TransferOperation)
|
||||
.find(Button)
|
||||
.at(0)
|
||||
.simulate('click');
|
||||
wrapper.find(TransferOperation).find(Button).at(0).simulate('click');
|
||||
expect(handleChange).toHaveBeenCalledWith(['1', '3', '4'], 'right', ['1']);
|
||||
});
|
||||
|
||||
@ -423,7 +381,7 @@ describe('Transfer', () => {
|
||||
expect(handleSelectChange).toHaveBeenLastCalledWith(['b'], []);
|
||||
});
|
||||
|
||||
it('should show sorted targetkey', () => {
|
||||
it('should show sorted targetKey', () => {
|
||||
const sortedTargetKeyProps = {
|
||||
dataSource: [
|
||||
{
|
||||
@ -537,4 +495,51 @@ describe('Transfer', () => {
|
||||
const wrapper = mount(<Transfer {...listCommonProps} selectAllLabels={selectAllLabels} />);
|
||||
expect(headerText(wrapper)).toEqual('1 of 2');
|
||||
});
|
||||
|
||||
describe('pagination', () => {
|
||||
it('boolean', () => {
|
||||
const wrapper = mount(<Transfer {...listDisabledProps} pagination />);
|
||||
expect(wrapper.find('Pagination').first().props()).toEqual(
|
||||
expect.objectContaining({
|
||||
pageSize: 10,
|
||||
}),
|
||||
);
|
||||
});
|
||||
|
||||
it('object', () => {
|
||||
const wrapper = mount(<Transfer {...listDisabledProps} pagination={{ pageSize: 1 }} />);
|
||||
expect(
|
||||
wrapper.find('.ant-transfer-list').first().find('.ant-transfer-list-content-item'),
|
||||
).toHaveLength(1);
|
||||
expect(wrapper.find('Pagination').first().props()).toEqual(
|
||||
expect.objectContaining({
|
||||
pageSize: 1,
|
||||
}),
|
||||
);
|
||||
});
|
||||
|
||||
it('not exceed max size', () => {
|
||||
const wrapper = mount(<Transfer {...listDisabledProps} pagination={{ pageSize: 1 }} />);
|
||||
wrapper.find('.ant-pagination-next .ant-pagination-item-link').first().simulate('click');
|
||||
expect(wrapper.find('Pagination').first().props()).toEqual(
|
||||
expect.objectContaining({
|
||||
current: 2,
|
||||
}),
|
||||
);
|
||||
|
||||
wrapper.setProps({ targetKeys: ['b', 'c'] });
|
||||
expect(wrapper.find('Pagination').first().props()).toEqual(
|
||||
expect.objectContaining({
|
||||
current: 1,
|
||||
}),
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
it('remove by click icon', () => {
|
||||
const onChange = jest.fn();
|
||||
const wrapper = mount(<Transfer {...listCommonProps} onChange={onChange} oneWay />);
|
||||
wrapper.find('.ant-transfer-list-content-item-remove').first().simulate('click');
|
||||
expect(onChange).toHaveBeenCalledWith([], 'left', ['b']);
|
||||
});
|
||||
});
|
||||
|
@ -14,7 +14,7 @@ title:
|
||||
The most basic usage of `Transfer` involves providing the source data and target keys arrays, plus the rendering and some callback functions.
|
||||
|
||||
```jsx
|
||||
import { Transfer, Switch } from 'antd';
|
||||
import { Space, Transfer, Switch } from 'antd';
|
||||
|
||||
const mockData = [];
|
||||
for (let i = 0; i < 20; i++) {
|
||||
@ -74,13 +74,14 @@ class App extends React.Component {
|
||||
render={item => item.title}
|
||||
disabled={disabled}
|
||||
/>
|
||||
<Switch
|
||||
unCheckedChildren="disabled"
|
||||
checkedChildren="disabled"
|
||||
checked={disabled}
|
||||
onChange={this.handleDisable}
|
||||
style={{ marginTop: 16 }}
|
||||
/>
|
||||
<Space style={{ marginTop: 16 }}>
|
||||
<Switch
|
||||
unCheckedChildren="disabled"
|
||||
checkedChildren="disabled"
|
||||
checked={disabled}
|
||||
onChange={this.handleDisable}
|
||||
/>
|
||||
</Space>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
@ -1,35 +1,29 @@
|
||||
---
|
||||
order: 4
|
||||
debug: true
|
||||
title:
|
||||
zh-CN: 大数据性能测试
|
||||
en-US: Performance Test
|
||||
zh-CN: 分页
|
||||
en-US: Pagination
|
||||
---
|
||||
|
||||
## zh-CN
|
||||
|
||||
2000 条数据。
|
||||
大数据下使用分页。
|
||||
|
||||
## en-US
|
||||
|
||||
2000 items.
|
||||
large count of items with pagination.
|
||||
|
||||
```jsx
|
||||
import { Transfer } from 'antd';
|
||||
import { Transfer, Switch } from 'antd';
|
||||
|
||||
class App extends React.Component {
|
||||
state = {
|
||||
mockData: [],
|
||||
targetKeys: [],
|
||||
};
|
||||
const App = () => {
|
||||
const [oneWay, setOneWay] = React.useState(false);
|
||||
const [mockData, setMockData] = React.useState([]);
|
||||
const [targetKeys, setTargetKeys] = React.useState([]);
|
||||
|
||||
componentDidMount() {
|
||||
this.getMock();
|
||||
}
|
||||
|
||||
getMock = () => {
|
||||
const targetKeys = [];
|
||||
const mockData = [];
|
||||
React.useEffect(() => {
|
||||
const newTargetKeys = [];
|
||||
const newMockData = [];
|
||||
for (let i = 0; i < 2000; i++) {
|
||||
const data = {
|
||||
key: i.toString(),
|
||||
@ -38,29 +32,40 @@ class App extends React.Component {
|
||||
chosen: Math.random() * 2 > 1,
|
||||
};
|
||||
if (data.chosen) {
|
||||
targetKeys.push(data.key);
|
||||
newTargetKeys.push(data.key);
|
||||
}
|
||||
mockData.push(data);
|
||||
newMockData.push(data);
|
||||
}
|
||||
this.setState({ mockData, targetKeys });
|
||||
|
||||
setTargetKeys(newTargetKeys);
|
||||
setMockData(newMockData);
|
||||
}, []);
|
||||
|
||||
const onChange = (newTargetKeys, direction, moveKeys) => {
|
||||
console.log(newTargetKeys, direction, moveKeys);
|
||||
setTargetKeys(newTargetKeys);
|
||||
};
|
||||
|
||||
handleChange = (targetKeys, direction, moveKeys) => {
|
||||
console.log(targetKeys, direction, moveKeys);
|
||||
this.setState({ targetKeys });
|
||||
};
|
||||
|
||||
render() {
|
||||
return (
|
||||
return (
|
||||
<>
|
||||
<Transfer
|
||||
dataSource={this.state.mockData}
|
||||
targetKeys={this.state.targetKeys}
|
||||
onChange={this.handleChange}
|
||||
dataSource={mockData}
|
||||
targetKeys={targetKeys}
|
||||
onChange={onChange}
|
||||
render={item => item.title}
|
||||
oneWay={oneWay}
|
||||
pagination
|
||||
/>
|
||||
);
|
||||
}
|
||||
}
|
||||
<br />
|
||||
<Switch
|
||||
unCheckedChildren="one way"
|
||||
checkedChildren="one way"
|
||||
checked={oneWay}
|
||||
onChange={setOneWay}
|
||||
/>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
ReactDOM.render(<App />, mountNode);
|
||||
```
|
||||
|
92
components/transfer/demo/oneWay.md
Normal file
92
components/transfer/demo/oneWay.md
Normal file
@ -0,0 +1,92 @@
|
||||
---
|
||||
order: 0.1
|
||||
title:
|
||||
zh-CN: 单向样式
|
||||
en-US: One Way
|
||||
---
|
||||
|
||||
## zh-CN
|
||||
|
||||
通过 `oneWay` 将 Transfer 转为单向样式。
|
||||
|
||||
## en-US
|
||||
|
||||
Use `oneWay` to makes Transfer to one way style.
|
||||
|
||||
```jsx
|
||||
import { Space, Transfer, Switch } from 'antd';
|
||||
|
||||
const 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);
|
||||
|
||||
class App extends React.Component {
|
||||
state = {
|
||||
targetKeys: oriTargetKeys,
|
||||
selectedKeys: [],
|
||||
disabled: false,
|
||||
};
|
||||
|
||||
handleChange = (nextTargetKeys, direction, moveKeys) => {
|
||||
this.setState({ targetKeys: nextTargetKeys });
|
||||
|
||||
console.log('targetKeys: ', nextTargetKeys);
|
||||
console.log('direction: ', direction);
|
||||
console.log('moveKeys: ', moveKeys);
|
||||
};
|
||||
|
||||
handleSelectChange = (sourceSelectedKeys, targetSelectedKeys) => {
|
||||
this.setState({ selectedKeys: [...sourceSelectedKeys, ...targetSelectedKeys] });
|
||||
|
||||
console.log('sourceSelectedKeys: ', sourceSelectedKeys);
|
||||
console.log('targetSelectedKeys: ', targetSelectedKeys);
|
||||
};
|
||||
|
||||
handleScroll = (direction, e) => {
|
||||
console.log('direction:', direction);
|
||||
console.log('target:', e.target);
|
||||
};
|
||||
|
||||
handleDisable = disabled => {
|
||||
this.setState({ disabled });
|
||||
};
|
||||
|
||||
render() {
|
||||
const { targetKeys, selectedKeys, disabled } = this.state;
|
||||
return (
|
||||
<div>
|
||||
<Transfer
|
||||
dataSource={mockData}
|
||||
titles={['Source', 'Target']}
|
||||
targetKeys={targetKeys}
|
||||
selectedKeys={selectedKeys}
|
||||
onChange={this.handleChange}
|
||||
onSelectChange={this.handleSelectChange}
|
||||
onScroll={this.handleScroll}
|
||||
render={item => item.title}
|
||||
disabled={disabled}
|
||||
oneWay
|
||||
/>
|
||||
<Space style={{ marginTop: 16 }}>
|
||||
<Switch
|
||||
unCheckedChildren="disabled"
|
||||
checkedChildren="disabled"
|
||||
checked={disabled}
|
||||
onChange={this.handleDisable}
|
||||
/>
|
||||
</Space>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
ReactDOM.render(<App />, mountNode);
|
||||
```
|
@ -16,19 +16,17 @@ Customize render list with Tree component.
|
||||
```jsx
|
||||
import { Transfer, Tree } from 'antd';
|
||||
|
||||
const { TreeNode } = Tree;
|
||||
|
||||
// Customize Table Transfer
|
||||
const isChecked = (selectedKeys, eventKey) => {
|
||||
return selectedKeys.indexOf(eventKey) !== -1;
|
||||
};
|
||||
|
||||
const generateTree = (treeNodes = [], checkedKeys = []) => {
|
||||
return treeNodes.map(({ children, ...props }) => (
|
||||
<TreeNode {...props} disabled={checkedKeys.includes(props.key)} key={props.key}>
|
||||
{generateTree(children, checkedKeys)}
|
||||
</TreeNode>
|
||||
));
|
||||
return treeNodes.map(({ children, ...props }) => ({
|
||||
...props,
|
||||
disabled: checkedKeys.includes(props.key),
|
||||
children: generateTree(children, checkedKeys),
|
||||
}));
|
||||
};
|
||||
|
||||
const TreeTransfer = ({ dataSource, targetKeys, ...restProps }) => {
|
||||
@ -60,29 +58,14 @@ const TreeTransfer = ({ dataSource, targetKeys, ...restProps }) => {
|
||||
checkStrictly
|
||||
defaultExpandAll
|
||||
checkedKeys={checkedKeys}
|
||||
onCheck={(
|
||||
_,
|
||||
{
|
||||
node: {
|
||||
props: { eventKey },
|
||||
},
|
||||
},
|
||||
) => {
|
||||
onItemSelect(eventKey, !isChecked(checkedKeys, eventKey));
|
||||
treeData={generateTree(dataSource, targetKeys)}
|
||||
onCheck={(_, { node: { key } }) => {
|
||||
onItemSelect(key, !isChecked(checkedKeys, key));
|
||||
}}
|
||||
onSelect={(
|
||||
_,
|
||||
{
|
||||
node: {
|
||||
props: { eventKey },
|
||||
},
|
||||
},
|
||||
) => {
|
||||
onItemSelect(eventKey, !isChecked(checkedKeys, eventKey));
|
||||
onSelect={(_, { node: { key } }) => {
|
||||
onItemSelect(key, !isChecked(checkedKeys, key));
|
||||
}}
|
||||
>
|
||||
{generateTree(dataSource, targetKeys)}
|
||||
</Tree>
|
||||
/>
|
||||
);
|
||||
}
|
||||
}}
|
||||
@ -95,7 +78,10 @@ const treeData = [
|
||||
{
|
||||
key: '0-1',
|
||||
title: '0-1',
|
||||
children: [{ key: '0-1-0', title: '0-1-0' }, { key: '0-1-1', title: '0-1-1' }],
|
||||
children: [
|
||||
{ key: '0-1-0', title: '0-1-0' },
|
||||
{ key: '0-1-1', title: '0-1-1' },
|
||||
],
|
||||
},
|
||||
{ key: '0-2', title: '0-3' },
|
||||
];
|
||||
|
@ -20,7 +20,6 @@ One or more elements can be selected from either column, one click on the proper
|
||||
|
||||
| Property | Description | Type | Default | Version |
|
||||
| --- | --- | --- | --- | --- |
|
||||
| className | A custom CSS class. | string | \['', ''] | |
|
||||
| dataSource | Used for setting the source data. The elements that are part of this array will be present the left column. Except the elements whose keys are included in `targetKeys` prop. | [TransferItem](https://git.io/vMM64)\[] | \[] | |
|
||||
| disabled | Whether disabled transfer | boolean | false | |
|
||||
| filterOption | A function to determine whether an item should show in search result list | (inputValue, option): boolean | | |
|
||||
@ -28,12 +27,13 @@ One or more elements can be selected from either column, one click on the proper
|
||||
| listStyle | A custom CSS style used for rendering the transfer columns. | object\|({direction: 'left'\|'right'}) => object | | |
|
||||
| locale | i18n text including filter, empty text, item unit, etc | { itemUnit: string; itemsUnit: string; searchPlaceholder: string; notFoundContent: ReactNode; } | `{ itemUnit: 'item', itemsUnit: 'items', notFoundContent: 'The list is empty', searchPlaceholder: 'Search here' }` | |
|
||||
| operations | A set of operations that are sorted from top to bottom. | string\[] | \['>', '<'] | |
|
||||
| oneWay | Display as single direction style | boolean | false | 4.3.0 |
|
||||
| operationStyle | A custom CSS style used for rendering the operations column. | object | | |
|
||||
| pagination | Use pagination. Not work in render props | boolean \| { pageSize: number } | false | 4.3.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 React element which is generated from that record. Also, it can return a plain object with `value` and `label`, `label` is a React element and `value` is for title | (record) => ReactNode | | |
|
||||
| selectedKeys | 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 | |
|
||||
| style | A custom CSS style used for rendering wrapper element. | CSSProperties | | |
|
||||
| targetKeys | A set of keys of elements that are listed on the right column. | string\[] | \[] | |
|
||||
| titles | A set of titles that are sorted from left to right. | ReactNode\[] | - | |
|
||||
| selectAllLabels | A set of customized labels for select all checkboxs on the header | (ReactNode \| (info: { selectedCount: number, totalCount: number }) => ReactNode)[] | | |
|
||||
|
@ -6,7 +6,9 @@ import Search from './search';
|
||||
import LocaleReceiver from '../locale-provider/LocaleReceiver';
|
||||
import defaultLocale from '../locale/default';
|
||||
import { ConfigConsumer, ConfigConsumerProps, RenderEmptyHandler } from '../config-provider';
|
||||
import { TransferListBodyProps } from './renderListBody';
|
||||
import { TransferListBodyProps } from './ListBody';
|
||||
import { PaginationType } from './interface';
|
||||
import warning from '../_util/warning';
|
||||
|
||||
export { TransferListProps } from './list';
|
||||
export { TransferOperationProps } from './operation';
|
||||
@ -64,6 +66,8 @@ export interface TransferProps {
|
||||
children?: (props: TransferListBodyProps) => React.ReactNode;
|
||||
showSelectAll?: boolean;
|
||||
selectAllLabels?: SelectAllLabel[];
|
||||
oneWay?: boolean;
|
||||
pagination?: PaginationType;
|
||||
}
|
||||
|
||||
export interface TransferLocale {
|
||||
@ -72,9 +76,20 @@ export interface TransferLocale {
|
||||
searchPlaceholder: string;
|
||||
itemUnit: string;
|
||||
itemsUnit: string;
|
||||
remove: string;
|
||||
selectAll: string;
|
||||
selectCurrent: string;
|
||||
selectInvert: string;
|
||||
removeAll: string;
|
||||
removeCurrent: string;
|
||||
}
|
||||
|
||||
class Transfer extends React.Component<TransferProps, any> {
|
||||
interface TransferState {
|
||||
sourceSelectedKeys: string[];
|
||||
targetSelectedKeys: string[];
|
||||
}
|
||||
|
||||
class Transfer extends React.Component<TransferProps, TransferState> {
|
||||
// For high-level customized Transfer @dqaria
|
||||
static List = List;
|
||||
|
||||
@ -89,14 +104,26 @@ class Transfer extends React.Component<TransferProps, any> {
|
||||
listStyle: () => {},
|
||||
};
|
||||
|
||||
static getDerivedStateFromProps(nextProps: TransferProps) {
|
||||
if (nextProps.selectedKeys) {
|
||||
const targetKeys = nextProps.targetKeys || [];
|
||||
static getDerivedStateFromProps({
|
||||
selectedKeys,
|
||||
targetKeys,
|
||||
pagination,
|
||||
children,
|
||||
}: TransferProps) {
|
||||
if (selectedKeys) {
|
||||
const mergedTargetKeys = targetKeys || [];
|
||||
return {
|
||||
sourceSelectedKeys: nextProps.selectedKeys.filter(key => !targetKeys.includes(key)),
|
||||
targetSelectedKeys: nextProps.selectedKeys.filter(key => targetKeys.includes(key)),
|
||||
sourceSelectedKeys: selectedKeys.filter(key => !mergedTargetKeys.includes(key)),
|
||||
targetSelectedKeys: selectedKeys.filter(key => mergedTargetKeys.includes(key)),
|
||||
};
|
||||
}
|
||||
|
||||
warning(
|
||||
!pagination || !children,
|
||||
'Transfer',
|
||||
'`pagination` not support customize render list.',
|
||||
);
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
@ -115,10 +142,20 @@ class Transfer extends React.Component<TransferProps, any> {
|
||||
};
|
||||
}
|
||||
|
||||
// eslint-disable-next-line class-methods-use-this
|
||||
getSelectedKeysName(direction: TransferDirection) {
|
||||
return direction === 'left' ? 'sourceSelectedKeys' : 'targetSelectedKeys';
|
||||
}
|
||||
setStateKeys = (
|
||||
direction: TransferDirection,
|
||||
keys: string[] | ((prevKeys: string[]) => string[]),
|
||||
) => {
|
||||
if (direction === 'left') {
|
||||
this.setState(({ sourceSelectedKeys }) => ({
|
||||
sourceSelectedKeys: typeof keys === 'function' ? keys(sourceSelectedKeys || []) : keys,
|
||||
}));
|
||||
} else {
|
||||
this.setState(({ targetSelectedKeys }) => ({
|
||||
targetSelectedKeys: typeof keys === 'function' ? keys(targetSelectedKeys || []) : keys,
|
||||
}));
|
||||
}
|
||||
};
|
||||
|
||||
getTitles(transferLocale: TransferLocale): string[] {
|
||||
const { titles } = this.props;
|
||||
@ -148,9 +185,7 @@ class Transfer extends React.Component<TransferProps, any> {
|
||||
|
||||
// empty checked keys
|
||||
const oppositeDirection = direction === 'right' ? 'left' : 'right';
|
||||
this.setState({
|
||||
[this.getSelectedKeysName(oppositeDirection)]: [],
|
||||
});
|
||||
this.setStateKeys(oppositeDirection, []);
|
||||
this.handleSelectChange(oppositeDirection, []);
|
||||
|
||||
if (onChange) {
|
||||
@ -163,26 +198,20 @@ class Transfer extends React.Component<TransferProps, any> {
|
||||
moveToRight = () => this.moveTo('right');
|
||||
|
||||
onItemSelectAll = (direction: TransferDirection, selectedKeys: string[], checkAll: boolean) => {
|
||||
const originalSelectedKeys = this.state[this.getSelectedKeysName(direction)] || [];
|
||||
this.setStateKeys(direction, prevKeys => {
|
||||
let mergedCheckedKeys = [];
|
||||
if (checkAll) {
|
||||
// Merge current keys with origin key
|
||||
mergedCheckedKeys = Array.from(new Set([...prevKeys, ...selectedKeys]));
|
||||
} else {
|
||||
// Remove current keys from origin keys
|
||||
mergedCheckedKeys = prevKeys.filter((key: string) => selectedKeys.indexOf(key) === -1);
|
||||
}
|
||||
|
||||
let mergedCheckedKeys = [];
|
||||
if (checkAll) {
|
||||
// Merge current keys with origin key
|
||||
mergedCheckedKeys = Array.from(new Set([...originalSelectedKeys, ...selectedKeys]));
|
||||
} else {
|
||||
// Remove current keys from origin keys
|
||||
mergedCheckedKeys = originalSelectedKeys.filter(
|
||||
(key: string) => selectedKeys.indexOf(key) === -1,
|
||||
);
|
||||
}
|
||||
this.handleSelectChange(direction, mergedCheckedKeys);
|
||||
|
||||
this.handleSelectChange(direction, mergedCheckedKeys);
|
||||
|
||||
if (!this.props.selectedKeys) {
|
||||
this.setState({
|
||||
[this.getSelectedKeysName(direction)]: mergedCheckedKeys,
|
||||
});
|
||||
}
|
||||
return mergedCheckedKeys;
|
||||
});
|
||||
};
|
||||
|
||||
onLeftItemSelectAll = (selectedKeys: string[], checkAll: boolean) =>
|
||||
@ -227,9 +256,7 @@ class Transfer extends React.Component<TransferProps, any> {
|
||||
this.handleSelectChange(direction, holder);
|
||||
|
||||
if (!this.props.selectedKeys) {
|
||||
this.setState({
|
||||
[this.getSelectedKeysName(direction)]: holder,
|
||||
});
|
||||
this.setStateKeys(direction, holder);
|
||||
}
|
||||
};
|
||||
|
||||
@ -239,6 +266,20 @@ class Transfer extends React.Component<TransferProps, any> {
|
||||
onRightItemSelect = (selectedKey: string, checked: boolean) =>
|
||||
this.onItemSelect('right', selectedKey, checked);
|
||||
|
||||
onRightItemRemove = (selectedKeys: string[]) => {
|
||||
const { targetKeys = [], onChange } = this.props;
|
||||
|
||||
this.setStateKeys('right', []);
|
||||
|
||||
if (onChange) {
|
||||
onChange(
|
||||
targetKeys.filter(key => !selectedKeys.includes(key)),
|
||||
'left',
|
||||
[...selectedKeys],
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
handleScroll = (direction: TransferDirection, e: React.SyntheticEvent<HTMLUListElement>) => {
|
||||
const { onScroll } = this.props;
|
||||
if (onScroll) {
|
||||
@ -317,11 +358,15 @@ class Transfer extends React.Component<TransferProps, any> {
|
||||
render,
|
||||
children,
|
||||
showSelectAll,
|
||||
oneWay,
|
||||
pagination,
|
||||
} = this.props;
|
||||
const prefixCls = getPrefixCls('transfer', customizePrefixCls);
|
||||
const locale = this.getLocale(transferLocale, renderEmpty);
|
||||
const { sourceSelectedKeys, targetSelectedKeys } = this.state;
|
||||
|
||||
const mergedPagination = !children && pagination;
|
||||
|
||||
const { leftDataSource, rightDataSource } = this.separateDataSource();
|
||||
const leftActive = targetSelectedKeys.length > 0;
|
||||
const rightActive = sourceSelectedKeys.length > 0;
|
||||
@ -356,6 +401,7 @@ class Transfer extends React.Component<TransferProps, any> {
|
||||
direction="left"
|
||||
showSelectAll={showSelectAll}
|
||||
selectAllLabel={selectAllLabels[0]}
|
||||
pagination={mergedPagination}
|
||||
{...locale}
|
||||
/>
|
||||
<Operation
|
||||
@ -369,6 +415,7 @@ class Transfer extends React.Component<TransferProps, any> {
|
||||
style={operationStyle}
|
||||
disabled={disabled}
|
||||
direction={direction}
|
||||
oneWay={oneWay}
|
||||
/>
|
||||
<List
|
||||
prefixCls={`${prefixCls}-list`}
|
||||
@ -381,6 +428,7 @@ class Transfer extends React.Component<TransferProps, any> {
|
||||
handleClear={this.handleRightClear}
|
||||
onItemSelect={this.onRightItemSelect}
|
||||
onItemSelectAll={this.onRightItemSelectAll}
|
||||
onItemRemove={this.onRightItemRemove}
|
||||
render={render}
|
||||
showSearch={showSearch}
|
||||
renderList={children}
|
||||
@ -390,6 +438,8 @@ class Transfer extends React.Component<TransferProps, any> {
|
||||
direction="right"
|
||||
showSelectAll={showSelectAll}
|
||||
selectAllLabel={selectAllLabels[1]}
|
||||
showRemove={oneWay}
|
||||
pagination={mergedPagination}
|
||||
{...locale}
|
||||
/>
|
||||
</div>
|
||||
|
@ -23,19 +23,19 @@ title: Transfer
|
||||
|
||||
| 参数 | 说明 | 类型 | 默认值 | 版本 |
|
||||
| --- | --- | --- | --- | --- |
|
||||
| className | 自定义类 | string | | |
|
||||
| dataSource | 数据源,其中的数据将会被渲染到左边一栏中,`targetKeys` 中指定的除外。 | [TransferItem](https://git.io/vMM64)\[] | \[] | |
|
||||
| disabled | 是否禁用 | boolean | false | |
|
||||
| filterOption | 接收 `inputValue` `option` 两个参数,当 `option` 符合筛选条件时,应返回 `true`,反之则返回 `false`。 | (inputValue, option): boolean | | |
|
||||
| footer | 底部渲染函数 | (props) => ReactNode | | |
|
||||
| listStyle | 两个穿梭框的自定义样式 | object\|({direction: 'left'\|'right'}) => object | | |
|
||||
| locale | 各种语言 | { itemUnit: string; itemsUnit: string; searchPlaceholder: string; notFoundContent: ReactNode; } | `{ itemUnit: '项', itemsUnit: '项', searchPlaceholder: '请输入搜索内容' }` | |
|
||||
| oneWay | 展示为单向样式 | boolean | false | 4.3.0 |
|
||||
| operations | 操作文案集合,顺序从上至下 | string\[] | \['>', '<'] | |
|
||||
| pagination | 使用分页样式,自定义渲染列表下无效 | boolean \| { pageSize: number } | false | 4.3.0 |
|
||||
| render | 每行数据渲染函数,该函数的入参为 `dataSource` 中的项,返回值为 ReactElement。或者返回一个普通对象,其中 `label` 字段为 ReactElement,`value` 字段为 title | (record) => ReactNode | | |
|
||||
| selectedKeys | 设置哪些项应该被选中 | string\[] | \[] | |
|
||||
| showSearch | 是否显示搜索框 | boolean | false | |
|
||||
| showSelectAll | 是否展示全选勾选框 | boolean | true | |
|
||||
| style | 容器的自定义样式 | CSSProperties | | |
|
||||
| targetKeys | 显示在右侧框数据的 key 集合 | string\[] | \[] | |
|
||||
| titles | 标题集合,顺序从左至右 | ReactNode\[] | \['', ''] | |
|
||||
| selectAllLabels | 自定义顶部多选框标题的集合 | (ReactNode \| (info: { selectedCount: number, totalCount: number }) => ReactNode)[] | | |
|
||||
|
5
components/transfer/interface.ts
Normal file
5
components/transfer/interface.ts
Normal file
@ -0,0 +1,5 @@
|
||||
export type PaginationType =
|
||||
| boolean
|
||||
| {
|
||||
pageSize?: number;
|
||||
};
|
@ -1,16 +1,21 @@
|
||||
import * as React from 'react';
|
||||
import omit from 'omit.js';
|
||||
import classNames from 'classnames';
|
||||
import { DownOutlined } from '@ant-design/icons';
|
||||
import Checkbox from '../checkbox';
|
||||
import Menu from '../menu';
|
||||
import Dropdown from '../dropdown';
|
||||
import {
|
||||
TransferItem,
|
||||
TransferDirection,
|
||||
RenderResult,
|
||||
RenderResultObject,
|
||||
SelectAllLabel,
|
||||
TransferLocale,
|
||||
} from './index';
|
||||
import Search from './search';
|
||||
import defaultRenderList, { TransferListBodyProps, OmitProps } from './renderListBody';
|
||||
import DefaultListBody, { TransferListBodyProps, OmitProps } from './ListBody';
|
||||
import { PaginationType } from './interface';
|
||||
|
||||
const defaultRender = () => null;
|
||||
|
||||
@ -22,6 +27,10 @@ function isRenderResultPlainObject(result: RenderResult) {
|
||||
);
|
||||
}
|
||||
|
||||
function getEnabledItemKeys(items: TransferItem[]) {
|
||||
return items.filter(data => !data.disabled).map(data => data.key);
|
||||
}
|
||||
|
||||
export interface RenderedItem {
|
||||
renderedText: string;
|
||||
renderedEl: React.ReactNode;
|
||||
@ -30,7 +39,7 @@ export interface RenderedItem {
|
||||
|
||||
type RenderListFunction = (props: TransferListBodyProps) => React.ReactNode;
|
||||
|
||||
export interface TransferListProps {
|
||||
export interface TransferListProps extends TransferLocale {
|
||||
prefixCls: string;
|
||||
titleText: string;
|
||||
dataSource: TransferItem[];
|
||||
@ -40,11 +49,12 @@ export interface TransferListProps {
|
||||
handleFilter: (e: React.ChangeEvent<HTMLInputElement>) => void;
|
||||
onItemSelect: (key: string, check: boolean) => void;
|
||||
onItemSelectAll: (dataSource: string[], checkAll: boolean) => void;
|
||||
onItemRemove?: (keys: string[]) => void;
|
||||
handleClear: () => void;
|
||||
/** render item */
|
||||
render?: (item: TransferItem) => RenderResult;
|
||||
showSearch?: boolean;
|
||||
searchPlaceholder: string;
|
||||
notFoundContent: React.ReactNode;
|
||||
itemUnit: string;
|
||||
itemsUnit: string;
|
||||
renderList?: RenderListFunction;
|
||||
@ -54,6 +64,8 @@ export interface TransferListProps {
|
||||
direction: TransferDirection;
|
||||
showSelectAll?: boolean;
|
||||
selectAllLabel?: SelectAllLabel;
|
||||
showRemove?: boolean;
|
||||
pagination?: PaginationType;
|
||||
}
|
||||
|
||||
interface TransferListState {
|
||||
@ -61,18 +73,6 @@ interface TransferListState {
|
||||
filterValue: string;
|
||||
}
|
||||
|
||||
function renderListNode(renderList: RenderListFunction | undefined, props: TransferListBodyProps) {
|
||||
let bodyContent: React.ReactNode = renderList ? renderList(props) : null;
|
||||
const customize: boolean = !!bodyContent;
|
||||
if (!customize) {
|
||||
bodyContent = defaultRenderList(props);
|
||||
}
|
||||
return {
|
||||
customize,
|
||||
bodyContent,
|
||||
};
|
||||
}
|
||||
|
||||
export default class TransferList extends React.PureComponent<
|
||||
TransferListProps,
|
||||
TransferListState
|
||||
@ -87,6 +87,8 @@ export default class TransferList extends React.PureComponent<
|
||||
|
||||
triggerScrollTimer: number;
|
||||
|
||||
defaultListBodyRef = React.createRef<DefaultListBody>();
|
||||
|
||||
constructor(props: TransferListProps) {
|
||||
super(props);
|
||||
this.state = {
|
||||
@ -109,6 +111,7 @@ export default class TransferList extends React.PureComponent<
|
||||
return 'part';
|
||||
}
|
||||
|
||||
// ================================ Item ================================
|
||||
getFilteredItems(
|
||||
dataSource: TransferItem[],
|
||||
filterValue: string,
|
||||
@ -132,6 +135,46 @@ export default class TransferList extends React.PureComponent<
|
||||
return { filteredItems, filteredRenderItems };
|
||||
}
|
||||
|
||||
// =============================== Filter ===============================
|
||||
handleFilter = (e: React.ChangeEvent<HTMLInputElement>) => {
|
||||
const { handleFilter } = this.props;
|
||||
const {
|
||||
target: { value: filterValue },
|
||||
} = e;
|
||||
this.setState({ filterValue });
|
||||
handleFilter(e);
|
||||
};
|
||||
|
||||
handleClear = () => {
|
||||
const { handleClear } = this.props;
|
||||
this.setState({ filterValue: '' });
|
||||
handleClear();
|
||||
};
|
||||
|
||||
matchFilter = (text: string, item: TransferItem) => {
|
||||
const { filterValue } = this.state;
|
||||
const { filterOption } = this.props;
|
||||
if (filterOption) {
|
||||
return filterOption(filterValue, item);
|
||||
}
|
||||
return text.indexOf(filterValue) >= 0;
|
||||
};
|
||||
|
||||
getCurrentPageItems = () => {};
|
||||
|
||||
// =============================== Render ===============================
|
||||
renderListBody = (renderList: RenderListFunction | undefined, props: TransferListBodyProps) => {
|
||||
let bodyContent: React.ReactNode = renderList ? renderList(props) : null;
|
||||
const customize: boolean = !!bodyContent;
|
||||
if (!customize) {
|
||||
bodyContent = <DefaultListBody ref={this.defaultListBodyRef} {...props} />;
|
||||
}
|
||||
return {
|
||||
customize,
|
||||
bodyContent,
|
||||
};
|
||||
};
|
||||
|
||||
getListBody(
|
||||
prefixCls: string,
|
||||
searchPlaceholder: string,
|
||||
@ -157,7 +200,7 @@ export default class TransferList extends React.PureComponent<
|
||||
</div>
|
||||
) : null;
|
||||
|
||||
const { bodyContent, customize } = renderListNode(renderList, {
|
||||
const { bodyContent, customize } = this.renderListBody(renderList, {
|
||||
...omit(this.props, OmitProps),
|
||||
filteredItems,
|
||||
filteredRenderItems,
|
||||
@ -214,30 +257,6 @@ export default class TransferList extends React.PureComponent<
|
||||
return checkAllCheckbox;
|
||||
}
|
||||
|
||||
handleFilter = (e: React.ChangeEvent<HTMLInputElement>) => {
|
||||
const { handleFilter } = this.props;
|
||||
const {
|
||||
target: { value: filterValue },
|
||||
} = e;
|
||||
this.setState({ filterValue });
|
||||
handleFilter(e);
|
||||
};
|
||||
|
||||
handleClear = () => {
|
||||
const { handleClear } = this.props;
|
||||
this.setState({ filterValue: '' });
|
||||
handleClear();
|
||||
};
|
||||
|
||||
matchFilter = (text: string, item: TransferItem) => {
|
||||
const { filterValue } = this.state;
|
||||
const { filterOption } = this.props;
|
||||
if (filterOption) {
|
||||
return filterOption(filterValue, item);
|
||||
}
|
||||
return text.indexOf(filterValue) >= 0;
|
||||
};
|
||||
|
||||
renderItem = (item: TransferItem): RenderedItem => {
|
||||
const { render = defaultRender } = this.props;
|
||||
const renderResult: RenderResult = render(item);
|
||||
@ -279,16 +298,25 @@ export default class TransferList extends React.PureComponent<
|
||||
style,
|
||||
searchPlaceholder,
|
||||
notFoundContent,
|
||||
selectAll,
|
||||
selectCurrent,
|
||||
selectInvert,
|
||||
removeAll,
|
||||
removeCurrent,
|
||||
renderList,
|
||||
onItemSelectAll,
|
||||
onItemRemove,
|
||||
showSelectAll,
|
||||
showRemove,
|
||||
pagination,
|
||||
} = this.props;
|
||||
|
||||
// Custom Layout
|
||||
const footerDom = footer && footer(this.props);
|
||||
|
||||
const listCls = classNames(prefixCls, {
|
||||
[`${prefixCls}-with-footer`]: !!footerDom,
|
||||
[`${prefixCls}-with-pagination`]: pagination,
|
||||
[`${prefixCls}-with-footer`]: footerDom,
|
||||
});
|
||||
|
||||
// ====================== Get filtered, checked item list ======================
|
||||
@ -313,11 +341,97 @@ export default class TransferList extends React.PureComponent<
|
||||
// ================================ List Footer ================================
|
||||
const listFooter = footerDom ? <div className={`${prefixCls}-footer`}>{footerDom}</div> : null;
|
||||
|
||||
const checkAllCheckbox = this.getCheckBox(
|
||||
filteredItems,
|
||||
onItemSelectAll,
|
||||
showSelectAll,
|
||||
disabled,
|
||||
const checkAllCheckbox =
|
||||
!showRemove &&
|
||||
!pagination &&
|
||||
this.getCheckBox(filteredItems, onItemSelectAll, showSelectAll, disabled);
|
||||
|
||||
let menu: React.ReactElement | null = null;
|
||||
if (showRemove) {
|
||||
menu = (
|
||||
<Menu>
|
||||
{/* Remove Current Page */}
|
||||
{pagination && (
|
||||
<Menu.Item
|
||||
onClick={() => {
|
||||
const pageKeys = getEnabledItemKeys(
|
||||
(this.defaultListBodyRef.current?.getItems() || []).map(entity => entity.item),
|
||||
);
|
||||
onItemRemove?.(pageKeys);
|
||||
}}
|
||||
>
|
||||
{removeCurrent}
|
||||
</Menu.Item>
|
||||
)}
|
||||
|
||||
{/* Remove All */}
|
||||
<Menu.Item
|
||||
onClick={() => {
|
||||
onItemRemove?.(getEnabledItemKeys(filteredItems));
|
||||
}}
|
||||
>
|
||||
{removeAll}
|
||||
</Menu.Item>
|
||||
</Menu>
|
||||
);
|
||||
} else {
|
||||
menu = (
|
||||
<Menu>
|
||||
<Menu.Item
|
||||
onClick={() => {
|
||||
const keys = getEnabledItemKeys(filteredItems);
|
||||
onItemSelectAll(keys, keys.length !== checkedKeys.length);
|
||||
}}
|
||||
>
|
||||
{selectAll}
|
||||
</Menu.Item>
|
||||
{pagination && (
|
||||
<Menu.Item
|
||||
onClick={() => {
|
||||
const pageItems = this.defaultListBodyRef.current?.getItems() || [];
|
||||
onItemSelectAll(getEnabledItemKeys(pageItems.map(entity => entity.item)), true);
|
||||
}}
|
||||
>
|
||||
{selectCurrent}
|
||||
</Menu.Item>
|
||||
)}
|
||||
<Menu.Item
|
||||
onClick={() => {
|
||||
let availableKeys: string[];
|
||||
if (pagination) {
|
||||
availableKeys = getEnabledItemKeys(
|
||||
(this.defaultListBodyRef.current?.getItems() || []).map(entity => entity.item),
|
||||
);
|
||||
} else {
|
||||
availableKeys = getEnabledItemKeys(filteredItems);
|
||||
}
|
||||
|
||||
const checkedKeySet = new Set(checkedKeys);
|
||||
const newCheckedKeys: string[] = [];
|
||||
const newUnCheckedKeys: string[] = [];
|
||||
|
||||
availableKeys.forEach(key => {
|
||||
if (checkedKeySet.has(key)) {
|
||||
newUnCheckedKeys.push(key);
|
||||
} else {
|
||||
newCheckedKeys.push(key);
|
||||
}
|
||||
});
|
||||
|
||||
onItemSelectAll(newCheckedKeys, true);
|
||||
onItemSelectAll(newUnCheckedKeys, false);
|
||||
}}
|
||||
>
|
||||
{selectInvert}
|
||||
</Menu.Item>
|
||||
</Menu>
|
||||
);
|
||||
}
|
||||
|
||||
const dropdown = (
|
||||
<Dropdown className={`${prefixCls}-header-dropdown`} overlay={menu} disabled={disabled}>
|
||||
<DownOutlined />
|
||||
</Dropdown>
|
||||
);
|
||||
|
||||
// ================================== Render ===================================
|
||||
@ -326,10 +440,12 @@ export default class TransferList extends React.PureComponent<
|
||||
{/* Header */}
|
||||
<div className={`${prefixCls}-header`}>
|
||||
{checkAllCheckbox}
|
||||
{dropdown}
|
||||
<span className={`${prefixCls}-header-selected`}>
|
||||
<span>{this.getSelectAllLabel(checkedKeys.length, filteredItems.length)}</span>
|
||||
<span className={`${prefixCls}-header-title`}>{titleText}</span>
|
||||
{this.getSelectAllLabel(checkedKeys.length, filteredItems.length)}
|
||||
</span>
|
||||
|
||||
<span className={`${prefixCls}-header-title`}>{titleText}</span>
|
||||
</div>
|
||||
|
||||
{/* Body */}
|
||||
|
@ -14,6 +14,7 @@ export interface TransferOperationProps {
|
||||
style?: React.CSSProperties;
|
||||
disabled?: boolean;
|
||||
direction?: 'ltr' | 'rtl';
|
||||
oneWay?: boolean;
|
||||
}
|
||||
|
||||
const Operation = ({
|
||||
@ -27,6 +28,7 @@ const Operation = ({
|
||||
className,
|
||||
style,
|
||||
direction,
|
||||
oneWay,
|
||||
}: TransferOperationProps) => (
|
||||
<div className={className} style={style}>
|
||||
<Button
|
||||
@ -38,15 +40,17 @@ const Operation = ({
|
||||
>
|
||||
{rightArrowText}
|
||||
</Button>
|
||||
<Button
|
||||
type="primary"
|
||||
size="small"
|
||||
disabled={disabled || !leftActive}
|
||||
onClick={moveToLeft}
|
||||
icon={direction !== 'rtl' ? <LeftOutlined /> : <RightOutlined />}
|
||||
>
|
||||
{leftArrowText}
|
||||
</Button>
|
||||
{!oneWay && (
|
||||
<Button
|
||||
type="primary"
|
||||
size="small"
|
||||
disabled={disabled || !leftActive}
|
||||
onClick={moveToLeft}
|
||||
icon={direction !== 'rtl' ? <LeftOutlined /> : <RightOutlined />}
|
||||
>
|
||||
{leftArrowText}
|
||||
</Button>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
|
||||
|
@ -1,59 +0,0 @@
|
||||
import * as React from 'react';
|
||||
import { ElementOf, Omit, tuple } from '../_util/type';
|
||||
import { TransferItem } from '.';
|
||||
import { TransferListProps, RenderedItem } from './list';
|
||||
import ListItem from './ListItem';
|
||||
|
||||
export const OmitProps = tuple('handleFilter', 'handleClear', 'checkedKeys');
|
||||
export type OmitProp = ElementOf<typeof OmitProps>;
|
||||
type PartialTransferListProps = Omit<TransferListProps, OmitProp>;
|
||||
|
||||
export interface TransferListBodyProps extends PartialTransferListProps {
|
||||
filteredItems: TransferItem[];
|
||||
filteredRenderItems: RenderedItem[];
|
||||
selectedKeys: string[];
|
||||
}
|
||||
|
||||
class ListBody extends React.Component<TransferListBodyProps> {
|
||||
onItemSelect = (item: TransferItem) => {
|
||||
const { onItemSelect, selectedKeys } = this.props;
|
||||
const checked = selectedKeys.indexOf(item.key) >= 0;
|
||||
onItemSelect(item.key, !checked);
|
||||
};
|
||||
|
||||
render() {
|
||||
const {
|
||||
prefixCls,
|
||||
onScroll,
|
||||
filteredRenderItems,
|
||||
selectedKeys,
|
||||
disabled: globalDisabled,
|
||||
} = this.props;
|
||||
|
||||
return (
|
||||
<ul className={`${prefixCls}-content`} onScroll={onScroll}>
|
||||
{filteredRenderItems.map(({ renderedEl, renderedText, item }: RenderedItem) => {
|
||||
const { disabled } = item;
|
||||
const checked = selectedKeys.indexOf(item.key) >= 0;
|
||||
|
||||
return (
|
||||
<ListItem
|
||||
disabled={globalDisabled || disabled}
|
||||
key={item.key}
|
||||
item={item}
|
||||
renderedText={renderedText}
|
||||
renderedEl={renderedEl}
|
||||
checked={checked}
|
||||
prefixCls={prefixCls}
|
||||
onClick={this.onItemSelect}
|
||||
/>
|
||||
);
|
||||
})}
|
||||
</ul>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
const ListBodyWrapper = (props: TransferListBodyProps) => <ListBody {...props} />;
|
||||
|
||||
export default ListBodyWrapper;
|
@ -4,34 +4,11 @@
|
||||
@input-prefix-cls: ~'@{ant-prefix}-input';
|
||||
|
||||
.@{transfer-prefix-cls}-customize-list {
|
||||
display: flex;
|
||||
|
||||
.@{transfer-prefix-cls}-operation {
|
||||
flex: none;
|
||||
align-self: center;
|
||||
}
|
||||
|
||||
.@{transfer-prefix-cls}-list {
|
||||
flex: auto;
|
||||
flex: 1 1 50%;
|
||||
width: auto;
|
||||
height: auto;
|
||||
min-height: @transfer-list-height;
|
||||
|
||||
&-body {
|
||||
&-with-search {
|
||||
padding-top: 0;
|
||||
}
|
||||
|
||||
// Search box in customize mode do not need fix top
|
||||
&-search-wrapper {
|
||||
position: relative;
|
||||
padding-bottom: 0;
|
||||
}
|
||||
|
||||
&-customize-wrapper {
|
||||
padding: 12px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// =================== Hook Components ===================
|
||||
|
@ -13,6 +13,8 @@
|
||||
.reset-component;
|
||||
|
||||
position: relative;
|
||||
display: flex;
|
||||
align-items: stretch;
|
||||
|
||||
&-disabled {
|
||||
.@{transfer-prefix-cls}-list {
|
||||
@ -21,17 +23,16 @@
|
||||
}
|
||||
|
||||
&-list {
|
||||
position: relative;
|
||||
display: inline-block;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
width: 180px;
|
||||
height: @transfer-list-height;
|
||||
padding-top: @transfer-header-height;
|
||||
vertical-align: middle;
|
||||
border: @border-width-base @border-style-base @border-color-base;
|
||||
border-radius: @border-radius-base;
|
||||
|
||||
&-with-footer {
|
||||
padding-bottom: 34px;
|
||||
&-with-pagination {
|
||||
width: 250px;
|
||||
height: auto;
|
||||
}
|
||||
|
||||
&-search {
|
||||
@ -61,72 +62,120 @@
|
||||
}
|
||||
|
||||
&-header {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
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;
|
||||
overflow: hidden;
|
||||
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;
|
||||
|
||||
&-title {
|
||||
position: absolute;
|
||||
right: 12px;
|
||||
> *:not(:last-child) {
|
||||
margin-right: 4px;
|
||||
}
|
||||
|
||||
.@{ant-prefix}-checkbox-wrapper + span {
|
||||
padding-left: 8px;
|
||||
> * {
|
||||
flex: none;
|
||||
}
|
||||
|
||||
&-title {
|
||||
flex: auto;
|
||||
overflow: hidden;
|
||||
white-space: nowrap;
|
||||
text-align: right;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
|
||||
&-dropdown {
|
||||
transform: translateY(10%);
|
||||
cursor: pointer;
|
||||
.iconfont-size-under-12px(10px);
|
||||
}
|
||||
}
|
||||
|
||||
&-body {
|
||||
position: relative;
|
||||
height: 100%;
|
||||
display: flex;
|
||||
flex: auto;
|
||||
flex-direction: column;
|
||||
overflow: hidden;
|
||||
font-size: @font-size-base;
|
||||
|
||||
&-search-wrapper {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
position: relative;
|
||||
flex: none;
|
||||
padding: @padding-sm;
|
||||
}
|
||||
}
|
||||
|
||||
&-body-with-search {
|
||||
padding-top: @input-height-base + @padding-sm * 2;
|
||||
}
|
||||
|
||||
&-content {
|
||||
height: 100%;
|
||||
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;
|
||||
overflow: hidden;
|
||||
line-height: @transfer-item-height - 2 * @transfer-item-padding-vertical;
|
||||
white-space: nowrap;
|
||||
text-overflow: ellipsis;
|
||||
transition: all 0.3s;
|
||||
> span {
|
||||
padding-right: 0;
|
||||
|
||||
> *:not(:last-child) {
|
||||
margin-right: 8px;
|
||||
}
|
||||
|
||||
> * {
|
||||
flex: none;
|
||||
}
|
||||
|
||||
&-text {
|
||||
padding-left: 8px;
|
||||
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;
|
||||
&-item:not(&-item-disabled) {
|
||||
&:hover {
|
||||
background-color: @transfer-item-hover-bg;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
&.@{transfer-prefix-cls}-list-content-item-checked:hover {
|
||||
background-color: darken(@item-active-bg, 2%);
|
||||
}
|
||||
}
|
||||
|
||||
// Do not change hover style when `oneWay` mode
|
||||
&-show-remove &-item:not(&-item-disabled):hover {
|
||||
background: transparent;
|
||||
cursor: default;
|
||||
}
|
||||
|
||||
&-item-checked {
|
||||
@ -139,33 +188,30 @@
|
||||
}
|
||||
}
|
||||
|
||||
&-pagination {
|
||||
flex: none;
|
||||
align-self: flex-end;
|
||||
padding: @padding-xs 0;
|
||||
}
|
||||
|
||||
&-body-not-found {
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
flex: none;
|
||||
width: 100%;
|
||||
padding-top: 0;
|
||||
margin: auto 0;
|
||||
color: @disabled-color;
|
||||
text-align: center;
|
||||
transform: translateY(-50%);
|
||||
|
||||
// with filter should offset the search box height
|
||||
.@{transfer-prefix-cls}-list-body-with-search & {
|
||||
margin-top: @input-height-base / 2;
|
||||
}
|
||||
}
|
||||
|
||||
&-footer {
|
||||
position: absolute;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
border-top: @border-width-base @border-style-base @border-color-split;
|
||||
border-radius: 0 0 @border-radius-base @border-radius-base;
|
||||
}
|
||||
}
|
||||
|
||||
&-operation {
|
||||
display: inline-block;
|
||||
display: flex;
|
||||
flex: none;
|
||||
flex-direction: column;
|
||||
align-self: center;
|
||||
margin: 0 8px;
|
||||
overflow: hidden;
|
||||
vertical-align: middle;
|
||||
|
@ -6,3 +6,6 @@ import '../../empty/style';
|
||||
import '../../checkbox/style';
|
||||
import '../../button/style';
|
||||
import '../../input/style';
|
||||
import '../../menu/style';
|
||||
import '../../dropdown/style';
|
||||
import '../../pagination/style';
|
||||
|
Loading…
Reference in New Issue
Block a user