chore: auto merge branchs (#34432)

chore: sync master into next
This commit is contained in:
github-actions[bot] 2022-03-11 07:27:50 +00:00 committed by GitHub
commit 15112c0653
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
34 changed files with 914 additions and 681 deletions

View File

@ -1,7 +1,7 @@
import * as React from 'react';
import useState from 'rc-util/lib/hooks/useState';
import Button from '../button';
import { LegacyButtonType, ButtonProps, convertLegacyProps } from '../button/button';
import useDestroyed from './hooks/useDestroyed';
export interface ActionButtonProps {
type?: LegacyButtonType;
@ -21,8 +21,7 @@ function isThenable(thing?: PromiseLike<any>): boolean {
const ActionButton: React.FC<ActionButtonProps> = props => {
const clickedRef = React.useRef<boolean>(false);
const ref = React.useRef<any>();
const isDestroyed = useDestroyed();
const [loading, setLoading] = React.useState<ButtonProps['loading']>(false);
const [loading, setLoading] = useState<ButtonProps['loading']>(false);
React.useEffect(() => {
let timeoutId: any;
@ -45,9 +44,7 @@ const ActionButton: React.FC<ActionButtonProps> = props => {
setLoading(true);
returnValueOfOnOk!.then(
(...args: any[]) => {
if (!isDestroyed()) {
setLoading(false);
}
setLoading(false, true);
close(...args);
clickedRef.current = false;
},
@ -56,9 +53,7 @@ const ActionButton: React.FC<ActionButtonProps> = props => {
// eslint-disable-next-line no-console
console.error(e);
// See: https://github.com/ant-design/ant-design/issues/6183
if (!isDestroyed()) {
setLoading(false);
}
setLoading(false, true);
clickedRef.current = false;
},
);

View File

@ -1,20 +0,0 @@
import { mount } from 'enzyme';
import React from 'react';
import useDestroyed from '../hooks/useDestroyed';
describe('useMounted', () => {
it('should work properly', () => {
let isDestroyed = null;
const AutoUnmounted = () => {
isDestroyed = useDestroyed();
return <div>Mounted</div>;
};
const wrapper = mount(<AutoUnmounted />);
expect(isDestroyed()).toBeFalsy();
wrapper.unmount();
expect(isDestroyed()).toBeTruthy();
});
});

View File

@ -1,14 +0,0 @@
import * as React from 'react';
export default function useDestroyed() {
const mountedRef = React.useRef<boolean>(true);
React.useEffect(
() => () => {
mountedRef.current = false;
},
[],
);
return () => !mountedRef.current;
}

View File

@ -120,6 +120,11 @@
border-radius: 0;
}
// hide the last border-bottom in borderless mode
&-borderless > &-item:last-child {
border-bottom: 0;
}
&-borderless > &-item > &-content {
background-color: transparent;
border-top: 0;

View File

@ -79,4 +79,24 @@ describe('Dropdown', () => {
expect.stringContaining("[antd: Dropdown] You are using 'topCenter'"),
);
});
// zombieJ: when replaced with react test lib, it may be mock fully content
it('dropdown should support auto adjust placement', () => {
const wrapper = mount(
<Dropdown overlay={<div>menu</div>} visible>
<button type="button">button</button>
</Dropdown>,
);
expect(wrapper.find('Trigger').prop('builtinPlacements')).toEqual(
expect.objectContaining({
bottomLeft: expect.objectContaining({
overflow: {
adjustX: 1,
adjustY: 1,
},
}),
}),
);
});
});

View File

@ -189,6 +189,7 @@ const Dropdown: DropdownInterface = props => {
const builtinPlacements = getPlacements({
arrowPointAtCenter: typeof arrow === 'object' && arrow.pointAtCenter,
autoAdjustOverflow: true,
});
return (

View File

@ -5,6 +5,7 @@ import { Field, FormInstance, FieldContext, ListContext } from 'rc-field-form';
import { FieldProps } from 'rc-field-form/lib/Field';
import { Meta, NamePath } from 'rc-field-form/lib/interface';
import { supportRef } from 'rc-util/lib/ref';
import useState from 'rc-util/lib/hooks/useState';
import omit from 'rc-util/lib/omit';
import Row from '../grid/row';
import { ConfigContext } from '../config-provider';
@ -132,7 +133,7 @@ function FormItem<Values = any>(props: FormItemProps<Values>): React.ReactElemen
const [subFieldErrors, setSubFieldErrors] = useFrameState<Record<string, FieldError>>({});
// >>>>> Current field errors
const [meta, setMeta] = React.useState<Meta>(() => genEmptyMeta());
const [meta, setMeta] = useState<Meta>(() => genEmptyMeta());
const onMetaChange = (nextMeta: Meta & { destroy?: boolean }) => {
// This keyInfo is not correct when field is removed
@ -141,7 +142,7 @@ function FormItem<Values = any>(props: FormItemProps<Values>): React.ReactElemen
const keyInfo = listContext?.getKey(nextMeta.name);
// Destroy will reset all the meta
setMeta(nextMeta.destroy ? genEmptyMeta() : nextMeta);
setMeta(nextMeta.destroy ? genEmptyMeta() : nextMeta, true);
// Bump to parent since noStyle
if (noStyle && notifyParentMetaChange) {

View File

@ -1,6 +1,8 @@
import React from 'react';
import { mount } from 'enzyme';
import { act } from 'react-dom/test-utils';
import { render, fireEvent } from '@testing-library/react';
import '@testing-library/jest-dom';
import Form from '..';
import Input from '../../input';
import Button from '../../button';
@ -204,4 +206,52 @@ describe('Form.List', () => {
const wrapper = mount(<Form.ErrorList />);
expect(wrapper.render()).toMatchSnapshot();
});
it('no warning when reset in validate', async () => {
const errorSpy = jest.spyOn(console, 'error').mockImplementation(() => {});
const Demo = () => {
const [form] = Form.useForm();
React.useEffect(() => {
form.setFieldsValue({
list: [1],
});
}, []);
return (
<Form form={form}>
<Form.List name="list">
{fields =>
fields.map(field => (
<Form.Item key={field.key} {...field}>
<Input />
</Form.Item>
))
}
</Form.List>
<button
id="validate"
type="button"
onClick={() => {
form.validateFields().then(() => {
form.resetFields();
});
}}
>
Validate
</button>
</Form>
);
};
const { container } = render(<Demo />);
fireEvent.click(container.querySelector('button'));
await sleep();
expect(errorSpy).not.toHaveBeenCalled();
errorSpy.mockRestore();
});
});

View File

@ -3,6 +3,7 @@ import omit from 'rc-util/lib/omit';
import { Meta } from 'rc-field-form/lib/interface';
import { FormProvider as RcFormProvider } from 'rc-field-form';
import { FormProviderProps as RcFormProviderProps } from 'rc-field-form/lib/FormContext';
import { FC, PropsWithChildren, useMemo } from 'react';
import { ColProps } from '../grid/col';
import { FormLabelAlign } from './interface';
import { RequiredMark } from './Form';
@ -57,3 +58,11 @@ export interface FormItemStatusContextProps {
}
export const FormItemStatusContext = React.createContext<FormItemStatusContextProps>({});
export const NoFormStatus: FC<PropsWithChildren<{}>> = ({ children }: PropsWithChildren<{}>) => {
const emptyContext = useMemo(() => ({}), []);
return (
<FormItemStatusContext.Provider value={emptyContext}>{children}</FormItemStatusContext.Provider>
);
};

View File

@ -6,7 +6,7 @@ import * as React from 'react';
import { useContext } from 'react';
import { ConfigContext } from '../config-provider';
import SizeContext, { SizeType } from '../config-provider/SizeContext';
import { FormItemStatusContext } from '../form/context';
import { FormItemStatusContext, NoFormStatus } from '../form/context';
import { cloneElement } from '../_util/reactNode';
import {
getFeedbackIcon,
@ -168,9 +168,9 @@ const InputNumber = React.forwardRef<HTMLInputElement, InputNumberProps>((props,
element = (
<div className={mergedGroupClassName} style={props.style}>
<div className={mergedWrapperClassName}>
{addonBeforeNode}
{addonBeforeNode && <NoFormStatus>{addonBeforeNode}</NoFormStatus>}
{cloneElement(element, { style: null })}
{addonAfterNode}
{addonAfterNode && <NoFormStatus>{addonAfterNode}</NoFormStatus>}
</div>
</div>
);

View File

@ -11,7 +11,7 @@ import {
InputStatus,
} from '../_util/statusUtils';
import { ConfigContext } from '../config-provider';
import { FormItemStatusContext } from '../form/context';
import { FormItemStatusContext, NoFormStatus } from '../form/context';
import { hasPrefixSuffix } from './utils';
import devWarning from '../_util/devWarning';
@ -121,6 +121,7 @@ export interface InputProps
size?: SizeType;
status?: InputStatus;
bordered?: boolean;
[key: `data-${string}`]: string;
}
const Input = forwardRef<InputRef, InputProps>((props, ref) => {
@ -133,6 +134,8 @@ const Input = forwardRef<InputRef, InputProps>((props, ref) => {
onFocus,
suffix,
allowClear,
addonAfter,
addonBefore,
...rest
} = props;
const { getPrefixCls, direction, input, iconPrefixCls } = React.useContext(ConfigContext);
@ -223,6 +226,8 @@ const Input = forwardRef<InputRef, InputProps>((props, ref) => {
onFocus={handleFocus}
suffix={suffixNode}
allowClear={mergedAllowClear}
addonAfter={addonAfter && <NoFormStatus>{addonAfter}</NoFormStatus>}
addonBefore={addonBefore && <NoFormStatus>{addonBefore}</NoFormStatus>}
inputClassName={classNames(
!withPrefixSuffix && {
[`${prefixCls}-sm`]: mergedSize === 'small',

View File

@ -2,7 +2,7 @@ import React, { useState } from 'react';
import { mount } from 'enzyme';
// eslint-disable-next-line import/no-unresolved
import Form from '../../form';
import Input from '..';
import Input, { InputProps, InputRef } from '..';
import mountTest from '../../../tests/shared/mountTest';
import rtlTest from '../../../tests/shared/rtlTest';
@ -29,7 +29,7 @@ describe('Input', () => {
});
it('select()', () => {
const ref = React.createRef();
const ref = React.createRef<InputRef>();
mount(<Input ref={ref} />);
ref.current?.select();
});
@ -55,7 +55,7 @@ describe('Input', () => {
describe('focus trigger warning', () => {
it('not trigger', () => {
const wrapper = mount(<Input suffix="bamboo" />);
wrapper.find('input').instance().focus();
(wrapper.find('input').instance() as any).focus();
wrapper.setProps({
suffix: 'light',
});
@ -63,7 +63,7 @@ describe('Input', () => {
});
it('trigger warning', () => {
const wrapper = mount(<Input />);
wrapper.find('input').first().getDOMNode().focus();
wrapper.find('input').first().getDOMNode<HTMLInputElement>().focus();
wrapper.setProps({
suffix: 'light',
});
@ -77,11 +77,11 @@ describe('Input', () => {
it('set mouse cursor position', () => {
const defaultValue = '11111';
const valLength = defaultValue.length;
const ref = React.createRef();
const ref = React.createRef<InputRef>();
const wrapper = mount(<Input ref={ref} autoFocus defaultValue={defaultValue} />);
ref.current?.setSelectionRange(valLength, valLength);
expect(wrapper.find('input').first().getDOMNode().selectionStart).toEqual(5);
expect(wrapper.find('input').first().getDOMNode().selectionEnd).toEqual(5);
expect(wrapper.find('input').first().getDOMNode<HTMLInputElement>().selectionStart).toEqual(5);
expect(wrapper.find('input').first().getDOMNode<HTMLInputElement>().selectionEnd).toEqual(5);
});
});
@ -106,8 +106,12 @@ describe('prefix and suffix', () => {
</>,
);
expect(wrapper.find('.prefix-with-hidden').at(0).getDOMNode().hidden).toBe(true);
expect(wrapper.find('.suffix-with-hidden').at(0).getDOMNode().hidden).toBe(true);
expect(wrapper.find('.prefix-with-hidden').at(0).getDOMNode<HTMLInputElement>().hidden).toBe(
true,
);
expect(wrapper.find('.suffix-with-hidden').at(0).getDOMNode<HTMLInputElement>().hidden).toBe(
true,
);
});
});
@ -143,6 +147,7 @@ describe('Input setting hidden', () => {
showCount
allowClear
prefix="11"
// @ts-ignore
suffix="22"
addonBefore="http://"
addonAfter=".com"
@ -162,10 +167,10 @@ describe('Input setting hidden', () => {
</>,
);
expect(wrapper.find('.input').at(0).getDOMNode().hidden).toBe(true);
expect(wrapper.find('.input-search').at(0).getDOMNode().hidden).toBe(true);
expect(wrapper.find('.input-textarea').at(0).getDOMNode().hidden).toBe(true);
expect(wrapper.find('.input-password').at(0).getDOMNode().hidden).toBe(true);
expect(wrapper.find('.input').at(0).getDOMNode<HTMLInputElement>().hidden).toBe(true);
expect(wrapper.find('.input-search').at(0).getDOMNode<HTMLInputElement>().hidden).toBe(true);
expect(wrapper.find('.input-textarea').at(0).getDOMNode<HTMLInputElement>().hidden).toBe(true);
expect(wrapper.find('.input-password').at(0).getDOMNode<HTMLInputElement>().hidden).toBe(true);
});
});
@ -250,17 +255,18 @@ describe('Input allowClear', () => {
it('should change type when click', () => {
const wrapper = mount(<Input allowClear />);
wrapper.find('input').simulate('change', { target: { value: '111' } });
expect(wrapper.find('input').getDOMNode().value).toEqual('111');
expect(wrapper.find('input').getDOMNode<HTMLInputElement>().value).toEqual('111');
expect(wrapper.render()).toMatchSnapshot();
wrapper.find('.ant-input-clear-icon').at(0).simulate('click');
expect(wrapper.render()).toMatchSnapshot();
expect(wrapper.find('input').getDOMNode().value).toEqual('');
expect(wrapper.find('input').getDOMNode<HTMLInputElement>().value).toEqual('');
});
it('should not show icon if value is undefined, null or empty string', () => {
// @ts-ignore
const wrappers = [null, undefined, ''].map(val => mount(<Input allowClear value={val} />));
wrappers.forEach(wrapper => {
expect(wrapper.find('input').getDOMNode().value).toEqual('');
expect(wrapper.find('input').getDOMNode<HTMLInputElement>().value).toEqual('');
expect(wrapper.find('.ant-input-clear-icon-hidden').exists()).toBeTruthy();
expect(wrapper.render()).toMatchSnapshot();
});
@ -268,41 +274,43 @@ describe('Input allowClear', () => {
it('should not show icon if defaultValue is undefined, null or empty string', () => {
const wrappers = [null, undefined, ''].map(val =>
// @ts-ignore
mount(<Input allowClear defaultValue={val} />),
);
wrappers.forEach(wrapper => {
expect(wrapper.find('input').getDOMNode().value).toEqual('');
expect(wrapper.find('input').getDOMNode<HTMLInputElement>().value).toEqual('');
expect(wrapper.find('.ant-input-clear-icon-hidden').exists()).toBeTruthy();
expect(wrapper.render()).toMatchSnapshot();
});
});
it('should trigger event correctly', () => {
let argumentEventObject;
let argumentEventObject: React.ChangeEvent<HTMLInputElement> | undefined;
let argumentEventObjectValue;
const onChange = e => {
const onChange: InputProps['onChange'] = e => {
argumentEventObject = e;
argumentEventObjectValue = e.target.value;
};
const wrapper = mount(<Input allowClear defaultValue="111" onChange={onChange} />);
wrapper.find('.ant-input-clear-icon').at(0).simulate('click');
expect(argumentEventObject.type).toBe('click');
expect(argumentEventObject?.type).toBe('click');
expect(argumentEventObjectValue).toBe('');
expect(wrapper.find('input').at(0).getDOMNode().value).toBe('');
expect(wrapper.find('input').at(0).getDOMNode<HTMLInputElement>().value).toBe('');
});
it('should trigger event correctly on controlled mode', () => {
let argumentEventObject;
let argumentEventObject: React.ChangeEvent<HTMLInputElement> | undefined;
let argumentEventObjectValue;
const onChange = e => {
const onChange: InputProps['onChange'] = e => {
argumentEventObject = e;
argumentEventObjectValue = e.target.value;
};
const wrapper = mount(<Input allowClear value="111" onChange={onChange} />);
wrapper.find('.ant-input-clear-icon').at(0).simulate('click');
expect(argumentEventObject.type).toBe('click');
expect(argumentEventObject?.type).toBe('click');
expect(argumentEventObjectValue).toBe('');
expect(wrapper.find('input').at(0).getDOMNode().value).toBe('111');
expect(wrapper.find('input').at(0).getDOMNode<HTMLInputElement>().value).toBe('111');
});
it('should focus input after clear', () => {
@ -332,12 +340,12 @@ describe('Input allowClear', () => {
const wrapper = mount(<Input allowClear defaultValue="value" onBlur={onBlur} />, {
attachTo: document.body,
});
wrapper.find('input').getDOMNode().focus();
wrapper.find('input').getDOMNode<HTMLInputElement>().focus();
wrapper.find('.ant-input-clear-icon').at(0).simulate('mouseDown');
wrapper.find('.ant-input-clear-icon').at(0).simulate('click');
wrapper.find('.ant-input-clear-icon').at(0).simulate('mouseUp');
wrapper.find('.ant-input-clear-icon').at(0).simulate('focus');
wrapper.find('.ant-input-clear-icon').at(0).getDOMNode().click();
wrapper.find('.ant-input-clear-icon').at(0).getDOMNode<HTMLInputElement>().click();
expect(onBlur).not.toBeCalled();
wrapper.unmount();
});
@ -359,12 +367,12 @@ describe('Input allowClear', () => {
const wrapper = mount(<App />);
wrapper.find('input').getDOMNode().focus();
wrapper.find('input').getDOMNode<HTMLInputElement>().focus();
wrapper.find('input').simulate('change', { target: { value: '111' } });
expect(wrapper.find('input').getDOMNode().value).toEqual('111');
expect(wrapper.find('input').getDOMNode<HTMLInputElement>().value).toEqual('111');
wrapper.find('.ant-input-clear-icon').at(0).simulate('click');
expect(wrapper.find('input').getDOMNode().value).toEqual('');
expect(wrapper.find('input').getDOMNode<HTMLInputElement>().value).toEqual('');
wrapper.unmount();
});
@ -375,10 +383,11 @@ describe('Input allowClear', () => {
});
it('should display boolean value as string', () => {
// @ts-ignore
const wrapper = mount(<Input value />);
expect(wrapper.find('input').first().getDOMNode().value).toBe('true');
expect(wrapper.find('input').first().getDOMNode<HTMLInputElement>().value).toBe('true');
wrapper.setProps({ value: false });
expect(wrapper.find('input').first().getDOMNode().value).toBe('false');
expect(wrapper.find('input').first().getDOMNode<HTMLInputElement>().value).toBe('false');
});
it('should support custom clearIcon', () => {
@ -386,3 +395,19 @@ describe('Input allowClear', () => {
expect(wrapper.find('.ant-input-clear-icon').text()).toBe('clear');
});
});
describe('typescript types ', () => {
it('InputProps type should support data-* attributes', () => {
const props: InputProps = {
value: 123,
// expect no ts error here
'data-testid': 'test-id',
'data-id': '12345',
};
const wrapper = mount(<Input {...props} />);
const input = wrapper.find('input').first().getDOMNode();
expect(input.getAttribute('data-testid')).toBe('test-id');
expect(input.getAttribute('data-id')).toBe('12345');
});
});

View File

@ -7,6 +7,10 @@
.input-lg() {
padding: @input-padding-vertical-lg @input-padding-horizontal-lg;
font-size: @font-size-lg;
input {
font-size: @font-size-lg;
}
}
.input-sm() {

View File

@ -1162,7 +1162,7 @@ exports[`renders ./components/list/demo/loadmore.md extend context correctly 1`]
</div>
`;
exports[`renders ./components/list/demo/resposive.md extend context correctly 1`] = `
exports[`renders ./components/list/demo/responsive.md extend context correctly 1`] = `
<div
class="ant-list ant-list-split ant-list-grid"
>

View File

@ -1162,7 +1162,7 @@ exports[`renders ./components/list/demo/loadmore.md correctly 1`] = `
</div>
`;
exports[`renders ./components/list/demo/resposive.md correctly 1`] = `
exports[`renders ./components/list/demo/responsive.md correctly 1`] = `
<div
class="ant-list ant-list-split ant-list-grid"
>

View File

@ -3,15 +3,14 @@ import RcMenu, { ItemGroup, MenuProps as RcMenuProps } from 'rc-menu';
import classNames from 'classnames';
import omit from 'rc-util/lib/omit';
import EllipsisOutlined from '@ant-design/icons/EllipsisOutlined';
import memoize from 'memoize-one';
import SubMenu, { SubMenuProps } from './SubMenu';
import Item, { MenuItemProps } from './MenuItem';
import { ConfigConsumer, ConfigConsumerProps, DirectionType } from '../config-provider';
import { ConfigContext } from '../config-provider';
import devWarning from '../_util/devWarning';
import { SiderContext, SiderContextProps } from '../layout/Sider';
import collapseMotion from '../_util/motion';
import { cloneElement } from '../_util/reactNode';
import MenuContext, { MenuTheme, MenuContextProps } from './MenuContext';
import MenuContext, { MenuTheme } from './MenuContext';
import MenuDivider from './MenuDivider';
export { MenuDividerProps } from './MenuDivider';
@ -37,108 +36,87 @@ type InternalMenuProps = MenuProps &
collapsedWidth?: string | number;
};
class InternalMenu extends React.Component<InternalMenuProps> {
static defaultProps: Partial<MenuProps> = {
theme: 'light', // or dark
};
function InternalMenu(props: InternalMenuProps) {
const { getPrefixCls, getPopupContainer, direction } = React.useContext(ConfigContext);
constructor(props: InternalMenuProps) {
super(props);
const rootPrefixCls = getPrefixCls();
devWarning(
!('inlineCollapsed' in props && props.mode !== 'inline'),
'Menu',
'`inlineCollapsed` should only be used when `mode` is inline.',
);
const {
prefixCls: customizePrefixCls,
className,
theme = 'light',
expandIcon,
_internalDisableMenuItemTitleTooltip,
inlineCollapsed,
siderCollapsed,
...restProps
} = props;
devWarning(
!(props.siderCollapsed !== undefined && 'inlineCollapsed' in props),
'Menu',
'`inlineCollapsed` not control Menu under Sider. Should set `collapsed` on Sider instead.',
);
}
const passedProps = omit(restProps, ['collapsedWidth']);
getInlineCollapsed() {
const { inlineCollapsed, siderCollapsed } = this.props;
// ======================== Warning ==========================
devWarning(
!('inlineCollapsed' in props && props.mode !== 'inline'),
'Menu',
'`inlineCollapsed` should only be used when `mode` is inline.',
);
devWarning(
!(props.siderCollapsed !== undefined && 'inlineCollapsed' in props),
'Menu',
'`inlineCollapsed` not control Menu under Sider. Should set `collapsed` on Sider instead.',
);
// ======================== Collapsed ========================
// Inline Collapsed
const mergedInlineCollapsed = React.useMemo(() => {
if (siderCollapsed !== undefined) {
return siderCollapsed;
}
return inlineCollapsed;
}
}, [inlineCollapsed, siderCollapsed]);
getMemoizedContextValue = memoize(
(
cls: string,
collapsed: boolean | undefined,
the: MenuTheme | undefined,
dir: DirectionType,
disableMenuItemTitleTooltip: boolean | undefined,
): MenuContextProps => ({
prefixCls: cls,
inlineCollapsed: collapsed || false,
antdMenuTheme: the,
direction: dir,
firstLevel: true,
disableMenuItemTitleTooltip,
}),
);
renderMenu = ({ getPopupContainer, getPrefixCls, direction }: ConfigConsumerProps) => {
const rootPrefixCls = getPrefixCls();
const {
prefixCls: customizePrefixCls,
className,
theme,
expandIcon,
_internalDisableMenuItemTitleTooltip,
...restProps
} = this.props;
const passedProps = omit(restProps, ['siderCollapsed', 'collapsedWidth']);
const inlineCollapsed = this.getInlineCollapsed();
const defaultMotions = {
horizontal: { motionName: `${rootPrefixCls}-slide-up` },
inline: collapseMotion,
other: { motionName: `${rootPrefixCls}-zoom-big` },
};
const prefixCls = getPrefixCls('menu', customizePrefixCls);
const menuClassName = classNames(`${prefixCls}-${theme}`, className);
// TODO: refactor menu with function component
const contextValue = this.getMemoizedContextValue(
prefixCls,
inlineCollapsed,
theme,
direction,
_internalDisableMenuItemTitleTooltip,
);
return (
<MenuContext.Provider value={contextValue}>
<RcMenu
getPopupContainer={getPopupContainer}
overflowedIndicator={<EllipsisOutlined />}
overflowedIndicatorPopupClassName={`${prefixCls}-${theme}`}
{...passedProps}
inlineCollapsed={inlineCollapsed}
className={menuClassName}
prefixCls={prefixCls}
direction={direction}
defaultMotions={defaultMotions}
expandIcon={cloneElement(expandIcon, {
className: `${prefixCls}-submenu-expand-icon`,
})}
/>
</MenuContext.Provider>
);
const defaultMotions = {
horizontal: { motionName: `${rootPrefixCls}-slide-up` },
inline: collapseMotion,
other: { motionName: `${rootPrefixCls}-zoom-big` },
};
render() {
return <ConfigConsumer>{this.renderMenu}</ConfigConsumer>;
}
const prefixCls = getPrefixCls('menu', customizePrefixCls);
const menuClassName = classNames(`${prefixCls}-${theme}`, className);
// ======================== Context ==========================
const contextValue = React.useMemo(
() => ({
prefixCls,
inlineCollapsed: mergedInlineCollapsed || false,
antdMenuTheme: theme,
direction,
firstLevel: true,
disableMenuItemTitleTooltip: _internalDisableMenuItemTitleTooltip,
}),
[prefixCls, mergedInlineCollapsed, theme, direction, _internalDisableMenuItemTitleTooltip],
);
// ========================= Render ==========================
return (
<MenuContext.Provider value={contextValue}>
<RcMenu
getPopupContainer={getPopupContainer}
overflowedIndicator={<EllipsisOutlined />}
overflowedIndicatorPopupClassName={`${prefixCls}-${theme}`}
{...passedProps}
inlineCollapsed={mergedInlineCollapsed}
className={menuClassName}
prefixCls={prefixCls}
direction={direction}
defaultMotions={defaultMotions}
expandIcon={cloneElement(expandIcon, {
className: `${prefixCls}-submenu-expand-icon`,
})}
/>
</MenuContext.Provider>
);
}
// We should keep this as ref-able

View File

@ -58,30 +58,48 @@ Array [
<span
class="ant-page-header-heading-extra"
>
<button
class="ant-btn ant-btn-default"
type="button"
<div
class="ant-space ant-space-horizontal ant-space-align-center"
>
<span>
Operation
</span>
</button>
<button
class="ant-btn ant-btn-default"
type="button"
>
<span>
Operation
</span>
</button>
<button
class="ant-btn ant-btn-primary"
type="button"
>
<span>
Primary
</span>
</button>
<div
class="ant-space-item"
style="margin-right:8px"
>
<button
class="ant-btn ant-btn-default"
type="button"
>
<span>
Operation
</span>
</button>
</div>
<div
class="ant-space-item"
style="margin-right:8px"
>
<button
class="ant-btn ant-btn-default"
type="button"
>
<span>
Operation
</span>
</button>
</div>
<div
class="ant-space-item"
>
<button
class="ant-btn ant-btn-primary"
type="button"
>
<span>
Primary
</span>
</button>
</div>
</div>
</span>
</div>
<div
@ -272,30 +290,48 @@ Array [
<span
class="ant-page-header-heading-extra"
>
<button
class="ant-btn ant-btn-default"
type="button"
<div
class="ant-space ant-space-horizontal ant-space-align-center"
>
<span>
Operation
</span>
</button>
<button
class="ant-btn ant-btn-default"
type="button"
>
<span>
Operation
</span>
</button>
<button
class="ant-btn ant-btn-primary"
type="button"
>
<span>
Primary
</span>
</button>
<div
class="ant-space-item"
style="margin-right:8px"
>
<button
class="ant-btn ant-btn-default"
type="button"
>
<span>
Operation
</span>
</button>
</div>
<div
class="ant-space-item"
style="margin-right:8px"
>
<button
class="ant-btn ant-btn-default"
type="button"
>
<span>
Operation
</span>
</button>
</div>
<div
class="ant-space-item"
>
<button
class="ant-btn ant-btn-primary"
type="button"
>
<span>
Primary
</span>
</button>
</div>
</div>
</span>
</div>
<div
@ -620,252 +656,274 @@ exports[`renders ./components/page-header/demo/content.md extend context correct
<span
class="ant-page-header-heading-extra"
>
<button
class="ant-btn ant-btn-default"
type="button"
<div
class="ant-space ant-space-horizontal ant-space-align-center"
>
<span>
Operation
</span>
</button>
<button
class="ant-btn ant-btn-default"
type="button"
>
<span>
Operation
</span>
</button>
<button
class="ant-btn ant-btn-primary"
type="button"
>
<span>
Primary
</span>
</button>
<button
class="ant-btn ant-btn-default ant-dropdown-trigger"
style="border:none;padding:0"
type="button"
>
<span
aria-label="ellipsis"
class="anticon anticon-ellipsis"
role="img"
style="font-size:20px;vertical-align:top"
>
<svg
aria-hidden="true"
data-icon="ellipsis"
fill="currentColor"
focusable="false"
height="1em"
viewBox="64 64 896 896"
width="1em"
>
<path
d="M176 511a56 56 0 10112 0 56 56 0 10-112 0zm280 0a56 56 0 10112 0 56 56 0 10-112 0zm280 0a56 56 0 10112 0 56 56 0 10-112 0z"
/>
</svg>
</span>
</button>
<div>
<div
class="ant-dropdown"
style="opacity:0;pointer-events:none"
class="ant-space-item"
style="margin-right:8px"
>
<ul
class="ant-dropdown-menu ant-dropdown-menu-root ant-dropdown-menu-vertical ant-dropdown-menu-light"
data-menu-list="true"
role="menu"
tabindex="0"
<button
class="ant-btn ant-btn-default"
type="button"
>
<li
class="ant-dropdown-menu-item ant-dropdown-menu-item-only-child"
role="menuitem"
tabindex="-1"
>
<span
class="ant-dropdown-menu-title-content"
>
<a
href="http://www.alipay.com/"
rel="noopener noreferrer"
target="_blank"
>
1st menu item
</a>
</span>
</li>
<div>
<div
class="ant-tooltip ant-dropdown-menu-inline-collapsed-tooltip"
style="opacity:0;pointer-events:none"
>
<div
class="ant-tooltip-content"
>
<div
class="ant-tooltip-arrow"
>
<span
class="ant-tooltip-arrow-content"
/>
</div>
<div
class="ant-tooltip-inner"
role="tooltip"
/>
</div>
</div>
</div>
<li
class="ant-dropdown-menu-item ant-dropdown-menu-item-only-child"
role="menuitem"
tabindex="-1"
>
<span
class="ant-dropdown-menu-title-content"
>
<a
href="http://www.taobao.com/"
rel="noopener noreferrer"
target="_blank"
>
2nd menu item
</a>
</span>
</li>
<div>
<div
class="ant-tooltip ant-dropdown-menu-inline-collapsed-tooltip"
style="opacity:0;pointer-events:none"
>
<div
class="ant-tooltip-content"
>
<div
class="ant-tooltip-arrow"
>
<span
class="ant-tooltip-arrow-content"
/>
</div>
<div
class="ant-tooltip-inner"
role="tooltip"
/>
</div>
</div>
</div>
<li
class="ant-dropdown-menu-item ant-dropdown-menu-item-only-child"
role="menuitem"
tabindex="-1"
>
<span
class="ant-dropdown-menu-title-content"
>
<a
href="http://www.tmall.com/"
rel="noopener noreferrer"
target="_blank"
>
3rd menu item
</a>
</span>
</li>
<div>
<div
class="ant-tooltip ant-dropdown-menu-inline-collapsed-tooltip"
style="opacity:0;pointer-events:none"
>
<div
class="ant-tooltip-content"
>
<div
class="ant-tooltip-arrow"
>
<span
class="ant-tooltip-arrow-content"
/>
</div>
<div
class="ant-tooltip-inner"
role="tooltip"
/>
</div>
</div>
</div>
</ul>
<div
aria-hidden="true"
style="display:none"
<span>
Operation
</span>
</button>
</div>
<div
class="ant-space-item"
style="margin-right:8px"
>
<button
class="ant-btn ant-btn-default"
type="button"
>
<div>
<div
class="ant-tooltip ant-dropdown-menu-inline-collapsed-tooltip"
style="opacity:0;pointer-events:none"
<span>
Operation
</span>
</button>
</div>
<div
class="ant-space-item"
style="margin-right:8px"
>
<button
class="ant-btn ant-btn-primary"
type="button"
>
<span>
Primary
</span>
</button>
</div>
<div
class="ant-space-item"
>
<button
class="ant-btn ant-btn-text ant-btn-icon-only ant-dropdown-trigger"
type="button"
>
<span
aria-label="more"
class="anticon anticon-more"
role="img"
style="font-size:20px"
>
<svg
aria-hidden="true"
data-icon="more"
fill="currentColor"
focusable="false"
height="1em"
viewBox="64 64 896 896"
width="1em"
>
<div
class="ant-tooltip-content"
<path
d="M456 231a56 56 0 10112 0 56 56 0 10-112 0zm0 280a56 56 0 10112 0 56 56 0 10-112 0zm0 280a56 56 0 10112 0 56 56 0 10-112 0z"
/>
</svg>
</span>
</button>
<div>
<div
class="ant-dropdown"
style="opacity:0;pointer-events:none"
>
<ul
class="ant-dropdown-menu ant-dropdown-menu-root ant-dropdown-menu-vertical ant-dropdown-menu-light"
data-menu-list="true"
role="menu"
tabindex="0"
>
<li
class="ant-dropdown-menu-item ant-dropdown-menu-item-only-child"
role="menuitem"
tabindex="-1"
>
<div
class="ant-tooltip-arrow"
<span
class="ant-dropdown-menu-title-content"
>
<span
class="ant-tooltip-arrow-content"
/>
</div>
<a
href="http://www.alipay.com/"
rel="noopener noreferrer"
target="_blank"
>
1st menu item
</a>
</span>
</li>
<div>
<div
class="ant-tooltip-inner"
role="tooltip"
/>
class="ant-tooltip ant-dropdown-menu-inline-collapsed-tooltip"
style="opacity:0;pointer-events:none"
>
<div
class="ant-tooltip-content"
>
<div
class="ant-tooltip-arrow"
>
<span
class="ant-tooltip-arrow-content"
/>
</div>
<div
class="ant-tooltip-inner"
role="tooltip"
/>
</div>
</div>
</div>
</div>
</div>
<div>
<div
class="ant-tooltip ant-dropdown-menu-inline-collapsed-tooltip"
style="opacity:0;pointer-events:none"
>
<div
class="ant-tooltip-content"
<li
class="ant-dropdown-menu-item ant-dropdown-menu-item-only-child"
role="menuitem"
tabindex="-1"
>
<div
class="ant-tooltip-arrow"
<span
class="ant-dropdown-menu-title-content"
>
<span
class="ant-tooltip-arrow-content"
/>
</div>
<a
href="http://www.taobao.com/"
rel="noopener noreferrer"
target="_blank"
>
2nd menu item
</a>
</span>
</li>
<div>
<div
class="ant-tooltip-inner"
role="tooltip"
/>
class="ant-tooltip ant-dropdown-menu-inline-collapsed-tooltip"
style="opacity:0;pointer-events:none"
>
<div
class="ant-tooltip-content"
>
<div
class="ant-tooltip-arrow"
>
<span
class="ant-tooltip-arrow-content"
/>
</div>
<div
class="ant-tooltip-inner"
role="tooltip"
/>
</div>
</div>
</div>
</div>
</div>
<div>
<div
class="ant-tooltip ant-dropdown-menu-inline-collapsed-tooltip"
style="opacity:0;pointer-events:none"
>
<div
class="ant-tooltip-content"
<li
class="ant-dropdown-menu-item ant-dropdown-menu-item-only-child"
role="menuitem"
tabindex="-1"
>
<div
class="ant-tooltip-arrow"
<span
class="ant-dropdown-menu-title-content"
>
<span
class="ant-tooltip-arrow-content"
/>
</div>
<a
href="http://www.tmall.com/"
rel="noopener noreferrer"
target="_blank"
>
3rd menu item
</a>
</span>
</li>
<div>
<div
class="ant-tooltip-inner"
role="tooltip"
/>
class="ant-tooltip ant-dropdown-menu-inline-collapsed-tooltip"
style="opacity:0;pointer-events:none"
>
<div
class="ant-tooltip-content"
>
<div
class="ant-tooltip-arrow"
>
<span
class="ant-tooltip-arrow-content"
/>
</div>
<div
class="ant-tooltip-inner"
role="tooltip"
/>
</div>
</div>
</div>
</ul>
<div
aria-hidden="true"
style="display:none"
>
<div>
<div
class="ant-tooltip ant-dropdown-menu-inline-collapsed-tooltip"
style="opacity:0;pointer-events:none"
>
<div
class="ant-tooltip-content"
>
<div
class="ant-tooltip-arrow"
>
<span
class="ant-tooltip-arrow-content"
/>
</div>
<div
class="ant-tooltip-inner"
role="tooltip"
/>
</div>
</div>
</div>
<div>
<div
class="ant-tooltip ant-dropdown-menu-inline-collapsed-tooltip"
style="opacity:0;pointer-events:none"
>
<div
class="ant-tooltip-content"
>
<div
class="ant-tooltip-arrow"
>
<span
class="ant-tooltip-arrow-content"
/>
</div>
<div
class="ant-tooltip-inner"
role="tooltip"
/>
</div>
</div>
</div>
<div>
<div
class="ant-tooltip ant-dropdown-menu-inline-collapsed-tooltip"
style="opacity:0;pointer-events:none"
>
<div
class="ant-tooltip-content"
>
<div
class="ant-tooltip-arrow"
>
<span
class="ant-tooltip-arrow-content"
/>
</div>
<div
class="ant-tooltip-inner"
role="tooltip"
/>
</div>
</div>
</div>
</div>
</div>
@ -1000,30 +1058,48 @@ exports[`renders ./components/page-header/demo/ghost.md extend context correctly
<span
class="ant-page-header-heading-extra"
>
<button
class="ant-btn ant-btn-default"
type="button"
<div
class="ant-space ant-space-horizontal ant-space-align-center"
>
<span>
Operation
</span>
</button>
<button
class="ant-btn ant-btn-default"
type="button"
>
<span>
Operation
</span>
</button>
<button
class="ant-btn ant-btn-primary"
type="button"
>
<span>
Primary
</span>
</button>
<div
class="ant-space-item"
style="margin-right:8px"
>
<button
class="ant-btn ant-btn-default"
type="button"
>
<span>
Operation
</span>
</button>
</div>
<div
class="ant-space-item"
style="margin-right:8px"
>
<button
class="ant-btn ant-btn-default"
type="button"
>
<span>
Operation
</span>
</button>
</div>
<div
class="ant-space-item"
>
<button
class="ant-btn ant-btn-primary"
type="button"
>
<span>
Primary
</span>
</button>
</div>
</div>
</span>
</div>
<div
@ -1208,30 +1284,48 @@ exports[`renders ./components/page-header/demo/responsive.md extend context corr
<span
class="ant-page-header-heading-extra"
>
<button
class="ant-btn ant-btn-default"
type="button"
<div
class="ant-space ant-space-horizontal ant-space-align-center"
>
<span>
Operation
</span>
</button>
<button
class="ant-btn ant-btn-default"
type="button"
>
<span>
Operation
</span>
</button>
<button
class="ant-btn ant-btn-primary"
type="button"
>
<span>
Primary
</span>
</button>
<div
class="ant-space-item"
style="margin-right:8px"
>
<button
class="ant-btn ant-btn-default"
type="button"
>
<span>
Operation
</span>
</button>
</div>
<div
class="ant-space-item"
style="margin-right:8px"
>
<button
class="ant-btn ant-btn-default"
type="button"
>
<span>
Operation
</span>
</button>
</div>
<div
class="ant-space-item"
>
<button
class="ant-btn ant-btn-primary"
type="button"
>
<span>
Primary
</span>
</button>
</div>
</div>
</span>
</div>
<div

View File

@ -58,30 +58,48 @@ Array [
<span
class="ant-page-header-heading-extra"
>
<button
class="ant-btn ant-btn-default"
type="button"
<div
class="ant-space ant-space-horizontal ant-space-align-center"
>
<span>
Operation
</span>
</button>
<button
class="ant-btn ant-btn-default"
type="button"
>
<span>
Operation
</span>
</button>
<button
class="ant-btn ant-btn-primary"
type="button"
>
<span>
Primary
</span>
</button>
<div
class="ant-space-item"
style="margin-right:8px"
>
<button
class="ant-btn ant-btn-default"
type="button"
>
<span>
Operation
</span>
</button>
</div>
<div
class="ant-space-item"
style="margin-right:8px"
>
<button
class="ant-btn ant-btn-default"
type="button"
>
<span>
Operation
</span>
</button>
</div>
<div
class="ant-space-item"
>
<button
class="ant-btn ant-btn-primary"
type="button"
>
<span>
Primary
</span>
</button>
</div>
</div>
</span>
</div>
<div
@ -272,30 +290,48 @@ Array [
<span
class="ant-page-header-heading-extra"
>
<button
class="ant-btn ant-btn-default"
type="button"
<div
class="ant-space ant-space-horizontal ant-space-align-center"
>
<span>
Operation
</span>
</button>
<button
class="ant-btn ant-btn-default"
type="button"
>
<span>
Operation
</span>
</button>
<button
class="ant-btn ant-btn-primary"
type="button"
>
<span>
Primary
</span>
</button>
<div
class="ant-space-item"
style="margin-right:8px"
>
<button
class="ant-btn ant-btn-default"
type="button"
>
<span>
Operation
</span>
</button>
</div>
<div
class="ant-space-item"
style="margin-right:8px"
>
<button
class="ant-btn ant-btn-default"
type="button"
>
<span>
Operation
</span>
</button>
</div>
<div
class="ant-space-item"
>
<button
class="ant-btn ant-btn-primary"
type="button"
>
<span>
Primary
</span>
</button>
</div>
</div>
</span>
</div>
<div
@ -620,56 +656,78 @@ exports[`renders ./components/page-header/demo/content.md correctly 1`] = `
<span
class="ant-page-header-heading-extra"
>
<button
class="ant-btn ant-btn-default"
type="button"
<div
class="ant-space ant-space-horizontal ant-space-align-center"
>
<span>
Operation
</span>
</button>
<button
class="ant-btn ant-btn-default"
type="button"
>
<span>
Operation
</span>
</button>
<button
class="ant-btn ant-btn-primary"
type="button"
>
<span>
Primary
</span>
</button>
<button
class="ant-btn ant-btn-default ant-dropdown-trigger"
style="border:none;padding:0"
type="button"
>
<span
aria-label="ellipsis"
class="anticon anticon-ellipsis"
role="img"
style="font-size:20px;vertical-align:top"
<div
class="ant-space-item"
style="margin-right:8px"
>
<svg
aria-hidden="true"
data-icon="ellipsis"
fill="currentColor"
focusable="false"
height="1em"
viewBox="64 64 896 896"
width="1em"
<button
class="ant-btn ant-btn-default"
type="button"
>
<path
d="M176 511a56 56 0 10112 0 56 56 0 10-112 0zm280 0a56 56 0 10112 0 56 56 0 10-112 0zm280 0a56 56 0 10112 0 56 56 0 10-112 0z"
/>
</svg>
</span>
</button>
<span>
Operation
</span>
</button>
</div>
<div
class="ant-space-item"
style="margin-right:8px"
>
<button
class="ant-btn ant-btn-default"
type="button"
>
<span>
Operation
</span>
</button>
</div>
<div
class="ant-space-item"
style="margin-right:8px"
>
<button
class="ant-btn ant-btn-primary"
type="button"
>
<span>
Primary
</span>
</button>
</div>
<div
class="ant-space-item"
>
<button
class="ant-btn ant-btn-text ant-btn-icon-only ant-dropdown-trigger"
type="button"
>
<span
aria-label="more"
class="anticon anticon-more"
role="img"
style="font-size:20px"
>
<svg
aria-hidden="true"
data-icon="more"
fill="currentColor"
focusable="false"
height="1em"
viewBox="64 64 896 896"
width="1em"
>
<path
d="M456 231a56 56 0 10112 0 56 56 0 10-112 0zm0 280a56 56 0 10112 0 56 56 0 10-112 0zm0 280a56 56 0 10112 0 56 56 0 10-112 0z"
/>
</svg>
</span>
</button>
</div>
</div>
</span>
</div>
<div
@ -798,30 +856,48 @@ exports[`renders ./components/page-header/demo/ghost.md correctly 1`] = `
<span
class="ant-page-header-heading-extra"
>
<button
class="ant-btn ant-btn-default"
type="button"
<div
class="ant-space ant-space-horizontal ant-space-align-center"
>
<span>
Operation
</span>
</button>
<button
class="ant-btn ant-btn-default"
type="button"
>
<span>
Operation
</span>
</button>
<button
class="ant-btn ant-btn-primary"
type="button"
>
<span>
Primary
</span>
</button>
<div
class="ant-space-item"
style="margin-right:8px"
>
<button
class="ant-btn ant-btn-default"
type="button"
>
<span>
Operation
</span>
</button>
</div>
<div
class="ant-space-item"
style="margin-right:8px"
>
<button
class="ant-btn ant-btn-default"
type="button"
>
<span>
Operation
</span>
</button>
</div>
<div
class="ant-space-item"
>
<button
class="ant-btn ant-btn-primary"
type="button"
>
<span>
Primary
</span>
</button>
</div>
</div>
</span>
</div>
<div
@ -1006,30 +1082,48 @@ exports[`renders ./components/page-header/demo/responsive.md correctly 1`] = `
<span
class="ant-page-header-heading-extra"
>
<button
class="ant-btn ant-btn-default"
type="button"
<div
class="ant-space ant-space-horizontal ant-space-align-center"
>
<span>
Operation
</span>
</button>
<button
class="ant-btn ant-btn-default"
type="button"
>
<span>
Operation
</span>
</button>
<button
class="ant-btn ant-btn-primary"
type="button"
>
<span>
Primary
</span>
</button>
<div
class="ant-space-item"
style="margin-right:8px"
>
<button
class="ant-btn ant-btn-default"
type="button"
>
<span>
Operation
</span>
</button>
</div>
<div
class="ant-space-item"
style="margin-right:8px"
>
<button
class="ant-btn ant-btn-default"
type="button"
>
<span>
Operation
</span>
</button>
</div>
<div
class="ant-space-item"
>
<button
class="ant-btn ant-btn-primary"
type="button"
>
<span>
Primary
</span>
</button>
</div>
</div>
</span>
</div>
<div

View File

@ -15,7 +15,7 @@ Show all props provided by PageHeader.
```jsx
import { PageHeader, Menu, Dropdown, Button, Tag, Typography, Row } from 'antd';
import { EllipsisOutlined } from '@ant-design/icons';
import { MoreOutlined } from '@ant-design/icons';
const { Paragraph } = Typography;
@ -40,20 +40,8 @@ const menu = (
);
const DropdownMenu = () => (
<Dropdown key="more" overlay={menu}>
<Button
style={{
border: 'none',
padding: 0,
}}
>
<EllipsisOutlined
style={{
fontSize: 20,
verticalAlign: 'top',
}}
/>
</Button>
<Dropdown key="more" overlay={menu} placement="bottomRight">
<Button type="text" icon={<MoreOutlined style={{ fontSize: 20 }} />} />
</Dropdown>
);

View File

@ -1,5 +1,6 @@
import * as React from 'react';
import classNames from 'classnames';
import useState from 'rc-util/lib/hooks/useState';
import ArrowLeftOutlined from '@ant-design/icons/ArrowLeftOutlined';
import ArrowRightOutlined from '@ant-design/icons/ArrowRightOutlined';
import ResizeObserver from 'rc-resize-observer';
@ -7,9 +8,9 @@ import { ConfigConsumer, ConfigConsumerProps, DirectionType } from '../config-pr
import { TagType } from '../tag';
import Breadcrumb, { BreadcrumbProps } from '../breadcrumb';
import Avatar, { AvatarProps } from '../avatar';
import Space from '../space';
import TransButton from '../_util/transButton';
import LocaleReceiver from '../locale-provider/LocaleReceiver';
import useDestroyed from '../_util/hooks/useDestroyed';
export interface PageHeaderProps {
backIcon?: React.ReactNode;
@ -104,7 +105,11 @@ const renderTitle = (
{tags && <span className={`${headingPrefixCls}-tags`}>{tags}</span>}
</div>
)}
{extra && <span className={`${headingPrefixCls}-extra`}>{extra}</span>}
{extra && (
<span className={`${headingPrefixCls}-extra`}>
<Space>{extra}</Space>
</span>
)}
</div>
);
};
@ -121,12 +126,9 @@ const renderChildren = (prefixCls: string, children: React.ReactNode) => (
);
const PageHeader: React.FC<PageHeaderProps> = props => {
const [compact, updateCompact] = React.useState(false);
const isDestroyed = useDestroyed();
const [compact, updateCompact] = useState(false);
const onResize = ({ width }: { width: number }) => {
if (!isDestroyed()) {
updateCompact(width < 768);
}
updateCompact(width < 768, true);
};
return (
<ConfigConsumer>

View File

@ -87,13 +87,8 @@
white-space: nowrap;
> * {
margin-left: @margin-sm;
white-space: unset;
}
> *:first-child {
margin-left: 0;
}
}
}

View File

@ -3,3 +3,4 @@ import './index.less';
// style dependencies
import '../../breadcrumb/style';
import '../../avatar/style';
import '../../space/style';

View File

@ -13,7 +13,6 @@ import { getRenderPropValue, RenderFunction } from '../_util/getRenderPropValue'
import { cloneElement } from '../_util/reactNode';
import { getTransitionName } from '../_util/motion';
import ActionButton from '../_util/ActionButton';
import useDestroyed from '../_util/hooks/useDestroyed';
export interface PopconfirmProps extends AbstractTooltipProps {
title: React.ReactNode | RenderFunction;
@ -49,15 +48,13 @@ const Popconfirm = React.forwardRef<unknown, PopconfirmProps>((props, ref) => {
defaultValue: props.defaultVisible,
});
const isDestroyed = useDestroyed();
// const isDestroyed = useDestroyed();
const settingVisible = (
value: boolean,
e?: React.MouseEvent<HTMLButtonElement> | React.KeyboardEvent<HTMLDivElement>,
) => {
if (!isDestroyed()) {
setVisible(value);
}
setVisible(value, true);
props.onVisibleChange?.(value, e);
};

View File

@ -37,7 +37,7 @@ Select component to select value from options.
| dropdownMatchSelectWidth | Determine whether the dropdown menu and the select input are the same width. Default set `min-width` same as input. Will ignore when value less than select width. `false` will disable virtual scroll | boolean \| number | true | |
| dropdownRender | Customize dropdown content | (originNode: ReactNode) => ReactNode | - | |
| dropdownStyle | The style of dropdown menu | CSSProperties | - | |
| fieldNames | Customize node title, key, options field name | object | { label: `label`, key: `key`, options: `options` } | 4.17.0 |
| fieldNames | Customize node label, value, options field name | object | { label: `label`, value: `value`, options: `options` } | 4.17.0 |
| filterOption | If true, filter options by input, if function, filter options against it. The function will receive two arguments, `inputValue` and `option`, if the function returns `true`, the option will be included in the filtered set; Otherwise, it will be excluded | boolean \| function(inputValue, option) | true | |
| filterSort | Sort function for search options sorting, see [Array.sort](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/sort)'s compareFunction | (optionA: Option, optionB: Option) => number | - | 4.9.0 |
| getPopupContainer | Parent Node which the selector should be rendered to. Default to `body`. When position issues happen, try to modify it into scrollable content and position it relative. [Example](https://codesandbox.io/s/4j168r7jw0) | function(triggerNode) | () => document.body | |

View File

@ -38,7 +38,7 @@ cover: https://gw.alipayobjects.com/zos/alicdn/_0XzgOis7/Select.svg
| dropdownMatchSelectWidth | 下拉菜单和选择器同宽。默认将设置 `min-width`当值小于选择框宽度时会被忽略。false 时会关闭虚拟滚动 | boolean \| number | true | |
| dropdownRender | 自定义下拉框内容 | (originNode: ReactNode) => ReactNode | - | |
| dropdownStyle | 下拉菜单的 style 属性 | CSSProperties | - | |
| fieldNames | 自定义节点 label、key、options 的字段 | object | { label: `label`, key: `key`, options: `options` } | 4.17.0 |
| fieldNames | 自定义节点 label、value、options 的字段 | object | { label: `label`, value: `value`, options: `options` } | 4.17.0 |
| filterOption | 是否根据输入项进行筛选。当其为一个函数时,会接收 `inputValue` `option` 两个参数,当 `option` 符合筛选条件时,应返回 true反之则返回 false | boolean \| function(inputValue, option) | true | |
| filterSort | 搜索时对筛选结果项的排序函数, 类似[Array.sort](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/sort)里的 compareFunction | (optionA: Option, optionB: Option) => number | - | 4.9.0 |
| getPopupContainer | 菜单渲染父节点。默认渲染到 body 上,如果你遇到菜单滚动定位问题,试试修改为滚动的区域,并相对其定位。[示例](https://codesandbox.io/s/4j168r7jw0) | function(triggerNode) | () => document.body | |

View File

@ -18,7 +18,7 @@ When part of the page is waiting for asynchronous data or during a rendering pro
| delay | Specifies a delay in milliseconds for loading state (prevent flush) | number (milliseconds) | - |
| indicator | React node of the spinning indicator | ReactNode | - |
| size | The size of Spin, options: `small`, `default` and `large` | string | `default` |
| spinning | Whether Spin is spinning | boolean | true |
| spinning | Whether Spin is visible | boolean | true |
| tip | Customize description content when Spin has children | ReactNode | - |
| wrapperClassName | The className of wrapper when Spin has children | string | - |

View File

@ -516,7 +516,7 @@
// Tooltip background color
@tooltip-bg: rgba(0, 0, 0, 0.75);
// Tooltip arrow width
@tooltip-arrow-width: 5px;
@tooltip-arrow-width: 8px * sqrt(2);
// Tooltip distance with trigger
@tooltip-distance: @tooltip-arrow-width - 1px + 4px;
// Tooltip arrow color
@ -532,7 +532,7 @@
@popover-min-width: 177px;
@popover-min-height: 32px;
// Popover arrow width
@popover-arrow-width: 6px;
@popover-arrow-width: @tooltip-arrow-width;
// Popover arrow color
@popover-arrow-color: @popover-bg;
// Popover outer arrow width

View File

@ -62,7 +62,7 @@ const columns = [
| --- | --- | --- | --- | --- |
| bordered | Whether to show all table borders | boolean | false | |
| columns | Columns of table | [ColumnsType](#Column)\[] | - | |
| components | Override default table elements | [TableComponents](https://git.io/fANxz) | - | |
| components | Override default table elements | [TableComponents](https://github.com/react-component/table/blob/75ee0064e54a4b3215694505870c9d6c817e9e4a/src/interface.ts#L129) | - | |
| dataSource | Data record array to be displayed | object\[] | - | |
| expandable | Config expandable content | [expandable](#expandable) | - | |
| footer | Table footer renderer | function(currentPageData) | - | |

View File

@ -69,7 +69,7 @@ const columns = [
| --- | --- | --- | --- | --- |
| bordered | 是否展示外边框和列边框 | boolean | false | |
| columns | 表格列的配置描述,具体项见下表 | [ColumnsType](#Column)\[] | - | |
| components | 覆盖默认的 table 元素 | [TableComponents](https://git.io/fANxz) | - | |
| components | 覆盖默认的 table 元素 | [TableComponents](https://github.com/react-component/table/blob/75ee0064e54a4b3215694505870c9d6c817e9e4a/src/interface.ts#L129) | - | |
| dataSource | 数据数组 | object\[] | - | |
| expandable | 配置展开属性 | [expandable](#expandable) | - | |
| footer | 表格尾部 | function(currentPageData) | - | |

View File

@ -19,6 +19,7 @@
z-index: @zindex-tooltip;
display: block;
width: max-content;
width: intrinsic;
max-width: @tooltip-max-width;
visibility: visible;

View File

@ -79,7 +79,9 @@ export interface TransferProps<RecordType> {
showSearch?: boolean;
filterOption?: (inputValue: string, item: RecordType) => boolean;
locale?: Partial<TransferLocale>;
footer?: (props: TransferListProps<RecordType>) => React.ReactNode;
footer?: (props: TransferListProps<RecordType>, info?: {
direction: TransferDirection;
}) => React.ReactNode;
rowKey?: (record: RecordType) => string;
onSearch?: (direction: TransferDirection, value: string) => void;
onScroll?: (direction: TransferDirection, e: React.SyntheticEvent<HTMLUListElement>) => void;

View File

@ -129,7 +129,7 @@
"rc-dialog": "~8.6.0",
"rc-drawer": "~4.4.2",
"rc-dropdown": "~3.3.2",
"rc-field-form": "~1.23.0",
"rc-field-form": "~1.24.0",
"rc-image": "~5.2.5",
"rc-input": "^0.0.1-alpha.5",
"rc-input-number": "~7.3.0",
@ -154,7 +154,7 @@
"rc-tree-select": "~5.1.1",
"rc-trigger": "^5.2.10",
"rc-upload": "~4.3.0",
"rc-util": "^5.14.0",
"rc-util": "^5.19.3",
"scroll-into-view-if-needed": "^2.2.25"
},
"devDependencies": {
@ -166,7 +166,7 @@
"@qixian.cs/github-contributors-list": "^1.0.3",
"@stackblitz/sdk": "^1.3.0",
"@testing-library/jest-dom": "^5.16.2",
"@testing-library/react": "^12.1.3",
"@testing-library/react": "^12.1.4",
"@types/enzyme": "^3.10.5",
"@types/gtag.js": "^0.0.8",
"@types/jest": "^27.0.0",
@ -230,7 +230,7 @@
"intersection-observer": "^0.12.0",
"isomorphic-fetch": "^3.0.0",
"jest": "^27.0.3",
"jest-axe": "^5.0.1",
"jest-axe": "^6.0.0",
"jest-environment-node": "^27.4.4",
"jest-image-snapshot": "^4.5.1",
"jest-puppeteer": "^6.0.0",