diff --git a/.dumi/theme/common/SelectSemanticTemplate.tsx b/.dumi/theme/common/SelectSemanticTemplate.tsx index 69186f25d6..5098ebdc11 100644 --- a/.dumi/theme/common/SelectSemanticTemplate.tsx +++ b/.dumi/theme/common/SelectSemanticTemplate.tsx @@ -11,6 +11,7 @@ export const locales = { suffix: '后缀元素,包含后缀内容的布局和样式,如清除按钮、箭头图标等', input: '输入框元素,包含搜索输入框的样式、光标控制、字体继承等搜索相关样式,去除了边框样式', content: '多选容器,包含已选项的布局、间距、换行相关样式', + clear: '清除按钮元素,包含清除按钮的布局、样式和交互效果', item: '多选项元素,包含边框、背景、内边距、外边距样式', itemContent: '多选项内容区域,包含文字的省略样式', itemRemove: '多选项移除按钮,包含字体相关样式', @@ -29,6 +30,7 @@ export const locales = { 'Input element with search input styling, cursor control, font inheritance and other search-related styles. Remove border styles', content: 'Multiple selection container with layout, spacing, and wrapping styles for selected items', + clear: 'Clear button element with layout, styling and interactive effects for clear button', item: 'Multiple selection item element with border, background, padding, and margin styles', itemContent: 'Multiple selection item content area with text ellipsis styles', itemRemove: 'Multiple selection item remove button with font-related styles', @@ -142,6 +144,7 @@ const SelectSemanticTemplate: React.FC = ({ { name: 'prefix', desc: locale.prefix }, { name: 'content', desc: locale.content }, { name: 'placeholder', desc: locale.placeholder }, + { name: 'clear', desc: locale.clear }, { name: 'input', desc: locale.input }, { name: 'suffix', desc: locale.suffix }, { name: 'popup.root', desc: locale['popup.root'] }, @@ -152,11 +155,12 @@ const SelectSemanticTemplate: React.FC = ({ { name: 'root', desc: locale.root }, { name: 'prefix', desc: locale.prefix }, { name: 'content', desc: locale.content }, + { name: 'placeholder', desc: locale.placeholder }, + { name: 'clear', desc: locale.clear }, { name: 'item', desc: locale.item }, { name: 'itemContent', desc: locale.itemContent }, { name: 'itemRemove', desc: locale.itemRemove }, { name: 'input', desc: locale.input }, - { name: 'placeholder', desc: locale.placeholder }, { name: 'suffix', desc: locale.suffix }, { name: 'popup.root', desc: locale['popup.root'] }, { name: 'popup.list', desc: locale['popup.list'] }, diff --git a/.github/workflows/pr-check-merge.yml b/.github/workflows/pr-check-merge.yml index 23e5e6eef7..e62f52fcec 100644 --- a/.github/workflows/pr-check-merge.yml +++ b/.github/workflows/pr-check-merge.yml @@ -15,7 +15,7 @@ jobs: runs-on: ubuntu-latest if: (github.event.pull_request.head.ref == 'next' || github.event.pull_request.head.ref == 'feature' || github.event.pull_request.head.ref == 'master') && github.event.pull_request.head.user.login == 'ant-design' steps: - - uses: actions-cool/issues-helper@cbca8dff5d68beaba6fa47e9f49608ed2de544d6 + - uses: actions-cool/issues-helper@d1d51fccf39469b5458203b1369060db0ff0c0db with: actions: create-comment issue-number: ${{ github.event.number }} diff --git a/components/button/__tests__/__snapshots__/demo-extend.test.ts.snap b/components/button/__tests__/__snapshots__/demo-extend.test.ts.snap index e2aa201b42..e7143c819d 100644 --- a/components/button/__tests__/__snapshots__/demo-extend.test.ts.snap +++ b/components/button/__tests__/__snapshots__/demo-extend.test.ts.snap @@ -4414,3 +4414,52 @@ exports[`renders components/button/demo/style-class.tsx extend context correctly `; exports[`renders components/button/demo/style-class.tsx extend context correctly 2`] = `[]`; + +exports[`renders components/button/demo/wave.tsx extend context correctly 1`] = ` +
+ + + + + +
+`; + +exports[`renders components/button/demo/wave.tsx extend context correctly 2`] = `[]`; diff --git a/components/button/__tests__/__snapshots__/demo.test.ts.snap b/components/button/__tests__/__snapshots__/demo.test.ts.snap index 6689f2044a..84e1d4b9da 100644 --- a/components/button/__tests__/__snapshots__/demo.test.ts.snap +++ b/components/button/__tests__/__snapshots__/demo.test.ts.snap @@ -3883,3 +3883,50 @@ exports[`renders components/button/demo/style-class.tsx correctly 1`] = ` `; + +exports[`renders components/button/demo/wave.tsx correctly 1`] = ` +
+ + + + + +
+`; diff --git a/components/button/demo/wave.md b/components/button/demo/wave.md new file mode 100644 index 0000000000..c8d1c0c57e --- /dev/null +++ b/components/button/demo/wave.md @@ -0,0 +1,7 @@ +## zh-CN + +波纹效果带来了灵动性,你也可以使用 [`@ant-design/happy-work-theme`](https://github.com/ant-design/happy-work-theme) 提供的 HappyProvider 实现动态波纹效果。 + +## en-US + +Wave effect brings dynamic. You can also use HappyProvider from [`@ant-design/happy-work-theme`](https://github.com/ant-design/happy-work-theme) to implement dynamic wave effect. diff --git a/components/button/demo/wave.tsx b/components/button/demo/wave.tsx new file mode 100644 index 0000000000..a7cfa8b34a --- /dev/null +++ b/components/button/demo/wave.tsx @@ -0,0 +1,126 @@ +import React from 'react'; +import { HappyProvider } from '@ant-design/happy-work-theme'; +import { Button, ConfigProvider, Flex } from 'antd'; +import type { ConfigProviderProps, GetProp } from 'antd'; + +type WaveConfig = GetProp; + +// Prepare effect holder +const createHolder = (node: HTMLElement) => { + const { borderWidth } = getComputedStyle(node); + const borderWidthNum = Number.parseInt(borderWidth, 10); + + const div = document.createElement('div'); + div.style.position = 'absolute'; + div.style.inset = `-${borderWidthNum}px`; + div.style.borderRadius = 'inherit'; + div.style.background = 'transparent'; + div.style.zIndex = '999'; + div.style.pointerEvents = 'none'; + div.style.overflow = 'hidden'; + node.appendChild(div); + + return div; +}; + +const createDot = (holder: HTMLElement, color: string, left: number, top: number, size = 0) => { + const dot = document.createElement('div'); + dot.style.position = 'absolute'; + dot.style.left = `${left}px`; + dot.style.top = `${top}px`; + dot.style.width = `${size}px`; + dot.style.height = `${size}px`; + dot.style.borderRadius = '50%'; + dot.style.background = color; + dot.style.transform = 'translate3d(-50%, -50%, 0)'; + dot.style.transition = 'all 1s ease-out'; + holder.appendChild(dot); + return dot; +}; + +// Inset Effect +const showInsetEffect: WaveConfig['showEffect'] = (node, { event, component }) => { + if (component !== 'Button') { + return; + } + + const holder = createHolder(node); + + const rect = holder.getBoundingClientRect(); + + const left = event.clientX - rect.left; + const top = event.clientY - rect.top; + + const dot = createDot(holder, 'rgba(255, 255, 255, 0.65)', left, top); + + // Motion + requestAnimationFrame(() => { + dot.ontransitionend = () => { + holder.remove(); + }; + + dot.style.width = '200px'; + dot.style.height = '200px'; + dot.style.opacity = '0'; + }); +}; + +// Shake Effect +const showShakeEffect: WaveConfig['showEffect'] = (node, { component }) => { + if (component !== 'Button') { + return; + } + + const seq = [0, -15, 15, -5, 5, 0]; + const itv = 10; + + let steps = 0; + + const loop = () => { + cancelAnimationFrame((node as any).effectTimeout); + + (node as any).effectTimeout = requestAnimationFrame(() => { + const currentStep = Math.floor(steps / itv); + const current = seq[currentStep]; + const next = seq[currentStep + 1]; + + if (next === undefined || next === null) { + node.style.transform = ''; + node.style.transition = ''; + return; + } + + // Trans from current to next by itv + const angle = current + ((next - current) / itv) * (steps % itv); + + node.style.transform = `rotate(${angle}deg)`; + node.style.transition = 'none'; + + steps += 1; + loop(); + }); + }; + + loop(); +}; + +// Component +const Wrapper: React.FC = ({ name, ...wave }) => ( + + + +); + +const Demo: React.FC = () => ( + + + + + + + + + +); + +export default Demo; diff --git a/components/button/index.en-US.md b/components/button/index.en-US.md index 698648109a..98125cbcd2 100644 --- a/components/button/index.en-US.md +++ b/components/button/index.en-US.md @@ -52,6 +52,7 @@ And 4 other properties additionally. Loading style bug Component Token Gradient Button +Custom Wave Custom disabled backgroundColor Custom semantic dom styling diff --git a/components/button/index.zh-CN.md b/components/button/index.zh-CN.md index e1092a09d1..8199af9c16 100644 --- a/components/button/index.zh-CN.md +++ b/components/button/index.zh-CN.md @@ -53,6 +53,7 @@ group: 加载中状态 bug 还原 组件 Token 渐变按钮 +自定义按钮波纹 移除两个汉字之间的空格 自定义禁用样式背景 自定义语义结构的样式和类 diff --git a/components/button/style/token.ts b/components/button/style/token.ts index 9b46cceae7..330604a158 100644 --- a/components/button/style/token.ts +++ b/components/button/style/token.ts @@ -181,11 +181,6 @@ export interface ComponentToken { * @descEN Icon size of small button which only contains icon */ onlyIconSizeSM: number | string; - /** - * @desc 按钮组边框颜色 - * @descEN Border color of button group - */ - groupBorderColor: string; /** * @desc 链接按钮悬浮态背景色 * @descEN Background color of link button when hover @@ -246,7 +241,17 @@ type ShadowColorMap = { [Key in `${PresetColorKey}ShadowColor`]: string; }; -export interface ButtonToken extends FullToken<'Button'>, ShadowColorMap { +type GroupToken = { + /** + * @desc 按钮组边框颜色 + * @descEN Border color of button group + * @internal Button.Group 已废弃相关token不应该在显示在文档上 + */ + + groupBorderColor: string; +}; + +export interface ButtonToken extends FullToken<'Button'>, ShadowColorMap, GroupToken { /** * @desc 按钮横向内边距 * @descEN Horizontal padding of button diff --git a/components/config-provider/demo/wave.tsx b/components/config-provider/demo/wave.tsx index 736e6722ef..a7cfa8b34a 100644 --- a/components/config-provider/demo/wave.tsx +++ b/components/config-provider/demo/wave.tsx @@ -1,6 +1,6 @@ import React from 'react'; import { HappyProvider } from '@ant-design/happy-work-theme'; -import { Button, ConfigProvider, Space } from 'antd'; +import { Button, ConfigProvider, Flex } from 'antd'; import type { ConfigProviderProps, GetProp } from 'antd'; type WaveConfig = GetProp; @@ -32,10 +32,9 @@ const createDot = (holder: HTMLElement, color: string, left: number, top: number dot.style.height = `${size}px`; dot.style.borderRadius = '50%'; dot.style.background = color; - dot.style.transform = 'translate(-50%, -50%)'; + dot.style.transform = 'translate3d(-50%, -50%, 0)'; dot.style.transition = 'all 1s ease-out'; holder.appendChild(dot); - return dot; }; @@ -77,7 +76,7 @@ const showShakeEffect: WaveConfig['showEffect'] = (node, { component }) => { let steps = 0; - function loop() { + const loop = () => { cancelAnimationFrame((node as any).effectTimeout); (node as any).effectTimeout = requestAnimationFrame(() => { @@ -85,7 +84,7 @@ const showShakeEffect: WaveConfig['showEffect'] = (node, { component }) => { const current = seq[currentStep]; const next = seq[currentStep + 1]; - if (!next) { + if (next === undefined || next === null) { node.style.transform = ''; node.style.transition = ''; return; @@ -100,20 +99,20 @@ const showShakeEffect: WaveConfig['showEffect'] = (node, { component }) => { steps += 1; loop(); }); - } + }; loop(); }; // Component -const Wrapper = ({ name, ...wave }: WaveConfig & { name: string }) => ( +const Wrapper: React.FC = ({ name, ...wave }) => ( ); -const App = () => ( - +const Demo: React.FC = () => ( + @@ -121,7 +120,7 @@ const App = () => ( - + ); -export default App; +export default Demo; diff --git a/components/input-number/demo/change-on-wheel.tsx b/components/input-number/demo/change-on-wheel.tsx index 81bd2a35ad..23c91b7ecf 100644 --- a/components/input-number/demo/change-on-wheel.tsx +++ b/components/input-number/demo/change-on-wheel.tsx @@ -6,8 +6,19 @@ const onChange: InputNumberProps['onChange'] = (value) => { console.log('changed', value); }; +const onStep: InputNumberProps['onStep'] = (value, info) => { + console.log('onStep', value, info); +}; + const App: React.FC = () => ( - + ); export default App; diff --git a/components/input-number/index.en-US.md b/components/input-number/index.en-US.md index f00431085f..cd72b46d84 100644 --- a/components/input-number/index.en-US.md +++ b/components/input-number/index.en-US.md @@ -71,7 +71,7 @@ Common props ref:[Common props](/docs/react/common-props) | variant | Variants of Input | `outlined` \| `borderless` \| `filled` \| `underlined` | `outlined` | 5.13.0 \| `underlined`: 5.24.0 | | onChange | The callback triggered when the value is changed | function(value: number \| string \| null) | - | - | | onPressEnter | The callback function that is triggered when Enter key is pressed | function(e) | - | - | -| onStep | The callback function that is triggered when click up or down buttons | (value: number, info: { offset: number, type: 'up' \| 'down' }) => void | - | | +| onStep | The callback function that is triggered when click up or down buttons / Keyboard / Wheel | (value: number, info: { offset: number, type: 'up' \| 'down', emitter: 'handler' \| 'keydown' \| 'wheel' }) => void | - | | ## Ref diff --git a/components/input-number/index.zh-CN.md b/components/input-number/index.zh-CN.md index ca5db1c8e3..1595176eee 100644 --- a/components/input-number/index.zh-CN.md +++ b/components/input-number/index.zh-CN.md @@ -72,7 +72,7 @@ demo: | variant | 形态变体 | `outlined` \| `borderless` \| `filled` \| `underlined` | `outlined` | 5.13.0 \| `underlined`: 5.24.0 | | onChange | 变化回调 | function(value: number \| string \| null) | - | - | | onPressEnter | 按下回车的回调 | function(e) | - | - | -| onStep | 点击上下箭头的回调 | (value: number, info: { offset: number, type: 'up' \| 'down' }) => void | - | 4.7.0 | +| onStep | 点击上下箭头、键盘、滚轮的回调 | (value: number, info: { offset: number, type: 'up' \| 'down', emitter: 'handler' \| 'keydown' \| 'wheel' }) => void | - | 4.7.0 | ## Ref diff --git a/components/layout/layout.tsx b/components/layout/layout.tsx index 160a02c15d..ad845d2dbb 100644 --- a/components/layout/layout.tsx +++ b/components/layout/layout.tsx @@ -13,6 +13,7 @@ export interface GeneratorProps { tagName: 'header' | 'footer' | 'main' | 'div'; displayName: string; } + export interface BasicProps extends React.HTMLAttributes { prefixCls?: string; suffixCls?: string; @@ -24,17 +25,17 @@ interface BasicPropsWithTagName extends BasicProps { tagName: 'header' | 'footer' | 'main' | 'div'; } -function generator({ suffixCls, tagName, displayName }: GeneratorProps) { - return (BasicComponent: any) => { +const generator = ({ suffixCls, tagName, displayName }: GeneratorProps) => { + return (Component: React.ComponentType>) => { const Adapter = React.forwardRef((props, ref) => ( - + )); if (process.env.NODE_ENV !== 'production') { Adapter.displayName = displayName; } return Adapter; }; -} +}; const Basic = React.forwardRef((props, ref) => { const { diff --git a/components/select/__tests__/semantic.test.tsx b/components/select/__tests__/semantic.test.tsx index 37dbd962b4..d628db2397 100644 --- a/components/select/__tests__/semantic.test.tsx +++ b/components/select/__tests__/semantic.test.tsx @@ -1,7 +1,7 @@ import * as React from 'react'; import Select from '..'; -import type { SelectProps } from '..'; +import type { SelectClassNamesType, SelectProps } from '..'; import { render } from '../../../tests/utils'; describe('Select.Semantic', () => { @@ -28,7 +28,7 @@ describe('Select.Semantic', () => { list: 'custom-list', listItem: 'custom-list-item', }, - }; + } satisfies SelectClassNamesType; const styles = { root: { color: 'rgb(255, 0, 0)' }, prefix: { color: 'rgb(0, 128, 255)' }, @@ -60,9 +60,9 @@ describe('Select.Semantic', () => { expect(container.querySelector(`.${classNames.placeholder}`)).toHaveStyle(styles.placeholder); expect(container.querySelector(`.${classNames.input}`)).toHaveStyle(styles.input); expect(container.querySelector(`.${classNames.content}`)).toHaveStyle(styles.content); - expect(container.querySelector(`.${classNames.popup.root}`)).toHaveStyle(styles.popup.root); - expect(container.querySelector(`.${classNames.popup.list}`)).toHaveStyle(styles.popup.list); - expect(container.querySelector(`.${classNames.popup.listItem}`)).toHaveStyle( + expect(container.querySelector(`.${classNames.popup?.root}`)).toHaveStyle(styles.popup.root); + expect(container.querySelector(`.${classNames.popup?.list}`)).toHaveStyle(styles.popup.list); + expect(container.querySelector(`.${classNames.popup?.listItem}`)).toHaveStyle( styles.popup.listItem, ); }); @@ -72,16 +72,22 @@ describe('Select.Semantic', () => { root: 'custom-root', prefix: 'custom-prefix', suffix: 'custom-suffix', + item: 'custom-item', + itemContent: 'custom-item-content', + itemRemove: 'custom-item-remove', popup: { root: 'custom-popup', list: 'custom-list', listItem: 'custom-list-item', }, - }; + } satisfies SelectClassNamesType; const customStyles = { root: { color: 'rgb(255, 0, 0)' }, prefix: { color: 'rgb(0, 128, 255)' }, suffix: { color: 'rgb(255, 128, 0)' }, + item: { background: 'rgb(255, 255, 240)' }, + itemContent: { color: 'rgb(128, 0, 128)' }, + itemRemove: { color: 'rgb(255, 0, 0)' }, popup: { root: { color: 'rgb(128, 0, 128)' }, list: { color: 'rgb(0, 0, 255)' }, @@ -107,6 +113,9 @@ describe('Select.Semantic', () => { const list = container.querySelector('.rc-virtual-list'); const listItem = container.querySelector('.ant-select-item'); const popup = container.querySelector('.ant-select-dropdown'); + const item = container.querySelector('.ant-select-selection-item'); + const itemContent = container.querySelector('.ant-select-selection-item-content'); + const itemRemove = container.querySelector('.ant-select-selection-item-remove'); expect(root).toHaveClass(customClassNames.root); expect(prefix).toHaveClass(customClassNames.prefix); @@ -120,6 +129,15 @@ describe('Select.Semantic', () => { if (popup) { expect(popup).toHaveClass(customClassNames.popup.root); } + if (item) { + expect(item).toHaveClass(customClassNames.item); + } + if (itemContent) { + expect(itemContent).toHaveClass(customClassNames.itemContent); + } + if (itemRemove) { + expect(itemRemove).toHaveClass(customClassNames.itemRemove); + } expect(root).toHaveStyle(customStyles.root); expect(prefix).toHaveStyle(customStyles.prefix); @@ -133,6 +151,15 @@ describe('Select.Semantic', () => { if (popup) { expect(popup).toHaveStyle(customStyles.popup.root); } + if (item) { + expect(item).toHaveStyle(customStyles.item); + } + if (itemContent) { + expect(itemContent).toHaveStyle(customStyles.itemContent); + } + if (itemRemove) { + expect(itemRemove).toHaveStyle(customStyles.itemRemove); + } }); it('should support function-based classNames and styles', () => { diff --git a/components/select/index.tsx b/components/select/index.tsx index 6f5cadff60..a405b0fbf8 100755 --- a/components/select/index.tsx +++ b/components/select/index.tsx @@ -71,18 +71,30 @@ export interface InternalSelectProps< styles?: SelectSemanticStyles & { popup?: PopupSemanticStyles }; } -export type SelectSemanticName = 'root' | 'prefix' | 'suffix'; - export type SelectSemanticClassNames = { root?: string; prefix?: string; suffix?: string; + input?: string; + placeholder?: string; + content?: string; + item?: string; + itemContent?: string; + itemRemove?: string; + clear?: string; }; export type SelectSemanticStyles = { root?: React.CSSProperties; prefix?: React.CSSProperties; suffix?: React.CSSProperties; + input?: React.CSSProperties; + placeholder?: React.CSSProperties; + content?: React.CSSProperties; + item?: React.CSSProperties; + itemContent?: React.CSSProperties; + itemRemove?: React.CSSProperties; + clear?: React.CSSProperties; }; export type PopupSemanticClassNames = { diff --git a/components/transfer/ListItem.tsx b/components/transfer/ListItem.tsx index fd94dcf672..dca0335c7d 100644 --- a/components/transfer/ListItem.tsx +++ b/components/transfer/ListItem.tsx @@ -38,7 +38,7 @@ const ListItem = (props: ListItemProps -## 轮廓线与模版 +## 轮廓线与模板 -我们对设计模版进行了优化,根据出血位的尺寸,调整轮廓线的宽高,同时增加两个等边三角形和一个圆,这些都是图标设计中最常用的基本形式,设计师可以快速的调用并在此基础上做变形。 +我们对设计模板进行了优化,根据出血位的尺寸,调整轮廓线的宽高,同时增加两个等边三角形和一个圆,这些都是图标设计中最常用的基本形式,设计师可以快速的调用并在此基础上做变形。
-轮廓线与模版 +轮廓线与模板
## 图标设计指引 diff --git a/docs/spec/research-list.zh-CN.md b/docs/spec/research-list.zh-CN.md index 7bd9e9e006..53d6697ec8 100644 --- a/docs/spec/research-list.zh-CN.md +++ b/docs/spec/research-list.zh-CN.md @@ -52,7 +52,7 @@ title: 列表页 将数据过滤模块放置在侧栏,当过滤条件过多,横向空间充裕时使用。 -#### [模版 - 查询表格](https://preview.pro.ant.design/list/table-list) +#### [模板 - 查询表格](https://preview.pro.ant.design/list/table-list) @@ -62,7 +62,7 @@ title: 列表页 每个条目都需要露出很多字段;用户在搜寻条目时有准确的查询范围时使用。 -#### 模版 - 标准列表 +#### 模板 - 标准列表 @@ -82,7 +82,7 @@ title: 列表页 用户无需以特定顺序浏览条目,将每个条目以富有吸引力的方式呈现。 -#### 模版 - 搜索列表 +#### 模板 - 搜索列表 @@ -96,7 +96,7 @@ title: 列表页 筛选、搜索 -#### 模版 - 成员管理 +#### 模板 - 成员管理 diff --git a/package.json b/package.json index d6cdb9bd57..3e28398a2d 100644 --- a/package.json +++ b/package.json @@ -122,7 +122,7 @@ "@rc-component/dialog": "~1.6.0", "@rc-component/drawer": "~1.3.0", "@rc-component/dropdown": "~1.0.2", - "@rc-component/form": "~1.5.0", + "@rc-component/form": "~1.6.0", "@rc-component/image": "~1.5.3", "@rc-component/input": "~1.1.2", "@rc-component/input-number": "~1.6.2", @@ -224,7 +224,7 @@ "ajv": "^8.17.1", "ali-oss": "^6.23.0", "antd-img-crop": "^4.27.0", - "antd-style": "4.0.0-alpha.1", + "antd-style": "^4.0.0-beta.1", "antd-token-previewer": "^3.0.0", "axios": "^1.13.2", "chalk": "^5.6.2", @@ -247,7 +247,7 @@ "eslint-plugin-react-hooks": "^7.0.1", "eslint-plugin-react-refresh": "^0.4.24", "fast-glob": "^3.3.3", - "father": "4.6.10", + "father": "4.6.11", "fs-extra": "^11.3.2", "gh-pages": "^6.3.0", "github-slugger": "^2.0.0",