mirror of
https://github.com/ant-design/ant-design.git
synced 2024-12-04 00:49:39 +08:00
commit
33533ff22c
@ -6,13 +6,14 @@ import { composeRef, supportRef } from 'rc-util/lib/ref';
|
|||||||
import type { ConfigConsumerProps } from '../../config-provider';
|
import type { ConfigConsumerProps } from '../../config-provider';
|
||||||
import { ConfigContext } from '../../config-provider';
|
import { ConfigContext } from '../../config-provider';
|
||||||
import { cloneElement } from '../reactNode';
|
import { cloneElement } from '../reactNode';
|
||||||
|
import type { WaveComponent } from './interface';
|
||||||
import useStyle from './style';
|
import useStyle from './style';
|
||||||
import useWave from './useWave';
|
import useWave from './useWave';
|
||||||
|
|
||||||
export interface WaveProps {
|
export interface WaveProps {
|
||||||
disabled?: boolean;
|
disabled?: boolean;
|
||||||
children?: React.ReactNode;
|
children?: React.ReactNode;
|
||||||
component?: 'Tag' | 'Button' | 'Checkbox' | 'Radio' | 'Switch';
|
component?: WaveComponent;
|
||||||
}
|
}
|
||||||
|
|
||||||
const Wave: React.FC<WaveProps> = (props) => {
|
const Wave: React.FC<WaveProps> = (props) => {
|
||||||
|
@ -8,10 +8,12 @@ export type ShowWaveEffect = (
|
|||||||
info: {
|
info: {
|
||||||
className: string;
|
className: string;
|
||||||
token: GlobalToken;
|
token: GlobalToken;
|
||||||
component?: string;
|
component?: WaveComponent;
|
||||||
event: MouseEvent;
|
event: MouseEvent;
|
||||||
hashId: string;
|
hashId: string;
|
||||||
},
|
},
|
||||||
) => void;
|
) => void;
|
||||||
|
|
||||||
export type ShowWave = (event: MouseEvent) => void;
|
export type ShowWave = (event: MouseEvent) => void;
|
||||||
|
|
||||||
|
export type WaveComponent = 'Tag' | 'Button' | 'Checkbox' | 'Radio' | 'Switch';
|
||||||
|
@ -5,13 +5,13 @@ import raf from 'rc-util/lib/raf';
|
|||||||
import { ConfigContext } from '../../config-provider';
|
import { ConfigContext } from '../../config-provider';
|
||||||
import useToken from '../../theme/useToken';
|
import useToken from '../../theme/useToken';
|
||||||
import { TARGET_CLS } from './interface';
|
import { TARGET_CLS } from './interface';
|
||||||
import type { ShowWave } from './interface';
|
import type { ShowWave, WaveComponent } from './interface';
|
||||||
import showWaveEffect from './WaveEffect';
|
import showWaveEffect from './WaveEffect';
|
||||||
|
|
||||||
const useWave = (
|
const useWave = (
|
||||||
nodeRef: React.RefObject<HTMLElement>,
|
nodeRef: React.RefObject<HTMLElement>,
|
||||||
className: string,
|
className: string,
|
||||||
component?: 'Tag' | 'Button' | 'Checkbox' | 'Radio' | 'Switch',
|
component?: WaveComponent,
|
||||||
) => {
|
) => {
|
||||||
const { wave } = React.useContext(ConfigContext);
|
const { wave } = React.useContext(ConfigContext);
|
||||||
const [, token, hashId] = useToken();
|
const [, token, hashId] = useToken();
|
||||||
|
@ -145,7 +145,6 @@ const genSharedBadgeStyle: GenerateStyle<BadgeToken> = (token) => {
|
|||||||
iconCls,
|
iconCls,
|
||||||
antCls,
|
antCls,
|
||||||
badgeShadowSize,
|
badgeShadowSize,
|
||||||
motionDurationSlow,
|
|
||||||
textFontSize,
|
textFontSize,
|
||||||
textFontSizeSM,
|
textFontSizeSM,
|
||||||
statusSize,
|
statusSize,
|
||||||
@ -231,9 +230,6 @@ const genSharedBadgeStyle: GenerateStyle<BadgeToken> = (token) => {
|
|||||||
borderRadius: '100%',
|
borderRadius: '100%',
|
||||||
boxShadow: `0 0 0 ${unit(badgeShadowSize)} ${token.badgeShadowColor}`,
|
boxShadow: `0 0 0 ${unit(badgeShadowSize)} ${token.badgeShadowColor}`,
|
||||||
},
|
},
|
||||||
[`${componentCls}-dot${numberPrefixCls}`]: {
|
|
||||||
transition: `background ${motionDurationSlow}`,
|
|
||||||
},
|
|
||||||
[`${componentCls}-count, ${componentCls}-dot, ${numberPrefixCls}-custom-component`]: {
|
[`${componentCls}-count, ${componentCls}-dot, ${numberPrefixCls}-custom-component`]: {
|
||||||
position: 'absolute',
|
position: 'absolute',
|
||||||
top: 0,
|
top: 0,
|
||||||
|
@ -227,7 +227,6 @@ const genBaseStyle: GenerateStyle<DropdownToken> = (token) => {
|
|||||||
position: 'relative',
|
position: 'relative',
|
||||||
display: 'flex',
|
display: 'flex',
|
||||||
alignItems: 'center',
|
alignItems: 'center',
|
||||||
whiteSpace: 'nowrap',
|
|
||||||
},
|
},
|
||||||
|
|
||||||
[`${menuCls}-item-icon`]: {
|
[`${menuCls}-item-icon`]: {
|
||||||
|
@ -686,8 +686,6 @@ const genSearchInputStyle: GenerateStyle<InputToken> = (token: InputToken) => {
|
|||||||
paddingTop: 0,
|
paddingTop: 0,
|
||||||
paddingBottom: 0,
|
paddingBottom: 0,
|
||||||
borderStartStartRadius: 0,
|
borderStartStartRadius: 0,
|
||||||
borderStartEndRadius: token.borderRadius,
|
|
||||||
borderEndEndRadius: token.borderRadius,
|
|
||||||
borderEndStartRadius: 0,
|
borderEndStartRadius: 0,
|
||||||
boxShadow: 'none',
|
boxShadow: 'none',
|
||||||
},
|
},
|
||||||
|
@ -7,29 +7,18 @@ import { ConfigContext } from '../config-provider';
|
|||||||
import useCSSVarCls from '../config-provider/hooks/useCSSVarCls';
|
import useCSSVarCls from '../config-provider/hooks/useCSSVarCls';
|
||||||
import useSize from '../config-provider/hooks/useSize';
|
import useSize from '../config-provider/hooks/useSize';
|
||||||
import { RadioGroupContextProvider } from './context';
|
import { RadioGroupContextProvider } from './context';
|
||||||
import type { RadioChangeEvent, RadioGroupButtonStyle, RadioGroupProps } from './interface';
|
import type {
|
||||||
|
RadioChangeEvent,
|
||||||
|
RadioGroupButtonStyle,
|
||||||
|
RadioGroupContextProps,
|
||||||
|
RadioGroupProps,
|
||||||
|
} from './interface';
|
||||||
import Radio from './radio';
|
import Radio from './radio';
|
||||||
import useStyle from './style';
|
import useStyle from './style';
|
||||||
|
|
||||||
const RadioGroup = React.forwardRef<HTMLDivElement, RadioGroupProps>((props, ref) => {
|
const RadioGroup = React.forwardRef<HTMLDivElement, RadioGroupProps>((props, ref) => {
|
||||||
const { getPrefixCls, direction } = React.useContext(ConfigContext);
|
const { getPrefixCls, direction } = React.useContext(ConfigContext);
|
||||||
|
|
||||||
const [value, setValue] = useMergedState(props.defaultValue, {
|
|
||||||
value: props.value,
|
|
||||||
});
|
|
||||||
|
|
||||||
const onRadioChange = (ev: RadioChangeEvent) => {
|
|
||||||
const lastValue = value;
|
|
||||||
const val = ev.target.value;
|
|
||||||
if (!('value' in props)) {
|
|
||||||
setValue(val);
|
|
||||||
}
|
|
||||||
const { onChange } = props;
|
|
||||||
if (onChange && val !== lastValue) {
|
|
||||||
onChange(ev);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const {
|
const {
|
||||||
prefixCls: customizePrefixCls,
|
prefixCls: customizePrefixCls,
|
||||||
className,
|
className,
|
||||||
@ -41,11 +30,35 @@ const RadioGroup = React.forwardRef<HTMLDivElement, RadioGroupProps>((props, ref
|
|||||||
size: customizeSize,
|
size: customizeSize,
|
||||||
style,
|
style,
|
||||||
id,
|
id,
|
||||||
|
optionType,
|
||||||
|
name,
|
||||||
|
defaultValue,
|
||||||
|
value: customizedValue,
|
||||||
|
onChange,
|
||||||
onMouseEnter,
|
onMouseEnter,
|
||||||
onMouseLeave,
|
onMouseLeave,
|
||||||
onFocus,
|
onFocus,
|
||||||
onBlur,
|
onBlur,
|
||||||
} = props;
|
} = props;
|
||||||
|
|
||||||
|
const [value, setValue] = useMergedState(defaultValue, {
|
||||||
|
value: customizedValue,
|
||||||
|
});
|
||||||
|
|
||||||
|
const onRadioChange = React.useCallback(
|
||||||
|
(event: RadioChangeEvent) => {
|
||||||
|
const lastValue = value;
|
||||||
|
const val = event.target.value;
|
||||||
|
if (!('value' in props)) {
|
||||||
|
setValue(val);
|
||||||
|
}
|
||||||
|
if (val !== lastValue) {
|
||||||
|
onChange?.(event);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
[value, setValue, onChange],
|
||||||
|
);
|
||||||
|
|
||||||
const prefixCls = getPrefixCls('radio', customizePrefixCls);
|
const prefixCls = getPrefixCls('radio', customizePrefixCls);
|
||||||
const groupPrefixCls = `${prefixCls}-group`;
|
const groupPrefixCls = `${prefixCls}-group`;
|
||||||
|
|
||||||
@ -105,6 +118,12 @@ const RadioGroup = React.forwardRef<HTMLDivElement, RadioGroupProps>((props, ref
|
|||||||
cssVarCls,
|
cssVarCls,
|
||||||
rootCls,
|
rootCls,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const memoizedValue = React.useMemo<RadioGroupContextProps>(
|
||||||
|
() => ({ onChange: onRadioChange, value, disabled, name, optionType }),
|
||||||
|
[onRadioChange, value, disabled, name, optionType],
|
||||||
|
);
|
||||||
|
|
||||||
return wrapCSSVar(
|
return wrapCSSVar(
|
||||||
<div
|
<div
|
||||||
{...pickAttrs(props, { aria: true, data: true })}
|
{...pickAttrs(props, { aria: true, data: true })}
|
||||||
@ -117,15 +136,7 @@ const RadioGroup = React.forwardRef<HTMLDivElement, RadioGroupProps>((props, ref
|
|||||||
id={id}
|
id={id}
|
||||||
ref={ref}
|
ref={ref}
|
||||||
>
|
>
|
||||||
<RadioGroupContextProvider
|
<RadioGroupContextProvider value={memoizedValue}>
|
||||||
value={{
|
|
||||||
onChange: onRadioChange,
|
|
||||||
value,
|
|
||||||
disabled: props.disabled,
|
|
||||||
name: props.name,
|
|
||||||
optionType: props.optionType,
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
{childrenToRender}
|
{childrenToRender}
|
||||||
</RadioGroupContextProvider>
|
</RadioGroupContextProvider>
|
||||||
</div>,
|
</div>,
|
||||||
|
@ -118,7 +118,7 @@ Common props ref:[Common props](/docs/react/common-props)
|
|||||||
| status | Set validation status | 'error' \| 'warning' | - | 4.19.0 |
|
| status | Set validation status | 'error' \| 'warning' | - | 4.19.0 |
|
||||||
| suffixIcon | The custom suffix icon. Customize icon will not response click open to avoid icon designed to do other interactive. You can use `pointer-events: none` style to bypass | ReactNode | `<DownOutlined />` | |
|
| suffixIcon | The custom suffix icon. Customize icon will not response click open to avoid icon designed to do other interactive. You can use `pointer-events: none` style to bypass | ReactNode | `<DownOutlined />` | |
|
||||||
| tagRender | Customize tag render, only applies when `mode` is set to `multiple` or `tags` | (props) => ReactNode | - | |
|
| tagRender | Customize tag render, only applies when `mode` is set to `multiple` or `tags` | (props) => ReactNode | - | |
|
||||||
| labelRender | Customize selected label render | (label: ReactNode) => ReactNode | - | 5.15.0 |
|
| labelRender | Customize selected label render (LabelInValueType definition see [LabelInValueType](https://github.com/react-component/select/blob/b39c28aa2a94e7754ebc570f200ab5fd33bd31e7/src/Select.tsx#L70)) | (props: LabelInValueType) => ReactNode | - | 5.15.0 |
|
||||||
| tokenSeparators | Separator used to tokenize, only applies when `mode="tags"` | string\[] | - | |
|
| tokenSeparators | Separator used to tokenize, only applies when `mode="tags"` | string\[] | - | |
|
||||||
| value | Current selected option (considered as a immutable array) | string \| string\[] \| <br />number \| number\[] \| <br />LabeledValue \| LabeledValue\[] | - | |
|
| value | Current selected option (considered as a immutable array) | string \| string\[] \| <br />number \| number\[] \| <br />LabeledValue \| LabeledValue\[] | - | |
|
||||||
| variant | Variants of selector | `outlined` \| `borderless` \| `filled` | `outlined` | 5.13.0 |
|
| variant | Variants of selector | `outlined` \| `borderless` \| `filled` | `outlined` | 5.13.0 |
|
||||||
|
@ -119,7 +119,7 @@ return (
|
|||||||
| status | 设置校验状态 | 'error' \| 'warning' | - | 4.19.0 |
|
| status | 设置校验状态 | 'error' \| 'warning' | - | 4.19.0 |
|
||||||
| suffixIcon | 自定义的选择框后缀图标。以防止图标被用于其他交互,替换的图标默认不会响应展开、收缩事件,可以通过添加 `pointer-events: none` 样式透传。 | ReactNode | `<DownOutlined />` | |
|
| suffixIcon | 自定义的选择框后缀图标。以防止图标被用于其他交互,替换的图标默认不会响应展开、收缩事件,可以通过添加 `pointer-events: none` 样式透传。 | ReactNode | `<DownOutlined />` | |
|
||||||
| tagRender | 自定义 tag 内容 render,仅在 `mode` 为 `multiple` 或 `tags` 时生效 | (props) => ReactNode | - | |
|
| tagRender | 自定义 tag 内容 render,仅在 `mode` 为 `multiple` 或 `tags` 时生效 | (props) => ReactNode | - | |
|
||||||
| labelRender | 自定义当前选中的 label 内容 render | (label: ReactNode) => ReactNode | - | 5.15.0 |
|
| labelRender | 自定义当前选中的 label 内容 render (LabelInValueType的定义见 [LabelInValueType](https://github.com/react-component/select/blob/b39c28aa2a94e7754ebc570f200ab5fd33bd31e7/src/Select.tsx#L70)) | (props: LabelInValueType) => ReactNode | - | 5.15.0 |
|
||||||
| tokenSeparators | 自动分词的分隔符,仅在 `mode="tags"` 时生效 | string\[] | - | |
|
| tokenSeparators | 自动分词的分隔符,仅在 `mode="tags"` 时生效 | string\[] | - | |
|
||||||
| value | 指定当前选中的条目,多选时为一个数组。(value 数组引用未变化时,Select 不会更新) | string \| string\[] \| <br />number \| number\[] \| <br />LabeledValue \| LabeledValue\[] | - | |
|
| value | 指定当前选中的条目,多选时为一个数组。(value 数组引用未变化时,Select 不会更新) | string \| string\[] \| <br />number \| number\[] \| <br />LabeledValue \| LabeledValue\[] | - | |
|
||||||
| variant | 形态变体 | `outlined` \| `borderless` \| `filled` | `outlined` | 5.13.0 |
|
| variant | 形态变体 | `outlined` \| `borderless` \| `filled` | `outlined` | 5.13.0 |
|
||||||
|
@ -43,4 +43,111 @@ describe('Table.Virtual', () => {
|
|||||||
expect(errSpy).toHaveBeenCalledWith('Warning: `scroll.y` in virtual table must be number.');
|
expect(errSpy).toHaveBeenCalledWith('Warning: `scroll.y` in virtual table must be number.');
|
||||||
errSpy.mockRestore();
|
errSpy.mockRestore();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('should work with edit cell', () => {
|
||||||
|
const EditableRow: React.FC = ({ ...props }) => <tr {...props} />;
|
||||||
|
|
||||||
|
const EditableCell: React.FC<React.PropsWithChildren<any>> = ({ children, ...restProps }) => (
|
||||||
|
<td {...restProps}>{children}</td>
|
||||||
|
);
|
||||||
|
|
||||||
|
const components = {
|
||||||
|
body: {
|
||||||
|
row: EditableRow,
|
||||||
|
cell: EditableCell,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
const { container } = render(
|
||||||
|
<Table
|
||||||
|
virtual
|
||||||
|
components={components}
|
||||||
|
scroll={{ y: 100 }}
|
||||||
|
columns={[
|
||||||
|
{
|
||||||
|
dataIndex: 'key',
|
||||||
|
},
|
||||||
|
]}
|
||||||
|
dataSource={[
|
||||||
|
{
|
||||||
|
key: 'bamboo',
|
||||||
|
},
|
||||||
|
]}
|
||||||
|
/>,
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(
|
||||||
|
container.querySelectorAll('.ant-table-wrapper .ant-table-tbody-virtual .ant-table-row'),
|
||||||
|
).toHaveLength(1);
|
||||||
|
expect(
|
||||||
|
container.querySelectorAll('.ant-table-tbody-virtual-holder .ant-table-cell'),
|
||||||
|
).toHaveLength(1);
|
||||||
|
expect(
|
||||||
|
container.querySelector('.ant-table-tbody-virtual-holder .ant-table-cell')?.textContent,
|
||||||
|
).toEqual('bamboo');
|
||||||
|
const styleMap = getComputedStyle(
|
||||||
|
container.querySelector<HTMLElement>(
|
||||||
|
'.ant-table-wrapper .ant-table-tbody-virtual .ant-table-row',
|
||||||
|
)!,
|
||||||
|
);
|
||||||
|
expect(styleMap.display).toEqual('flex');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should work with sub table', () => {
|
||||||
|
const expandedRowRender = () => {
|
||||||
|
const columns = [
|
||||||
|
{ title: 'Date', dataIndex: 'date', key: 'date' },
|
||||||
|
{ title: 'Name', dataIndex: 'name', key: 'name' },
|
||||||
|
{ title: 'Upgrade Status', dataIndex: 'upgradeNum', key: 'upgradeNum' },
|
||||||
|
];
|
||||||
|
const data = [];
|
||||||
|
for (let i = 0; i < 3; ++i) {
|
||||||
|
data.push({
|
||||||
|
key: i.toString(),
|
||||||
|
date: '2014-12-24 23:12:00',
|
||||||
|
name: 'This is production name',
|
||||||
|
upgradeNum: 'Upgraded: 56',
|
||||||
|
});
|
||||||
|
}
|
||||||
|
return <Table columns={columns} dataSource={data} pagination={false} />;
|
||||||
|
};
|
||||||
|
const { container } = render(
|
||||||
|
<Table
|
||||||
|
columns={[
|
||||||
|
{
|
||||||
|
dataIndex: 'key',
|
||||||
|
},
|
||||||
|
]}
|
||||||
|
expandable={{ expandedRowRender, defaultExpandedRowKeys: ['0'] }}
|
||||||
|
dataSource={[
|
||||||
|
{
|
||||||
|
key: '0',
|
||||||
|
},
|
||||||
|
]}
|
||||||
|
size="middle"
|
||||||
|
virtual
|
||||||
|
scroll={{ y: 200 }}
|
||||||
|
/>,
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(
|
||||||
|
container.querySelectorAll('.ant-table-tbody-virtual-holder-inner > div > .ant-table-row'),
|
||||||
|
).toHaveLength(1);
|
||||||
|
expect(
|
||||||
|
container.querySelectorAll(
|
||||||
|
'.ant-table-tbody-virtual-holder-inner > div > .ant-table-row > .ant-table-cell',
|
||||||
|
)?.[1]?.textContent,
|
||||||
|
).toEqual('0');
|
||||||
|
|
||||||
|
expect(
|
||||||
|
container.querySelectorAll('.ant-table-tbody-virtual-holder .ant-table-expanded-row'),
|
||||||
|
).toHaveLength(1);
|
||||||
|
|
||||||
|
const styleMap = getComputedStyle(
|
||||||
|
container.querySelector<HTMLElement>(
|
||||||
|
'.ant-table-tbody-virtual-holder .ant-table-expanded-row .ant-table-row',
|
||||||
|
)!,
|
||||||
|
);
|
||||||
|
expect(styleMap.display).toEqual('table-row');
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
@ -89,7 +89,6 @@ const columns: TableProps<RecordType>['columns'] = [
|
|||||||
{
|
{
|
||||||
title: 'LastName',
|
title: 'LastName',
|
||||||
dataIndex: 'lastName',
|
dataIndex: 'lastName',
|
||||||
width: 120,
|
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
|
@ -15,11 +15,16 @@ const genVirtualStyle: GenerateStyle<TableToken, CSSObject> = (token) => {
|
|||||||
[`${componentCls}-wrapper`]: {
|
[`${componentCls}-wrapper`]: {
|
||||||
// ========================== Row ==========================
|
// ========================== Row ==========================
|
||||||
[`${componentCls}-tbody-virtual`]: {
|
[`${componentCls}-tbody-virtual`]: {
|
||||||
[`${componentCls}-row:not(tr)`]: {
|
[`${componentCls}-tbody-virtual-holder-inner`]: {
|
||||||
|
[`
|
||||||
|
& > ${componentCls}-row,
|
||||||
|
& > div:not(${componentCls}-row) > ${componentCls}-row
|
||||||
|
`]: {
|
||||||
display: 'flex',
|
display: 'flex',
|
||||||
boxSizing: 'border-box',
|
boxSizing: 'border-box',
|
||||||
width: '100%',
|
width: '100%',
|
||||||
},
|
},
|
||||||
|
},
|
||||||
|
|
||||||
[`${componentCls}-cell`]: {
|
[`${componentCls}-cell`]: {
|
||||||
borderBottom: tableBorder,
|
borderBottom: tableBorder,
|
||||||
|
Loading…
Reference in New Issue
Block a user