mirror of
https://github.com/ant-design/ant-design.git
synced 2026-02-14 05:19:20 +08:00
Compare commits
8 Commits
typography
...
feature
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
cd3f333892 | ||
|
|
c9a727c599 | ||
|
|
2c2773a8cc | ||
|
|
6915342818 | ||
|
|
956c1d1b51 | ||
|
|
4490fc6a4e | ||
|
|
6e993d2ef8 | ||
|
|
a46cd9d8ba |
@@ -255,7 +255,7 @@ function antdCodeAppend(docFileAbs: string, src: string): string {
|
||||
*
|
||||
* @param md - 原始 markdown 内容
|
||||
* @param docFileAbs - 文档文件的绝对路径,用于解析相对路径和检测语言
|
||||
* @param enablePickLocaleBlock - 是否启用多语言块提取,可以是布尔值或函数,默认为 true
|
||||
* @param codeAppend - 代码追加函数:在替换 <code src> 标签时,用于追加额外的内容(如 demo 描述信息)
|
||||
* @returns 替换后的 markdown 内容
|
||||
*/
|
||||
function replaceCodeSrcToMarkdown(
|
||||
@@ -556,7 +556,6 @@ function emitRawMd(api: IApi) {
|
||||
* 2. 在 HTML 文件导出阶段输出处理后的 raw markdown 文件
|
||||
*
|
||||
* @param api - Dumi API 实例
|
||||
* @param options - 插件配置选项
|
||||
*/
|
||||
export default function rawMdPlugin(api: IApi) {
|
||||
// 注册配置键,允许用户在配置中使用 rawMd 键
|
||||
|
||||
@@ -19,8 +19,8 @@ function extractSemantics(objContent: string): Record<string, string> {
|
||||
}
|
||||
|
||||
/**
|
||||
* 从 _semantic*.tsx 文件中提取语义信息
|
||||
* @param semanticFile - _semantic*.tsx 文件的绝对路径
|
||||
* 从 _semantic*.tsx 文件内容中提取语义信息
|
||||
* @param content - _semantic*.tsx 文件的文件内容字符串
|
||||
* @returns 包含中文和英文语义描述的对象,失败返回 null
|
||||
*/
|
||||
function extractLocaleInfoFromContent(content: string): {
|
||||
|
||||
12
components/_util/fallbackProp.ts
Normal file
12
components/_util/fallbackProp.ts
Normal file
@@ -0,0 +1,12 @@
|
||||
/**
|
||||
* Search for the first non-undefined value in the arguments and return it.
|
||||
*
|
||||
* ```js
|
||||
* const mergedIcon = fallbackProp(propIcon, contextIcon, defaultIcon);
|
||||
* ```
|
||||
*
|
||||
* Note: it is different from `??` operator which skips null
|
||||
*/
|
||||
export default function fallbackProp<T>(...args: T[]): T | undefined {
|
||||
return args.find((arg) => arg !== undefined);
|
||||
}
|
||||
@@ -907,4 +907,115 @@ describe('Cascader', () => {
|
||||
expect(screen.getAllByText('bamboo').length).toBe(1);
|
||||
});
|
||||
});
|
||||
|
||||
describe('clearIcon', () => {
|
||||
it('should support custom clearIcon', () => {
|
||||
render(
|
||||
<Cascader
|
||||
open
|
||||
allowClear={{ clearIcon: <div>bamboo</div> }}
|
||||
options={options}
|
||||
defaultValue={['zhejiang', 'hangzhou']}
|
||||
/>,
|
||||
);
|
||||
expect(screen.getAllByText('bamboo').length).toBe(1);
|
||||
});
|
||||
|
||||
it('should support ConfigProvider clearIcon', () => {
|
||||
render(
|
||||
<ConfigProvider cascader={{ clearIcon: <div>foobar</div> }}>
|
||||
<Cascader options={options} defaultValue={['zhejiang', 'hangzhou']} allowClear />
|
||||
</ConfigProvider>,
|
||||
);
|
||||
expect(screen.getAllByText('foobar').length).toBe(1);
|
||||
});
|
||||
|
||||
it('should prefer prop clearIcon over ConfigProvider clearIcon', () => {
|
||||
render(
|
||||
<ConfigProvider cascader={{ clearIcon: <div>foobar</div> }}>
|
||||
<Cascader
|
||||
allowClear={{ clearIcon: <div>bamboo</div> }}
|
||||
options={options}
|
||||
defaultValue={['zhejiang', 'hangzhou']}
|
||||
/>
|
||||
</ConfigProvider>,
|
||||
);
|
||||
expect(screen.getAllByText('bamboo').length).toBe(1);
|
||||
});
|
||||
});
|
||||
|
||||
describe('removeIcon', () => {
|
||||
it('should support custom removeIcon', () => {
|
||||
render(
|
||||
<Cascader
|
||||
multiple
|
||||
removeIcon={<div>bamboo</div>}
|
||||
options={options}
|
||||
defaultValue={[
|
||||
['zhejiang', 'hangzhou'],
|
||||
['jiangsu', 'nanjing'],
|
||||
]}
|
||||
/>,
|
||||
);
|
||||
expect(screen.getAllByText('bamboo').length).toBe(2);
|
||||
});
|
||||
|
||||
it('should support ConfigProvider removeIcon', () => {
|
||||
render(
|
||||
<ConfigProvider cascader={{ removeIcon: <div>foobar</div> }}>
|
||||
<Cascader
|
||||
multiple
|
||||
options={options}
|
||||
defaultValue={[
|
||||
['zhejiang', 'hangzhou'],
|
||||
['jiangsu', 'nanjing'],
|
||||
]}
|
||||
/>
|
||||
</ConfigProvider>,
|
||||
);
|
||||
expect(screen.getAllByText('foobar').length).toBe(2);
|
||||
});
|
||||
|
||||
it('should prefer prop removeIcon over ConfigProvider removeIcon', () => {
|
||||
render(
|
||||
<ConfigProvider cascader={{ removeIcon: <div>foobar</div> }}>
|
||||
<Cascader
|
||||
multiple
|
||||
options={options}
|
||||
defaultValue={[
|
||||
['zhejiang', 'hangzhou'],
|
||||
['jiangsu', 'nanjing'],
|
||||
]}
|
||||
removeIcon={<div>bamboo</div>}
|
||||
/>
|
||||
</ConfigProvider>,
|
||||
);
|
||||
expect(screen.getAllByText('bamboo').length).toBe(2);
|
||||
});
|
||||
});
|
||||
|
||||
describe('searchIcon', () => {
|
||||
it('should support custom searchIcon', () => {
|
||||
render(<Cascader open showSearch={{ searchIcon: <div>bamboo</div> }} options={options} />);
|
||||
expect(screen.getAllByText('bamboo').length).toBe(1);
|
||||
});
|
||||
|
||||
it('should support ConfigProvider searchIcon', () => {
|
||||
render(
|
||||
<ConfigProvider cascader={{ searchIcon: <div>foobar</div> }}>
|
||||
<Cascader open options={options} showSearch />
|
||||
</ConfigProvider>,
|
||||
);
|
||||
expect(screen.getAllByText('foobar').length).toBe(1);
|
||||
});
|
||||
|
||||
it('should prefer prop searchIcon over ConfigProvider searchIcon', () => {
|
||||
render(
|
||||
<ConfigProvider cascader={{ searchIcon: <div>foobar</div> }}>
|
||||
<Cascader open showSearch={{ searchIcon: <div>bamboo</div> }} options={options} />
|
||||
</ConfigProvider>,
|
||||
);
|
||||
expect(screen.getAllByText('bamboo').length).toBe(1);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -111,6 +111,7 @@ Common props ref:[Common props](/docs/react/common-props)
|
||||
| sort | Used to sort filtered options | function(a, b, inputValue) | - | |
|
||||
| searchValue | Set search value, Need work with `showSearch` | string | - | 4.17.0 |
|
||||
| onSearch | The callback function triggered when input changed | (search: string) => void | - | 4.17.0 |
|
||||
| searchIcon | Customize the search icon | ReactNode | - | 6.3.0 |
|
||||
|
||||
### Option
|
||||
|
||||
|
||||
@@ -166,6 +166,9 @@ export interface CascaderProps<
|
||||
bordered?: boolean;
|
||||
placement?: SelectCommonPlacement;
|
||||
suffixIcon?: React.ReactNode;
|
||||
showSearch?:
|
||||
| boolean
|
||||
| (SearchConfig<OptionType, keyof OptionType> & { searchIcon?: React.ReactNode });
|
||||
options?: OptionType[];
|
||||
status?: InputStatus;
|
||||
|
||||
@@ -244,11 +247,12 @@ const Cascader = React.forwardRef<CascaderRef, CascaderProps<any>>((props, ref)
|
||||
styles,
|
||||
classNames,
|
||||
loadingIcon,
|
||||
...rest
|
||||
clearIcon,
|
||||
removeIcon,
|
||||
suffixIcon,
|
||||
...restProps
|
||||
} = props;
|
||||
|
||||
const restProps = omit(rest, ['suffixIcon']);
|
||||
|
||||
const {
|
||||
getPrefixCls,
|
||||
getPopupContainer: getContextPopupContainer,
|
||||
@@ -258,6 +262,10 @@ const Cascader = React.forwardRef<CascaderRef, CascaderProps<any>>((props, ref)
|
||||
styles: contextStyles,
|
||||
expandIcon: contextExpandIcon,
|
||||
loadingIcon: contextLoadingIcon,
|
||||
clearIcon: contextClearIcon,
|
||||
removeIcon: contextRemoveIcon,
|
||||
suffixIcon: contextSuffixIcon,
|
||||
searchIcon: contextSearchIcon,
|
||||
} = useComponentConfig('cascader');
|
||||
|
||||
const { popupOverflow } = React.useContext(ConfigContext);
|
||||
@@ -367,9 +375,21 @@ const Cascader = React.forwardRef<CascaderRef, CascaderProps<any>>((props, ref)
|
||||
|
||||
// ===================== Icons =====================
|
||||
const showSuffixIcon = useShowArrow(props.suffixIcon, showArrow);
|
||||
const { suffixIcon, removeIcon, clearIcon } = useSelectIcons({
|
||||
const {
|
||||
suffixIcon: mergedSuffixIcon,
|
||||
removeIcon: mergedRemoveIcon,
|
||||
clearIcon: mergedClearIcon,
|
||||
} = useSelectIcons({
|
||||
...props,
|
||||
clearIcon,
|
||||
contextClearIcon,
|
||||
removeIcon,
|
||||
contextRemoveIcon,
|
||||
loadingIcon: mergedLoadingIcon,
|
||||
suffixIcon,
|
||||
contextSuffixIcon,
|
||||
searchIcon: typeof showSearch === 'object' && showSearch ? showSearch.searchIcon : undefined,
|
||||
contextSearchIcon,
|
||||
hasFeedback,
|
||||
feedbackIcon,
|
||||
showSuffixIcon,
|
||||
@@ -386,7 +406,7 @@ const Cascader = React.forwardRef<CascaderRef, CascaderProps<any>>((props, ref)
|
||||
return isRtl ? 'bottomRight' : 'bottomLeft';
|
||||
}, [placement, isRtl]);
|
||||
|
||||
const mergedAllowClear = allowClear === true ? { clearIcon } : allowClear;
|
||||
const mergedAllowClear = allowClear === true ? { clearIcon: mergedClearIcon } : allowClear;
|
||||
|
||||
// =========== Merged Props for Semantic ==========
|
||||
const mergedProps: CascaderProps<any> = {
|
||||
@@ -468,8 +488,8 @@ const Cascader = React.forwardRef<CascaderRef, CascaderProps<any>>((props, ref)
|
||||
allowClear={mergedAllowClear}
|
||||
showSearch={mergedShowSearch}
|
||||
expandIcon={mergedExpandIcon}
|
||||
suffixIcon={suffixIcon}
|
||||
removeIcon={removeIcon}
|
||||
suffixIcon={mergedSuffixIcon}
|
||||
removeIcon={mergedRemoveIcon}
|
||||
loadingIcon={mergedLoadingIcon}
|
||||
checkable={checkable}
|
||||
popupClassName={mergedPopupClassName}
|
||||
|
||||
@@ -114,6 +114,7 @@ demo:
|
||||
| sort | 用于排序 filter 后的选项 | function(a, b, inputValue) | - | |
|
||||
| searchValue | 设置搜索的值,需要与 `showSearch` 配合使用 | string | - | 4.17.0 |
|
||||
| onSearch | 监听搜索,返回输入的值 | (search: string) => void | - | 4.17.0 |
|
||||
| searchIcon | 自定义的搜索图标 | ReactNode | - | 6.3.0 |
|
||||
|
||||
### Option
|
||||
|
||||
|
||||
@@ -229,6 +229,34 @@ describe('ConfigProvider.Form', () => {
|
||||
});
|
||||
});
|
||||
|
||||
describe('form labelAlign', () => {
|
||||
it('set labelAlign left', () => {
|
||||
const { container } = render(
|
||||
<ConfigProvider form={{ labelAlign: 'left' }}>
|
||||
<Form>
|
||||
<Form.Item label="姓名">
|
||||
<input />
|
||||
</Form.Item>
|
||||
</Form>
|
||||
</ConfigProvider>,
|
||||
);
|
||||
expect(container.querySelector('.ant-form-item-label-left')).toBeTruthy();
|
||||
});
|
||||
|
||||
it('form labelAlign should override ConfigProvider labelAlign', () => {
|
||||
const { container } = render(
|
||||
<ConfigProvider form={{ labelAlign: 'left' }}>
|
||||
<Form labelAlign="right">
|
||||
<Form.Item label="姓名">
|
||||
<input />
|
||||
</Form.Item>
|
||||
</Form>
|
||||
</ConfigProvider>,
|
||||
);
|
||||
expect(container.querySelector('.ant-form-item-label-left')).toBeFalsy();
|
||||
});
|
||||
});
|
||||
|
||||
describe('form disabled', () => {
|
||||
it('set Input enabled', () => {
|
||||
const { container } = render(
|
||||
|
||||
@@ -302,6 +302,7 @@ export type FormConfig = ComponentStyleConfig &
|
||||
| 'classNames'
|
||||
| 'styles'
|
||||
| 'tooltip'
|
||||
| 'labelAlign'
|
||||
>;
|
||||
|
||||
export type FloatButtonConfig = ComponentStyleConfig &
|
||||
@@ -372,7 +373,10 @@ export type InputNumberConfig = ComponentStyleConfig &
|
||||
Pick<InputNumberProps, 'variant' | 'classNames' | 'styles'>;
|
||||
|
||||
export type CascaderConfig = ComponentStyleConfig &
|
||||
Pick<CascaderProps, 'variant' | 'styles' | 'classNames' | 'expandIcon' | 'loadingIcon'>;
|
||||
Pick<
|
||||
CascaderProps,
|
||||
'variant' | 'styles' | 'classNames' | 'expandIcon' | 'loadingIcon' | 'removeIcon' | 'suffixIcon'
|
||||
> & { clearIcon?: React.ReactNode; searchIcon?: React.ReactNode };
|
||||
|
||||
export type TreeSelectConfig = ComponentStyleConfig &
|
||||
Pick<TreeSelectProps, 'variant' | 'classNames' | 'styles' | 'switcherIcon'>;
|
||||
|
||||
@@ -119,7 +119,7 @@ const {
|
||||
| cardMeta | Set Card.Meta common props | { className?: string, style?: React.CSSProperties, classNames?: [CardMetaProps\["classNames"\]](/components/card#semantic-dom), styles?: [CardMetaProps\["styles"\]](/components/card#semantic-dom) } | - | 6.0.0 |
|
||||
| calendar | Set Calendar common props | { className?: string, style?: React.CSSProperties, classNames?: [CalendarConfig\["classNames"\]](/components/calendar#semantic-dom), styles?: [CalendarConfig\["styles"\]](/components/calendar#semantic-dom) } | - | 5.7.0, `classNames` and `styles`: 6.0.0 |
|
||||
| carousel | Set Carousel common props | { className?: string, style?: React.CSSProperties } | - | 5.7.0 |
|
||||
| cascader | Set Cascader common props | { className?: string, style?: React.CSSProperties, classNames?: [CascaderConfig\["classNames"\]](/components/cascader#semantic-dom), styles?: [CascaderConfig\["styles"\]](/components/cascader#semantic-dom), expandIcon?: React.ReactNode, loadingIcon?: React.ReactNode } | - | 5.7.0, `classNames` and `styles`: 6.0.0, `expandIcon` and `loadingIcon`: 6.3.0 |
|
||||
| cascader | Set Cascader common props | { className?: string, style?: React.CSSProperties, classNames?: [CascaderConfig\["classNames"\]](/components/cascader#semantic-dom), styles?: [CascaderConfig\["styles"\]](/components/cascader#semantic-dom), expandIcon?: React.ReactNode, loadingIcon?: React.ReactNode, searchIcon?: React.ReactNode, clearIcon?: React.ReactNode, removeIcon?: React.ReactNode, suffixIcon?: React.ReactNode } | - | 5.7.0, `classNames` and `styles`: 6.0.0, `expandIcon`, `loadingIcon`, `searchIcon`, `clearIcon`, `removeIcon`, `suffixIcon`: 6.4.0 |
|
||||
| checkbox | Set Checkbox common props | { className?: string, style?: React.CSSProperties, classNames?: [CheckboxConfig\["classNames"\]](/components/checkbox#semantic-dom), styles?: [CheckboxConfig\["styles"\]](/components/checkbox#semantic-dom) } | - | 5.7.0, `classNames` and `styles`: 6.0.0 |
|
||||
| collapse | Set Collapse common props | { className?: string, style?: React.CSSProperties, expandIcon?: (props) => ReactNode, classNames?: [CollapseProps\["classNames"\]](/components/collapse#semantic-dom), styles?: [CollapseProps\["styles"\]](/components/collapse#semantic-dom) } | - | 5.7.0, `expandIcon`: 5.15.0, `classNames` and `styles`: 6.0.0 |
|
||||
| colorPicker | Set ColorPicker common props | { className?: string, style?: React.CSSProperties, classNames?: [ColorPickerConfig\["classNames"\]](/components/color-picker#semantic-dom), styles?: [ColorPickerConfig\["styles"\]](/components/color-picker#semantic-dom) } | - | 5.7.0 |
|
||||
@@ -133,7 +133,7 @@ const {
|
||||
| flex | Set Flex common props | { className?: string, style?: React.CSSProperties, vertical?: boolean } | - | 5.10.0 |
|
||||
| floatButton | Set FloatButton common props | { className?: string, style?: React.CSSProperties, classNames?: [FloatButtonProps\["classNames"\]](/components/float-button#semantic-dom), styles?: [FloatButtonProps\["styles"\]](/components/float-button#semantic-dom), backTopIcon?: React.ReactNode } | - | |
|
||||
| floatButtonGroup | Set FloatButton.Group common props | { closeIcon?: React.ReactNode, className?: string, style?: React.CSSProperties, classNames?: [FloatButtonProps\["classNames"\]](/components/float-button#semantic-dom), styles?: [FloatButtonProps\["styles"\]](/components/float-button#semantic-dom) } | - | |
|
||||
| form | Set Form common props | { className?: string, style?: React.CSSProperties, validateMessages?: [ValidateMessages](/components/form/#validatemessages), requiredMark?: boolean \| `optional`, scrollToFirstError?: boolean \| [Options](https://github.com/stipsan/scroll-into-view-if-needed/tree/ece40bd9143f48caf4b99503425ecb16b0ad8249#options), classNames?:[FormConfig\["classNames"\]](/components/form#semantic-dom), styles?: [FormConfig\["styles"\]](/components/form#semantic-dom), tooltip?: [TooltipProps](/components/tooltip#api) & { icon?: ReactNode } } | - | `requiredMark`: 4.8.0; `colon`: 4.18.0; `scrollToFirstError`: 5.2.0; `className` and `style`: 5.7.0; `tooltip`: 6.3.0 |
|
||||
| form | Set Form common props | { className?: string, style?: React.CSSProperties, validateMessages?: [ValidateMessages](/components/form/#validatemessages), requiredMark?: boolean \| `optional`, scrollToFirstError?: boolean \| [Options](https://github.com/stipsan/scroll-into-view-if-needed/tree/ece40bd9143f48caf4b99503425ecb16b0ad8249#options), classNames?:[FormConfig\["classNames"\]](/components/form#semantic-dom), styles?: [FormConfig\["styles"\]](/components/form#semantic-dom), tooltip?: [TooltipProps](/components/tooltip#api) & { icon?: ReactNode }, labelAlign?: `left` \| `right` } | - | `requiredMark`: 4.8.0; `colon`: 4.18.0; `scrollToFirstError`: 5.2.0; `className` and `style`: 5.7.0; `tooltip`: 6.3.0; `labelAlign`: 6.4.0 |
|
||||
| image | Set Image common props | { className?: string, style?: React.CSSProperties, preview?: { closeIcon?: React.ReactNode, classNames?:[ImageConfig\["classNames"\]](/components/image#semantic-dom), styles?: [ImageConfig\["styles"\]](/components/image#semantic-dom) }, fallback?: string } | - | 5.7.0, `closeIcon`: 5.14.0, `classNames` and `styles`: 6.0.0 |
|
||||
| input | Set Input common props | { autoComplete?: string, className?: string, style?: React.CSSProperties, allowClear?: boolean \| { clearIcon?: ReactNode } } | - | 4.2.0, `allowClear`: 5.15.0 |
|
||||
| inputNumber | Set InputNumber common props | { className?: string, style?: React.CSSProperties, classNames?: [InputNumberConfig\["classNames"\]](/components/input-number#semantic-dom), styles?: [InputNumberConfig\["styles"\]](/components/input-number#semantic-dom) } | - | |
|
||||
|
||||
@@ -121,7 +121,7 @@ const {
|
||||
| card | 设置 Card 组件的通用属性 | { className?: string, style?: React.CSSProperties, classNames?: [CardProps\["classNames"\]](/components/card-cn#semantic-dom), styles?: [CardProps\["styles"\]](/components/card-cn#semantic-dom) } | - | 5.7.0, `classNames` 和 `styles`: 5.14.0 |
|
||||
| cardMeta | 设置 Card.Meta 组件的通用属性 | { className?: string, style?: React.CSSProperties, classNames?: [CardMetaProps\["classNames"\]](/components/card-cn#semantic-dom), styles?: [CardMetaProps\["styles"\]](/components/card-cn#semantic-dom) } | - | 6.0.0 |
|
||||
| carousel | 设置 Carousel 组件的通用属性 | { className?: string, style?: React.CSSProperties } | - | 5.7.0 |
|
||||
| cascader | 设置 Cascader 组件的通用属性 | { className?: string, style?: React.CSSProperties, classNames?: [CascaderConfig\["classNames"\]](/components/cascader#semantic-dom), styles?: [CascaderConfig\["styles"\]](/components/cascader#semantic-dom), expandIcon?: React.ReactNode, loadingIcon?: React.ReactNode } | - | 5.7.0, `classNames` 和 `styles`: 6.0.0, `expandIcon` 和 `loadingIcon`: 6.3.0 |
|
||||
| cascader | 设置 Cascader 组件的通用属性 | { className?: string, style?: React.CSSProperties, classNames?: [CascaderConfig\["classNames"\]](/components/cascader#semantic-dom), styles?: [CascaderConfig\["styles"\]](/components/cascader#semantic-dom), expandIcon?: React.ReactNode, loadingIcon?: React.ReactNode, searchIcon?: React.ReactNode, clearIcon?: React.ReactNode, removeIcon?: React.ReactNode, suffixIcon?: React.ReactNode } | - | 5.7.0, `classNames` 和 `styles`: 6.0.0, `expandIcon`, `loadingIcon`, `searchIcon`, `clearIcon`, `removeIcon`, `suffixIcon`: 6.4.0 |
|
||||
| checkbox | 设置 Checkbox 组件的通用属性 | { className?: string, style?: React.CSSProperties, classNames?: [CheckboxConfig\["classNames"\]](/components/checkbox-cn#semantic-dom), styles?: [CheckboxConfig\["styles"\]](/components/checkbox-cn#semantic-dom) } | - | 5.7.0, `classNames` 和 `styles`: 6.0.0 |
|
||||
| collapse | 设置 Collapse 组件的通用属性 | { className?: string, style?: React.CSSProperties, expandIcon?: (props) => ReactNode, classNames?: [CollapseProps\["classNames"\]](/components/collapse-cn#semantic-dom), styles?: [CollapseProps\["styles"\]](/components/collapse-cn#semantic-dom) } | - | 5.7.0, `expandIcon`: 5.15.0, `classNames` 和 `styles`: 6.0.0 |
|
||||
| colorPicker | 设置 ColorPicker 组件的通用属性 | { className?: string, style?: React.CSSProperties, classNames?: [ColorPickerConfig\["classNames"\]](/components/color-picker-cn#semantic-dom), styles?: [ColorPickerConfig\["styles"\]](/components/color-picker-cn#semantic-dom) } | - | 5.7.0 |
|
||||
@@ -135,7 +135,7 @@ const {
|
||||
| flex | 设置 Flex 组件的通用属性 | { className?: string, style?: React.CSSProperties, vertical?: boolean } | - | 5.10.0 |
|
||||
| floatButton | 设置 FloatButton 组件的通用属性 | { className?: string, style?: React.CSSProperties, classNames?: [FloatButtonProps\["classNames"\]](/components/float-button-cn#semantic-dom), styles?: [FloatButtonProps\["styles"\]](/components/float-button-cn#semantic-dom), backTopIcon?: React.ReactNode } | - | |
|
||||
| floatButtonGroup | 设置 FloatButton.Group 组件的通用属性 | { closeIcon?: React.ReactNode, className?: string, style?: React.CSSProperties, classNames?: [FloatButtonProps\["classNames"\]](/components/float-button-cn#semantic-dom), styles?: [FloatButtonProps\["styles"\]](/components/float-button-cn#semantic-dom) } | - | |
|
||||
| form | 设置 Form 组件的通用属性 | { className?: string, style?: React.CSSProperties, validateMessages?: [ValidateMessages](/components/form-cn#validatemessages), requiredMark?: boolean \| `optional`, colon?: boolean, scrollToFirstError?: boolean \| [Options](https://github.com/stipsan/scroll-into-view-if-needed/tree/ece40bd9143f48caf4b99503425ecb16b0ad8249#options), classNames?:[FormConfig\["classNames"\]](/components/form-cn#semantic-dom), styles?: [FormConfig\["styles"\]](/components/form-cn#semantic-dom), tooltip?: [TooltipProps](/components/tooltip-cn#api) & { icon?: ReactNode } } | - | `requiredMark`: 4.8.0; `colon`: 4.18.0; `scrollToFirstError`: 5.2.0; `className` 和 `style`: 5.7.0; `tooltip`: 6.3.0 |
|
||||
| form | 设置 Form 组件的通用属性 | { className?: string, style?: React.CSSProperties, validateMessages?: [ValidateMessages](/components/form-cn#validatemessages), requiredMark?: boolean \| `optional`, colon?: boolean, scrollToFirstError?: boolean \| [Options](https://github.com/stipsan/scroll-into-view-if-needed/tree/ece40bd9143f48caf4b99503425ecb16b0ad8249#options), classNames?:[FormConfig\["classNames"\]](/components/form-cn#semantic-dom), styles?: [FormConfig\["styles"\]](/components/form-cn#semantic-dom), tooltip?: [TooltipProps](/components/tooltip-cn#api) & { icon?: ReactNode }, labelAlign?: `left` \| `right` } | - | `requiredMark`: 4.8.0; `colon`: 4.18.0; `scrollToFirstError`: 5.2.0; `className` 和 `style`: 5.7.0; `tooltip`: 6.3.0; `labelAlign`: 6.4.0 |
|
||||
| image | 设置 Image 组件的通用属性 | { className?: string, style?: React.CSSProperties, preview?: { closeIcon?: React.ReactNode, classNames?:[ImageConfig\["classNames"\]](/components/image-cn#semantic-dom), styles?: [ImageConfig\["styles"\]](/components/image-cn#semantic-dom) }, fallback?: string } | - | 5.7.0, `closeIcon`: 5.14.0, `classNames` 和 `styles`: 6.0.0 |
|
||||
| input | 设置 Input 组件的通用属性 | { autoComplete?: string, className?: string, style?: React.CSSProperties,classNames?:[InputConfig\["classNames"\]](/components/input-cn#semantic-input), styles?: [InputConfig\["styles"\]](/components/input-cn#semantic-input), allowClear?: boolean \| { clearIcon?: ReactNode } } | - | 5.7.0, `allowClear`: 5.15.0 |
|
||||
| inputNumber | 设置 Input 组件的通用属性 | { className?: string, style?: React.CSSProperties, classNames?: [InputNumberConfig\["classNames"\]](/components/input-number-cn#semantic-dom), styles?: [InputNumberConfig\["styles"\]](/components/input-number-cn#semantic-dom) } | - | |
|
||||
|
||||
@@ -91,6 +91,7 @@ const InternalForm: React.ForwardRefRenderFunction<FormRef, FormProps> = (props,
|
||||
styles: contextStyles,
|
||||
classNames: contextClassNames,
|
||||
tooltip: contextTooltip,
|
||||
labelAlign: contextLabelAlign,
|
||||
} = useComponentConfig('form');
|
||||
|
||||
const {
|
||||
@@ -144,6 +145,8 @@ const InternalForm: React.ForwardRefRenderFunction<FormRef, FormProps> = (props,
|
||||
|
||||
const mergedColon = colon ?? contextColon;
|
||||
|
||||
const mergedLabelAlign = labelAlign ?? contextLabelAlign;
|
||||
|
||||
const mergedTooltip = { ...contextTooltip, ...tooltip };
|
||||
|
||||
const prefixCls = getPrefixCls('form', customizePrefixCls);
|
||||
@@ -194,7 +197,7 @@ const InternalForm: React.ForwardRefRenderFunction<FormRef, FormProps> = (props,
|
||||
const formContextValue = React.useMemo<FormContextProps>(
|
||||
() => ({
|
||||
name,
|
||||
labelAlign,
|
||||
labelAlign: mergedLabelAlign,
|
||||
labelCol,
|
||||
labelWrap,
|
||||
wrapperCol,
|
||||
@@ -210,7 +213,7 @@ const InternalForm: React.ForwardRefRenderFunction<FormRef, FormProps> = (props,
|
||||
}),
|
||||
[
|
||||
name,
|
||||
labelAlign,
|
||||
mergedLabelAlign,
|
||||
labelCol,
|
||||
wrapperCol,
|
||||
layout,
|
||||
|
||||
@@ -3146,7 +3146,7 @@ Array [
|
||||
style="width: 100px;"
|
||||
>
|
||||
<div
|
||||
class="ant-select-content ant-select-content-has-value"
|
||||
class="ant-select-content"
|
||||
title=""
|
||||
>
|
||||
<input
|
||||
|
||||
@@ -684,7 +684,7 @@ Array [
|
||||
style="width:100px"
|
||||
>
|
||||
<div
|
||||
class="ant-select-content ant-select-content-has-value"
|
||||
class="ant-select-content"
|
||||
title=""
|
||||
>
|
||||
<input
|
||||
|
||||
@@ -1778,7 +1778,9 @@ Array [
|
||||
exports[`renders components/select/demo/custom-tag-render.tsx extend context correctly 2`] = `[]`;
|
||||
|
||||
exports[`renders components/select/demo/debug.tsx extend context correctly 1`] = `
|
||||
Array [
|
||||
<div
|
||||
class="ant-flex css-var-test-id ant-flex-align-stretch ant-flex-gap-middle ant-flex-vertical"
|
||||
>
|
||||
<div
|
||||
class="ant-space ant-space-horizontal ant-space-align-center ant-space-gap-row-small ant-space-gap-col-small css-var-test-id"
|
||||
style="flex-wrap: wrap; width: 500px; position: relative; z-index: 1; border: 1px solid red; background-color: rgb(255, 255, 255);"
|
||||
@@ -2210,9 +2212,9 @@ Array [
|
||||
</span>
|
||||
</button>
|
||||
</div>
|
||||
</div>,
|
||||
</div>
|
||||
<div
|
||||
style="width: 200px; margin-top: 24px;"
|
||||
style="width: 200px;"
|
||||
>
|
||||
<div
|
||||
class="ant-select ant-select-outlined css-var-test-id ant-select-css-var ant-select-multiple ant-select-show-arrow ant-select-show-search"
|
||||
@@ -2379,8 +2381,113 @@ Array [
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>,
|
||||
]
|
||||
</div>
|
||||
<div
|
||||
class="ant-select ant-select-outlined css-var-test-id ant-select-css-var ant-select-single ant-select-show-arrow"
|
||||
>
|
||||
<div
|
||||
class="ant-select-content"
|
||||
title=""
|
||||
>
|
||||
<input
|
||||
aria-autocomplete="list"
|
||||
aria-expanded="false"
|
||||
aria-haspopup="listbox"
|
||||
autocomplete="off"
|
||||
class="ant-select-input"
|
||||
id="test-id"
|
||||
readonly=""
|
||||
role="combobox"
|
||||
type="search"
|
||||
value=""
|
||||
/>
|
||||
</div>
|
||||
<div
|
||||
class="ant-select-suffix"
|
||||
>
|
||||
<span
|
||||
aria-label="down"
|
||||
class="anticon anticon-down"
|
||||
role="img"
|
||||
>
|
||||
<svg
|
||||
aria-hidden="true"
|
||||
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>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
class="ant-select-dropdown ant-slide-up-appear ant-slide-up-appear-prepare ant-slide-up css-var-test-id ant-select-css-var ant-select-dropdown-empty ant-select-dropdown-placement-bottomLeft"
|
||||
style="--arrow-x: 0px; --arrow-y: 0px; left: -1000vw; top: -1000vh; right: auto; bottom: auto; box-sizing: border-box;"
|
||||
>
|
||||
<div>
|
||||
<div
|
||||
class="ant-select-item-empty"
|
||||
id="test-id_list"
|
||||
role="listbox"
|
||||
>
|
||||
<div
|
||||
class="css-var-test-id ant-empty ant-empty-normal ant-empty-small"
|
||||
>
|
||||
<div
|
||||
class="ant-empty-image"
|
||||
>
|
||||
<svg
|
||||
height="41"
|
||||
viewBox="0 0 64 41"
|
||||
width="64"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<title>
|
||||
No data
|
||||
</title>
|
||||
<g
|
||||
fill="none"
|
||||
fill-rule="evenodd"
|
||||
transform="translate(0 1)"
|
||||
>
|
||||
<ellipse
|
||||
cx="32"
|
||||
cy="33"
|
||||
fill="#f5f5f5"
|
||||
rx="32"
|
||||
ry="7"
|
||||
/>
|
||||
<g
|
||||
fill-rule="nonzero"
|
||||
stroke="#d9d9d9"
|
||||
>
|
||||
<path
|
||||
d="M55 12.8 44.9 1.3Q44 0 42.9 0H21.1q-1.2 0-2 1.3L9 12.8V22h46z"
|
||||
/>
|
||||
<path
|
||||
d="M41.6 16c0-1.7 1-3 2.2-3H55v18.1c0 2.2-1.3 3.9-3 3.9H12c-1.7 0-3-1.7-3-3.9V13h11.2c1.2 0 2.2 1.3 2.2 3s1 2.9 2.2 2.9h14.8c1.2 0 2.2-1.4 2.2-3"
|
||||
fill="#fafafa"
|
||||
/>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
||||
</div>
|
||||
<div
|
||||
class="ant-empty-description"
|
||||
>
|
||||
No data
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
|
||||
exports[`renders components/select/demo/debug.tsx extend context correctly 2`] = `[]`;
|
||||
|
||||
@@ -741,7 +741,9 @@ exports[`renders components/select/demo/custom-tag-render.tsx correctly 1`] = `
|
||||
`;
|
||||
|
||||
exports[`renders components/select/demo/debug.tsx correctly 1`] = `
|
||||
Array [
|
||||
<div
|
||||
class="ant-flex css-var-test-id ant-flex-align-stretch ant-flex-gap-middle ant-flex-vertical"
|
||||
>
|
||||
<div
|
||||
class="ant-space ant-space-horizontal ant-space-align-center ant-space-gap-row-small ant-space-gap-col-small css-var-test-id"
|
||||
style="flex-wrap:wrap;width:500px;position:relative;z-index:1;border:1px solid red;background-color:#fff"
|
||||
@@ -924,9 +926,9 @@ Array [
|
||||
</span>
|
||||
</button>
|
||||
</div>
|
||||
</div>,
|
||||
</div>
|
||||
<div
|
||||
style="width:200px;margin-top:24px"
|
||||
style="width:200px"
|
||||
>
|
||||
<div
|
||||
class="ant-select ant-select-outlined css-var-test-id ant-select-css-var ant-select-multiple ant-select-show-arrow ant-select-show-search"
|
||||
@@ -1018,8 +1020,52 @@ Array [
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>,
|
||||
]
|
||||
</div>
|
||||
<div
|
||||
class="ant-select ant-select-outlined css-var-test-id ant-select-css-var ant-select-single ant-select-show-arrow"
|
||||
>
|
||||
<div
|
||||
class="ant-select-content"
|
||||
title=""
|
||||
>
|
||||
<input
|
||||
aria-autocomplete="list"
|
||||
aria-expanded="false"
|
||||
aria-haspopup="listbox"
|
||||
autocomplete="off"
|
||||
class="ant-select-input"
|
||||
id="test-id"
|
||||
readonly=""
|
||||
role="combobox"
|
||||
type="search"
|
||||
value=""
|
||||
/>
|
||||
</div>
|
||||
<div
|
||||
class="ant-select-suffix"
|
||||
>
|
||||
<span
|
||||
aria-label="down"
|
||||
class="anticon anticon-down"
|
||||
role="img"
|
||||
>
|
||||
<svg
|
||||
aria-hidden="true"
|
||||
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>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
|
||||
exports[`renders components/select/demo/debug-flip-shift.tsx correctly 1`] = `
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import React from 'react';
|
||||
import { Button, Input, Select, Space } from 'antd';
|
||||
import { Button, Flex, Input, Select, Space } from 'antd';
|
||||
|
||||
const style: React.CSSProperties = {
|
||||
width: 500,
|
||||
@@ -14,7 +14,7 @@ const handleChange = (value: string | string[]) => {
|
||||
};
|
||||
|
||||
const App: React.FC = () => (
|
||||
<>
|
||||
<Flex vertical gap="middle">
|
||||
<Space style={style} wrap>
|
||||
<Input style={{ width: 100 }} value="222" />
|
||||
<Select
|
||||
@@ -48,7 +48,7 @@ const App: React.FC = () => (
|
||||
<span className="debug-align">AntDesign</span>
|
||||
<Button>222</Button>
|
||||
</Space>
|
||||
<div style={{ width: 200, marginTop: 24 }}>
|
||||
<div style={{ width: 200 }}>
|
||||
{/* https://github.com/ant-design/ant-design/issues/54179 */}
|
||||
<Select
|
||||
mode="multiple"
|
||||
@@ -62,7 +62,9 @@ const App: React.FC = () => (
|
||||
]}
|
||||
/>
|
||||
</div>
|
||||
</>
|
||||
|
||||
<Select defaultValue="" />
|
||||
</Flex>
|
||||
);
|
||||
|
||||
export default App;
|
||||
|
||||
@@ -7,17 +7,23 @@ import DownOutlined from '@ant-design/icons/DownOutlined';
|
||||
import LoadingOutlined from '@ant-design/icons/LoadingOutlined';
|
||||
import SearchOutlined from '@ant-design/icons/SearchOutlined';
|
||||
|
||||
import fallbackProp from '../_util/fallbackProp';
|
||||
import { devUseWarning } from '../_util/warning';
|
||||
|
||||
type RenderNode = React.ReactNode | ((props: any) => React.ReactNode);
|
||||
|
||||
export default function useIcons({
|
||||
suffixIcon,
|
||||
contextSuffixIcon,
|
||||
clearIcon,
|
||||
contextClearIcon,
|
||||
menuItemSelectedIcon,
|
||||
removeIcon,
|
||||
contextRemoveIcon,
|
||||
loading,
|
||||
loadingIcon,
|
||||
searchIcon,
|
||||
contextSearchIcon,
|
||||
multiple,
|
||||
hasFeedback,
|
||||
showSuffixIcon,
|
||||
@@ -26,11 +32,16 @@ export default function useIcons({
|
||||
componentName,
|
||||
}: {
|
||||
suffixIcon?: React.ReactNode;
|
||||
clearIcon?: RenderNode;
|
||||
contextSuffixIcon?: React.ReactNode;
|
||||
clearIcon?: React.ReactNode;
|
||||
contextClearIcon?: React.ReactNode;
|
||||
menuItemSelectedIcon?: RenderNode;
|
||||
removeIcon?: RenderNode;
|
||||
contextRemoveIcon?: RenderNode;
|
||||
loading?: boolean;
|
||||
loadingIcon?: React.ReactNode;
|
||||
searchIcon?: React.ReactNode;
|
||||
contextSearchIcon?: React.ReactNode;
|
||||
multiple?: boolean;
|
||||
hasFeedback?: boolean;
|
||||
feedbackIcon?: ReactNode;
|
||||
@@ -45,59 +56,72 @@ export default function useIcons({
|
||||
warning.deprecated(!clearIcon, 'clearIcon', 'allowClear={{ clearIcon: React.ReactNode }}');
|
||||
}
|
||||
|
||||
// Clear Icon
|
||||
const mergedClearIcon = clearIcon ?? <CloseCircleFilled />;
|
||||
return React.useMemo(() => {
|
||||
// Clear Icon
|
||||
const mergedClearIcon = fallbackProp(clearIcon, contextClearIcon, <CloseCircleFilled />);
|
||||
|
||||
// Validation Feedback Icon
|
||||
const getSuffixIconNode = (arrowIcon?: ReactNode) => {
|
||||
if (suffixIcon === null && !hasFeedback && !showArrow) {
|
||||
return null;
|
||||
}
|
||||
return (
|
||||
<>
|
||||
{showSuffixIcon !== false && arrowIcon}
|
||||
{hasFeedback && feedbackIcon}
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
// Arrow item icon
|
||||
let mergedSuffixIcon = null;
|
||||
if (suffixIcon !== undefined) {
|
||||
mergedSuffixIcon = getSuffixIconNode(suffixIcon);
|
||||
} else if (loading) {
|
||||
mergedSuffixIcon = getSuffixIconNode(loadingIcon ?? <LoadingOutlined spin />);
|
||||
} else {
|
||||
mergedSuffixIcon = ({ open, showSearch }: { open: boolean; showSearch: boolean }) => {
|
||||
if (open && showSearch) {
|
||||
return getSuffixIconNode(<SearchOutlined />);
|
||||
// Validation Feedback Icon
|
||||
const getSuffixIconNode = (arrowIcon?: ReactNode) => {
|
||||
if (suffixIcon === null && !hasFeedback && !showArrow) {
|
||||
return null;
|
||||
}
|
||||
return getSuffixIconNode(<DownOutlined />);
|
||||
return (
|
||||
<>
|
||||
{showSuffixIcon !== false && arrowIcon}
|
||||
{hasFeedback && feedbackIcon}
|
||||
</>
|
||||
);
|
||||
};
|
||||
}
|
||||
|
||||
// Checked item icon
|
||||
let mergedItemIcon = null;
|
||||
if (menuItemSelectedIcon !== undefined) {
|
||||
mergedItemIcon = menuItemSelectedIcon;
|
||||
} else if (multiple) {
|
||||
mergedItemIcon = <CheckOutlined />;
|
||||
} else {
|
||||
mergedItemIcon = null;
|
||||
}
|
||||
// Arrow item icon
|
||||
let mergedSuffixIcon = null;
|
||||
if (suffixIcon !== undefined) {
|
||||
mergedSuffixIcon = getSuffixIconNode(suffixIcon);
|
||||
} else if (loading) {
|
||||
mergedSuffixIcon = getSuffixIconNode(fallbackProp(loadingIcon, <LoadingOutlined spin />));
|
||||
} else {
|
||||
mergedSuffixIcon = ({ open, showSearch }: { open: boolean; showSearch: boolean }) => {
|
||||
if (open && showSearch) {
|
||||
return getSuffixIconNode(fallbackProp(searchIcon, contextSearchIcon, <SearchOutlined />));
|
||||
}
|
||||
return getSuffixIconNode(fallbackProp(contextSuffixIcon, <DownOutlined />));
|
||||
};
|
||||
}
|
||||
|
||||
let mergedRemoveIcon = null;
|
||||
if (removeIcon !== undefined) {
|
||||
mergedRemoveIcon = removeIcon;
|
||||
} else {
|
||||
mergedRemoveIcon = <CloseOutlined />;
|
||||
}
|
||||
// Checked item icon
|
||||
let mergedItemIcon = null;
|
||||
if (menuItemSelectedIcon !== undefined) {
|
||||
mergedItemIcon = menuItemSelectedIcon;
|
||||
} else if (multiple) {
|
||||
mergedItemIcon = <CheckOutlined />;
|
||||
} else {
|
||||
mergedItemIcon = null;
|
||||
}
|
||||
|
||||
return {
|
||||
// TODO: remove as when all the deps bumped
|
||||
clearIcon: mergedClearIcon as React.ReactNode,
|
||||
suffixIcon: mergedSuffixIcon,
|
||||
itemIcon: mergedItemIcon,
|
||||
removeIcon: mergedRemoveIcon,
|
||||
};
|
||||
const mergedRemoveIcon = fallbackProp(removeIcon, contextRemoveIcon, <CloseOutlined />);
|
||||
|
||||
return {
|
||||
clearIcon: mergedClearIcon,
|
||||
suffixIcon: mergedSuffixIcon,
|
||||
itemIcon: mergedItemIcon,
|
||||
removeIcon: mergedRemoveIcon,
|
||||
};
|
||||
}, [
|
||||
suffixIcon,
|
||||
contextSuffixIcon,
|
||||
clearIcon,
|
||||
contextClearIcon,
|
||||
menuItemSelectedIcon,
|
||||
removeIcon,
|
||||
contextRemoveIcon,
|
||||
loading,
|
||||
loadingIcon,
|
||||
searchIcon,
|
||||
contextSearchIcon,
|
||||
multiple,
|
||||
hasFeedback,
|
||||
showSuffixIcon,
|
||||
feedbackIcon,
|
||||
showArrow,
|
||||
]);
|
||||
}
|
||||
|
||||
@@ -7,11 +7,14 @@ export const InternalPanel = forwardRef<
|
||||
HTMLDivElement,
|
||||
React.PropsWithChildren<InternalPanelProps>
|
||||
>((props, ref) => {
|
||||
const { prefixCls, className, children, size, style = {} } = props;
|
||||
const { prefixCls, className, children, size, style = {}, supportMotion } = props;
|
||||
|
||||
const panelClassName = clsx(
|
||||
`${prefixCls}-panel`,
|
||||
{ [`${prefixCls}-panel-hidden`]: size === 0 },
|
||||
{
|
||||
[`${prefixCls}-panel-hidden`]: size === 0,
|
||||
[`${prefixCls}-panel-transition`]: supportMotion,
|
||||
},
|
||||
className,
|
||||
);
|
||||
|
||||
|
||||
@@ -28,6 +28,7 @@ const Splitter: React.FC<React.PropsWithChildren<SplitterProps>> = (props) => {
|
||||
prefixCls: customizePrefixCls,
|
||||
className,
|
||||
classNames,
|
||||
collapsible,
|
||||
style,
|
||||
styles,
|
||||
layout,
|
||||
@@ -217,9 +218,13 @@ const Splitter: React.FC<React.PropsWithChildren<SplitterProps>> = (props) => {
|
||||
style: { ...mergedStyles.panel, ...item.style },
|
||||
};
|
||||
|
||||
// Panel
|
||||
const panel = (
|
||||
<InternalPanel {...panelProps} prefixCls={prefixCls} size={panelSizes[idx]} />
|
||||
<InternalPanel
|
||||
{...panelProps}
|
||||
prefixCls={prefixCls}
|
||||
size={panelSizes[idx]}
|
||||
supportMotion={collapsible?.motion && movingIndex === undefined}
|
||||
/>
|
||||
);
|
||||
|
||||
// Split Bar
|
||||
|
||||
@@ -4,12 +4,40 @@ exports[`renders components/splitter/demo/collapsible.tsx extend context correct
|
||||
<div
|
||||
class="ant-flex css-var-test-id ant-flex-align-stretch ant-flex-gap-middle ant-flex-vertical"
|
||||
>
|
||||
<div
|
||||
class="ant-flex css-var-test-id ant-flex-gap-middle"
|
||||
>
|
||||
<button
|
||||
aria-checked="true"
|
||||
class="ant-switch css-var-test-id ant-switch-checked"
|
||||
role="switch"
|
||||
type="button"
|
||||
>
|
||||
<div
|
||||
class="ant-switch-handle"
|
||||
/>
|
||||
<span
|
||||
class="ant-switch-inner"
|
||||
>
|
||||
<span
|
||||
class="ant-switch-inner-checked"
|
||||
>
|
||||
motion
|
||||
</span>
|
||||
<span
|
||||
class="ant-switch-inner-unchecked"
|
||||
>
|
||||
motion
|
||||
</span>
|
||||
</span>
|
||||
</button>
|
||||
</div>
|
||||
<div
|
||||
class="ant-splitter ant-splitter-horizontal css-var-test-id ant-splitter-css-var"
|
||||
style="box-shadow: 0 0 10px rgba(0, 0, 0, 0.1); height: 200px;"
|
||||
>
|
||||
<div
|
||||
class="ant-splitter-panel"
|
||||
class="ant-splitter-panel ant-splitter-panel-transition"
|
||||
style="flex-basis: auto; flex-grow: 1;"
|
||||
>
|
||||
<div
|
||||
@@ -36,7 +64,7 @@ exports[`renders components/splitter/demo/collapsible.tsx extend context correct
|
||||
/>
|
||||
</div>
|
||||
<div
|
||||
class="ant-splitter-panel"
|
||||
class="ant-splitter-panel ant-splitter-panel-transition"
|
||||
style="flex-basis: auto; flex-grow: 1;"
|
||||
>
|
||||
<div
|
||||
@@ -57,7 +85,7 @@ exports[`renders components/splitter/demo/collapsible.tsx extend context correct
|
||||
style="box-shadow: 0 0 10px rgba(0, 0, 0, 0.1); height: 300px;"
|
||||
>
|
||||
<div
|
||||
class="ant-splitter-panel"
|
||||
class="ant-splitter-panel ant-splitter-panel-transition"
|
||||
style="flex-basis: auto; flex-grow: 1;"
|
||||
>
|
||||
<div
|
||||
@@ -84,7 +112,7 @@ exports[`renders components/splitter/demo/collapsible.tsx extend context correct
|
||||
/>
|
||||
</div>
|
||||
<div
|
||||
class="ant-splitter-panel"
|
||||
class="ant-splitter-panel ant-splitter-panel-transition"
|
||||
style="flex-basis: auto; flex-grow: 1;"
|
||||
>
|
||||
<div
|
||||
|
||||
@@ -4,12 +4,40 @@ exports[`renders components/splitter/demo/collapsible.tsx correctly 1`] = `
|
||||
<div
|
||||
class="ant-flex css-var-test-id ant-flex-align-stretch ant-flex-gap-middle ant-flex-vertical"
|
||||
>
|
||||
<div
|
||||
class="ant-flex css-var-test-id ant-flex-gap-middle"
|
||||
>
|
||||
<button
|
||||
aria-checked="true"
|
||||
class="ant-switch css-var-test-id ant-switch-checked"
|
||||
role="switch"
|
||||
type="button"
|
||||
>
|
||||
<div
|
||||
class="ant-switch-handle"
|
||||
/>
|
||||
<span
|
||||
class="ant-switch-inner"
|
||||
>
|
||||
<span
|
||||
class="ant-switch-inner-checked"
|
||||
>
|
||||
motion
|
||||
</span>
|
||||
<span
|
||||
class="ant-switch-inner-unchecked"
|
||||
>
|
||||
motion
|
||||
</span>
|
||||
</span>
|
||||
</button>
|
||||
</div>
|
||||
<div
|
||||
class="ant-splitter ant-splitter-horizontal css-var-test-id ant-splitter-css-var"
|
||||
style="box-shadow:0 0 10px rgba(0, 0, 0, 0.1);height:200px"
|
||||
>
|
||||
<div
|
||||
class="ant-splitter-panel"
|
||||
class="ant-splitter-panel ant-splitter-panel-transition"
|
||||
style="flex-basis:auto;flex-grow:1"
|
||||
>
|
||||
<div
|
||||
@@ -36,7 +64,7 @@ exports[`renders components/splitter/demo/collapsible.tsx correctly 1`] = `
|
||||
/>
|
||||
</div>
|
||||
<div
|
||||
class="ant-splitter-panel"
|
||||
class="ant-splitter-panel ant-splitter-panel-transition"
|
||||
style="flex-basis:auto;flex-grow:1"
|
||||
>
|
||||
<div
|
||||
@@ -57,7 +85,7 @@ exports[`renders components/splitter/demo/collapsible.tsx correctly 1`] = `
|
||||
style="box-shadow:0 0 10px rgba(0, 0, 0, 0.1);height:300px"
|
||||
>
|
||||
<div
|
||||
class="ant-splitter-panel"
|
||||
class="ant-splitter-panel ant-splitter-panel-transition"
|
||||
style="flex-basis:auto;flex-grow:1"
|
||||
>
|
||||
<div
|
||||
@@ -84,7 +112,7 @@ exports[`renders components/splitter/demo/collapsible.tsx correctly 1`] = `
|
||||
/>
|
||||
</div>
|
||||
<div
|
||||
class="ant-splitter-panel"
|
||||
class="ant-splitter-panel ant-splitter-panel-transition"
|
||||
style="flex-basis:auto;flex-grow:1"
|
||||
>
|
||||
<div
|
||||
|
||||
@@ -997,6 +997,21 @@ describe('Splitter', () => {
|
||||
expect(onCollapse).toHaveBeenCalledTimes(2);
|
||||
expect(onCollapse).toHaveBeenCalledWith([false, false], [50, 50]);
|
||||
});
|
||||
|
||||
it('should apply transition when motion is true', async () => {
|
||||
const { container } = render(
|
||||
<SplitterDemo
|
||||
items={[{ collapsible: true }, { collapsible: true }]}
|
||||
collapsible={{
|
||||
motion: true,
|
||||
}}
|
||||
/>,
|
||||
);
|
||||
|
||||
expect(container.querySelector('.ant-splitter-panel')).toHaveClass(
|
||||
'ant-splitter-panel-transition',
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
it('auto resize', async () => {
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import React from 'react';
|
||||
import { Flex, Splitter, Typography } from 'antd';
|
||||
import React, { useState } from 'react';
|
||||
import { Flex, Splitter, Switch, Typography } from 'antd';
|
||||
import type { SplitterProps } from 'antd';
|
||||
|
||||
const Desc: React.FC<Readonly<{ text?: string | number }>> = (props) => (
|
||||
@@ -21,11 +21,23 @@ const CustomSplitter: React.FC<Readonly<SplitterProps>> = ({ style, ...restProps
|
||||
</Splitter>
|
||||
);
|
||||
|
||||
const App: React.FC = () => (
|
||||
<Flex gap="middle" vertical>
|
||||
<CustomSplitter style={{ height: 200 }} />
|
||||
<CustomSplitter style={{ height: 300 }} orientation="vertical" />
|
||||
</Flex>
|
||||
);
|
||||
const App: React.FC = () => {
|
||||
const [motion, setMotion] = useState(true);
|
||||
|
||||
return (
|
||||
<Flex vertical gap="middle">
|
||||
<Flex gap="middle">
|
||||
<Switch
|
||||
checked={motion}
|
||||
onChange={setMotion}
|
||||
checkedChildren="motion"
|
||||
unCheckedChildren="motion"
|
||||
/>
|
||||
</Flex>
|
||||
<CustomSplitter style={{ height: 200 }} collapsible={{ motion }} />
|
||||
<CustomSplitter style={{ height: 300 }} orientation="vertical" collapsible={{ motion }} />
|
||||
</Flex>
|
||||
);
|
||||
};
|
||||
|
||||
export default App;
|
||||
|
||||
@@ -44,6 +44,7 @@ Common props ref:[Common props](/docs/react/common-props)
|
||||
| Property | Description | Type | Default | Version |
|
||||
| --- | --- | --- | --- | --- |
|
||||
| classNames | Customize class for each semantic structure inside the component. Supports object or function. | Record<[SemanticDOM](#semantic-dom), string> \| (info: { props })=> Record<[SemanticDOM](#semantic-dom), string> | - | |
|
||||
| collapsible | Collapse config. Set `motion: true` to enable collapse animation; duration is controlled by Component Token `panelMotionDuration` (inherits from `motionDurationSlow`) | `{ motion?: boolean }` | - | 6.4.0 |
|
||||
| collapsibleIcon | custom collapsible icon | `{start: ReactNode; end: ReactNode}` | - | 6.0.0 |
|
||||
| draggerIcon | custom dragger icon | `ReactNode` | - | 6.0.0 |
|
||||
| ~~layout~~ | Layout direction | `horizontal` \| `vertical` | `horizontal` | - |
|
||||
|
||||
@@ -45,6 +45,7 @@ demo:
|
||||
| 参数 | 说明 | 类型 | 默认值 | 版本 |
|
||||
| --- | --- | --- | --- | --- |
|
||||
| classNames | 用于自定义组件内部各语义化结构的 class,支持对象或函数 | Record<[SemanticDOM](#semantic-dom), string> \| (info: { props })=> Record<[SemanticDOM](#semantic-dom), string> | - | |
|
||||
| collapsible | 折叠配置。`motion: true` 时启用折叠动画,时长由组件 Token `panelMotionDuration` 控制(继承自 `motionDurationSlow`) | `{ motion?: boolean }` | - | 6.4.0 |
|
||||
| collapsibleIcon | 折叠图标 | `{start?: ReactNode; end?: ReactNode}` | - | 6.0.0 |
|
||||
| draggerIcon | 拖拽图标 | `ReactNode` | - | 6.0.0 |
|
||||
| ~~layout~~ | 布局方向 | `horizontal` \| `vertical` | `horizontal` | - |
|
||||
|
||||
@@ -46,6 +46,10 @@ export interface SplitterProps {
|
||||
prefixCls?: string;
|
||||
className?: string;
|
||||
classNames?: SplitterClassNamesType;
|
||||
/**
|
||||
* Collapse configuration. Set `motion: true` to enable collapse animation (duration follows Component Token).
|
||||
*/
|
||||
collapsible?: { motion?: boolean };
|
||||
style?: React.CSSProperties;
|
||||
styles?: SplitterStylesType;
|
||||
rootClassName?: string;
|
||||
@@ -87,6 +91,7 @@ export interface PanelProps {
|
||||
export interface InternalPanelProps extends PanelProps {
|
||||
className?: string;
|
||||
prefixCls?: string;
|
||||
supportMotion?: boolean;
|
||||
}
|
||||
|
||||
export interface UseResizeProps extends Pick<SplitterProps, 'onResize'> {
|
||||
|
||||
@@ -374,6 +374,18 @@ const genSplitterStyle: GenerateStyle<SplitterToken> = (token: SplitterToken): C
|
||||
[`&:has(${componentCls}:only-child)`]: {
|
||||
overflow: 'hidden',
|
||||
},
|
||||
|
||||
'&-transition': {
|
||||
transition: `flex-basis ${token.motionDurationSlow} ${token.motionEaseInOut}`,
|
||||
},
|
||||
},
|
||||
|
||||
'@media (prefers-reduced-motion: reduce)': {
|
||||
[splitPanelCls]: {
|
||||
'&-transition': {
|
||||
transition: 'none',
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
@@ -280,6 +280,12 @@ export const genBaseStyle = (prefixCls: string, token: TreeToken): CSSObject =>
|
||||
.equal(),
|
||||
},
|
||||
|
||||
// >>> Checkbox
|
||||
// https://github.com/ant-design/ant-design/issues/56957
|
||||
[`${treeCls}-checkbox`]: {
|
||||
flexShrink: 0,
|
||||
},
|
||||
|
||||
// >>> Switcher
|
||||
[`${treeCls}-switcher`]: {
|
||||
...getSwitchStyle(prefixCls, token),
|
||||
|
||||
@@ -136,7 +136,7 @@
|
||||
"@rc-component/rate": "~1.0.1",
|
||||
"@rc-component/resize-observer": "^1.1.1",
|
||||
"@rc-component/segmented": "~1.3.0",
|
||||
"@rc-component/select": "~1.6.5",
|
||||
"@rc-component/select": "~1.6.8",
|
||||
"@rc-component/slider": "~1.0.1",
|
||||
"@rc-component/steps": "~1.2.2",
|
||||
"@rc-component/switch": "~1.0.3",
|
||||
|
||||
Reference in New Issue
Block a user