diff --git a/.antd-tools.config.js b/.antd-tools.config.js index 7a5bec3673..5c139e9d30 100644 --- a/.antd-tools.config.js +++ b/.antd-tools.config.js @@ -1,195 +1,32 @@ const fs = require('fs'); const path = require('path'); -const defaultVars = require('./scripts/default-vars'); -const darkVars = require('./scripts/dark-vars'); -const compactVars = require('./scripts/compact-vars'); -function generateThemeFileContent(theme) { - return `const { ${theme}ThemeSingle } = require('./theme');\nconst defaultTheme = require('./default-theme');\n -module.exports = { - ...defaultTheme, - ...${theme}ThemeSingle -}`; -} - -// We need compile additional content for antd user function finalizeCompile() { - if (fs.existsSync(path.join(__dirname, './lib'))) { - // Build a entry less file to dist/antd.less - const componentsPath = path.join(process.cwd(), 'components'); - let componentsLessContent = ''; - // Build components in one file: lib/style/components.less - fs.readdir(componentsPath, (err, files) => { - files.forEach(file => { - if (fs.existsSync(path.join(componentsPath, file, 'style', 'index.less'))) { - componentsLessContent += `@import "../${path.posix.join( - file, - 'style', - 'index-pure.less', - )}";\n`; - } - }); - fs.writeFileSync( - path.join(process.cwd(), 'lib', 'style', 'components.less'), - componentsLessContent, - ); - }); - } -} - -function buildThemeFile(theme, vars) { - // Build less entry file: dist/antd.${theme}.less - if (theme !== 'default') { - fs.writeFileSync( - path.join(process.cwd(), 'dist', `antd.${theme}.less`), - `@import "../lib/style/${theme}.less";\n@import "../lib/style/components.less";`, + if (fs.existsSync(path.join(__dirname, './es'))) { + // Build less entry file: dist/antd.less + fs.copyFileSync( + path.join(process.cwd(), 'components', 'style', 'reset.css'), + path.join(process.cwd(), 'es', 'style', 'reset.css'), ); - // eslint-disable-next-line no-console - console.log(`Built a entry less file to dist/antd.${theme}.less`); - } else { - fs.writeFileSync( - path.join(process.cwd(), 'dist', `default-theme.js`), - `module.exports = ${JSON.stringify(vars, null, 2)};\n`, - ); - return; } - - // Build ${theme}.js: dist/${theme}-theme.js, for less-loader - - fs.writeFileSync( - path.join(process.cwd(), 'dist', `theme.js`), - `const ${theme}ThemeSingle = ${JSON.stringify(vars, null, 2)};\n`, - { - flag: 'a', - }, - ); - - fs.writeFileSync( - path.join(process.cwd(), 'dist', `${theme}-theme.js`), - generateThemeFileContent(theme), - ); - - // eslint-disable-next-line no-console - console.log(`Built a ${theme} theme js file to dist/${theme}-theme.js`); } function finalizeDist() { if (fs.existsSync(path.join(__dirname, './dist'))) { // Build less entry file: dist/antd.less - fs.writeFileSync( - path.join(process.cwd(), 'dist', 'antd.less'), - '@import "../lib/style/default.less";\n@import "../lib/style/components.less";', - ); - // eslint-disable-next-line no-console - fs.writeFileSync( - path.join(process.cwd(), 'dist', 'theme.js'), - `const defaultTheme = require('./default-theme.js');\n`, - ); - // eslint-disable-next-line no-console - console.log('Built a entry less file to dist/antd.less'); - buildThemeFile('default', defaultVars); - buildThemeFile('dark', darkVars); - buildThemeFile('compact', compactVars); - buildThemeFile('variable', {}); - fs.writeFileSync( - path.join(process.cwd(), 'dist', `theme.js`), - ` -function getThemeVariables(options = {}) { - let themeVar = { - 'hack': \`true;@import "\${require.resolve('antd/lib/style/color/colorPalette.less')}";\`, - ...defaultTheme - }; - if(options.dark) { - themeVar = { - ...themeVar, - ...darkThemeSingle - } - } - if(options.compact){ - themeVar = { - ...themeVar, - ...compactThemeSingle - } - } - return themeVar; -} - -module.exports = { - darkThemeSingle, - compactThemeSingle, - getThemeVariables -}`, - { - flag: 'a', - }, + fs.copyFileSync( + path.join(process.cwd(), 'components', 'style', 'reset.css'), + path.join(process.cwd(), 'dist', 'reset.css'), ); } } -function isComponentStyleEntry(file) { - return file.path.match(/style(\/|\\)index\.tsx/); -} - -function needTransformStyle(content) { - return content.includes('../../style/index.less') || content.includes('./index.less'); -} - module.exports = { compile: { - includeLessFile: [/(\/|\\)components(\/|\\)style(\/|\\)default.less$/], - transformTSFile(file) { - if (isComponentStyleEntry(file)) { - let content = file.contents.toString(); - - if (needTransformStyle(content)) { - const cloneFile = file.clone(); - - // Origin - content = content.replace('../../style/index.less', '../../style/default.less'); - cloneFile.contents = Buffer.from(content); - - return cloneFile; - } - } - }, - transformFile(file) { - if (isComponentStyleEntry(file)) { - const indexLessFilePath = file.path.replace('index.tsx', 'index.less'); - - if (fs.existsSync(indexLessFilePath)) { - // We put origin `index.less` file to `index-pure.less` - const pureFile = file.clone(); - pureFile.contents = Buffer.from(fs.readFileSync(indexLessFilePath, 'utf8')); - pureFile.path = pureFile.path.replace('index.tsx', 'index-pure.less'); - - // Rewrite `index.less` file with `root-entry-name` - const indexLessFile = file.clone(); - indexLessFile.contents = Buffer.from( - [ - // Inject variable - '@root-entry-name: default;', - // Point to origin file - "@import './index-pure.less';", - ].join('\n\n'), - ); - indexLessFile.path = indexLessFile.path.replace('index.tsx', 'index.less'); - - return [indexLessFile, pureFile]; - } - } - - return []; - }, - lessConfig: { - modifyVars: { - 'root-entry-name': 'default', - }, - }, finalize: finalizeCompile, }, dist: { finalize: finalizeDist, }, - generateThemeFileContent, bail: true, }; diff --git a/.dumi/hooks/useLocale.tsx b/.dumi/hooks/useLocale.tsx new file mode 100644 index 0000000000..6ca4dd51c7 --- /dev/null +++ b/.dumi/hooks/useLocale.tsx @@ -0,0 +1,15 @@ +import * as React from 'react'; +import { useLocale as useDumiLocale } from 'dumi'; + +export interface LocaleMap { + cn: Record; + en: Record; +} + +export default function useLocale( + localeMap?: LocaleMap, +): [Record, 'cn' | 'en'] { + const { id } = useDumiLocale(); + const localeType = id === 'zh-CN' ? 'cn' : ('en' as const); + return [localeMap?.[localeType]!, localeType]; +} diff --git a/.dumi/hooks/useLocation.ts b/.dumi/hooks/useLocation.ts new file mode 100644 index 0000000000..3499a3bbe2 --- /dev/null +++ b/.dumi/hooks/useLocation.ts @@ -0,0 +1,47 @@ +import { useLocation as useDumiLocation } from 'dumi'; +import * as React from 'react'; +import useLocale from './useLocale'; + +function clearPath(path: string) { + return path.replace('-cn', '').replace(/\/$/, ''); +} + +export default function useLocation() { + const location = useDumiLocation(); + const { search } = location; + const [, localeType] = useLocale(); + + const getLink = React.useCallback( + (path: string, hash?: string | { cn: string; en: string }) => { + let pathname = clearPath(path); + + if (localeType === 'cn') { + pathname = `${pathname}-cn`; + } + + if (search) { + pathname = `${pathname}${search}`; + } + + if (hash) { + let hashStr: string; + if (typeof hash === 'object') { + hashStr = hash[localeType]; + } else { + hashStr = hash; + } + + pathname = `${pathname}#${hashStr}`; + } + + return pathname; + }, + [localeType, search], + ); + + return { + ...location, + pathname: clearPath(location.pathname), + getLink, + }; +} diff --git a/.dumi/hooks/useMenu.tsx b/.dumi/hooks/useMenu.tsx new file mode 100644 index 0000000000..a537adac3b --- /dev/null +++ b/.dumi/hooks/useMenu.tsx @@ -0,0 +1,138 @@ +import React, { ReactNode, useMemo } from 'react'; +import { MenuProps } from 'antd'; +import { Link, useFullSidebarData, useSidebarData } from 'dumi'; +import useLocation from './useLocation'; + +export type UseMenuOptions = { + before?: ReactNode; + after?: ReactNode; +}; + +const useMenu = (options: UseMenuOptions = {}): [MenuProps['items'], string] => { + const fullData = useFullSidebarData(); + const { pathname } = useLocation(); + const sidebarData = useSidebarData(); + const { before, after } = options; + + const menuItems = useMemo(() => { + const sidebarItems = [...(sidebarData ?? [])]; + + // 将设计文档未分类的放在最后 + if (pathname.startsWith('/docs/spec')) { + const notGrouped = sidebarItems.splice(0, 1); + sidebarItems.push(...notGrouped); + } + + // 把 /changelog 拼到开发文档中 + if (pathname.startsWith('/docs/react')) { + const changelogData = Object.entries(fullData).find(([key]) => + key.startsWith('/changelog'), + )?.[1]; + if (changelogData) { + sidebarItems.push(...changelogData); + } + } + if (pathname.startsWith('/changelog')) { + const reactDocData = Object.entries(fullData).find(([key]) => + key.startsWith('/docs/react'), + )?.[1]; + if (reactDocData) { + sidebarItems.unshift(...reactDocData); + } + } + + return ( + sidebarItems?.reduce>((result, group) => { + if (group.title) { + // 设计文档特殊处理二级分组 + if (pathname.startsWith('/docs/spec')) { + const childrenGroup = group.children.reduce< + Record[number]['children']> + >((childrenResult, child) => { + const type = (child.frontmatter as any).type ?? 'default'; + if (!childrenResult[type]) { + childrenResult[type] = []; + } + childrenResult[type].push(child); + return childrenResult; + }, {}); + const childItems = []; + childItems.push( + ...childrenGroup.default.map(item => ({ + label: ( + + {before} + {item.title} + {after} + + ), + key: item.link.replace(/(-cn$)/g, ''), + })), + ); + Object.entries(childrenGroup).forEach(([type, children]) => { + if (type !== 'default') { + childItems.push({ + type: 'group', + label: type, + key: type, + children: children?.map(item => ({ + label: ( + + {before} + {item.title} + {after} + + ), + key: item.link.replace(/(-cn$)/g, ''), + })), + }); + } + }); + result.push({ + label: group.title, + key: group.title, + children: childItems, + }); + } else { + result.push({ + type: 'group', + label: group.title, + key: group.title, + children: group.children?.map(item => ({ + label: ( + + {before} + {item.title} + + {(item.frontmatter as any).subtitle} + + {after} + + ), + key: item.link.replace(/(-cn$)/g, ''), + })), + }); + } + } else { + result.push( + ...group.children?.map(item => ({ + label: ( + + {before} + {item.title} + {after} + + ), + key: item.link.replace(/(-cn$)/g, ''), + })), + ); + } + return result; + }, []) ?? [] + ); + }, [sidebarData, fullData, pathname]); + + return [menuItems, pathname]; +}; + +export default useMenu; diff --git a/.dumi/hooks/useSiteToken.ts b/.dumi/hooks/useSiteToken.ts new file mode 100644 index 0000000000..dde187fd24 --- /dev/null +++ b/.dumi/hooks/useSiteToken.ts @@ -0,0 +1,35 @@ +import { theme } from 'antd'; +import { useContext } from 'react'; +import { ConfigContext } from 'antd/es/config-provider'; + +const { useToken } = theme; + +const useSiteToken = () => { + const result = useToken(); + const { getPrefixCls, iconPrefixCls } = useContext(ConfigContext); + const rootPrefixCls = getPrefixCls(); + const { token } = result; + const siteMarkdownCodeBg = token.colorFillTertiary; + + return { + ...result, + token: { + ...token, + headerHeight: 64, + menuItemBorder: 2, + mobileMaxWidth: 767.99, + siteMarkdownCodeBg, + antCls: `.${rootPrefixCls}`, + iconCls: `.${iconPrefixCls}`, + /** 56 */ + marginFarXS: (token.marginXXL / 6) * 7, + /** 80 */ + marginFarSM: (token.marginXXL / 3) * 5, + /** 96 */ + marginFar: token.marginXXL * 1.8, + codeFamily: `'SFMono-Regular', Consolas, 'Liberation Mono', Menlo, Courier, monospace`, + }, + }; +}; + +export default useSiteToken; diff --git a/site/theme/template/NotFound.tsx b/.dumi/pages/404/index.tsx similarity index 71% rename from site/theme/template/NotFound.tsx rename to .dumi/pages/404/index.tsx index 52bbb2438f..4f8d37c526 100644 --- a/site/theme/template/NotFound.tsx +++ b/.dumi/pages/404/index.tsx @@ -1,20 +1,10 @@ import React, { useEffect } from 'react'; -import { Link } from 'bisheng/router'; import { Result, Button } from 'antd'; import { HomeOutlined } from '@ant-design/icons'; -import * as utils from './utils'; +import { Link, useLocation } from 'dumi'; +import * as utils from '../../theme/utils'; export interface NotFoundProps { - location: { - pathname: string; - search: string; - hash: string; - state: any; - action: string; - key: any; - basename: string; - query: Record; - }; router: { push: (pathname: string) => void; replace: (pathname: string) => void; @@ -26,11 +16,8 @@ const DIRECT_MAP: Record = { 'docs/spec/work-with-us': 'docs/resources', }; -export default function NotFound(props: NotFoundProps) { - const { - location: { pathname }, - router, - } = props; +const NotFoundPage: React.FC = ({ router }) => { + const { pathname } = useLocation(); const isZhCN = utils.isZhCN(pathname); @@ -62,11 +49,8 @@ export default function NotFound(props: NotFoundProps) { } /> - diff --git a/components/alert/demo/action.tsx b/components/alert/demo/action.tsx new file mode 100644 index 0000000000..bdce18f934 --- /dev/null +++ b/components/alert/demo/action.tsx @@ -0,0 +1,59 @@ +import React from 'react'; +import { Alert, Button, Space } from 'antd'; + +const App: React.FC = () => ( + + + UNDO + + } + closable + /> + + Detail + + } + /> + + + + } + closable + /> + + + + + } + closable + /> + +); + +export default App; diff --git a/components/alert/demo/banner.md b/components/alert/demo/banner.md index a40fed7017..e1a1993e21 100644 --- a/components/alert/demo/banner.md +++ b/components/alert/demo/banner.md @@ -1,11 +1,3 @@ ---- -order: 6 -iframe: 250 -title: - zh-CN: 顶部公告 - en-US: Banner ---- - ## zh-CN 页面顶部通告形式,默认有图标且 `type` 为 'warning'。 @@ -13,26 +5,3 @@ title: ## en-US Display Alert as a banner at top of page. - -```tsx -import { Alert } from 'antd'; -import React from 'react'; - -const App: React.FC = () => ( - <> - -
- -
- -
- - -); - -export default App; -``` diff --git a/components/alert/demo/banner.tsx b/components/alert/demo/banner.tsx new file mode 100644 index 0000000000..552d6ca9ee --- /dev/null +++ b/components/alert/demo/banner.tsx @@ -0,0 +1,17 @@ +import React from 'react'; +import { Alert, Space } from 'antd'; + +const App: React.FC = () => ( + + + + + + +); + +export default App; diff --git a/components/alert/demo/basic.md b/components/alert/demo/basic.md index 8a28a29877..c2943733a2 100644 --- a/components/alert/demo/basic.md +++ b/components/alert/demo/basic.md @@ -1,10 +1,3 @@ ---- -order: 0 -title: - zh-CN: 基本 - en-US: Basic ---- - ## zh-CN 最简单的用法,适用于简短的警告提示。 @@ -12,18 +5,3 @@ title: ## en-US The simplest usage for short messages. - -```tsx -import { Alert } from 'antd'; -import React from 'react'; - -const App: React.FC = () => ; - -export default App; -``` - - diff --git a/components/alert/demo/basic.tsx b/components/alert/demo/basic.tsx new file mode 100644 index 0000000000..77318d30a2 --- /dev/null +++ b/components/alert/demo/basic.tsx @@ -0,0 +1,6 @@ +import React from 'react'; +import { Alert } from 'antd'; + +const App: React.FC = () => ; + +export default App; diff --git a/components/alert/demo/closable.md b/components/alert/demo/closable.md index 6472985d4e..8d5aa1b3ff 100644 --- a/components/alert/demo/closable.md +++ b/components/alert/demo/closable.md @@ -1,10 +1,3 @@ ---- -order: 2 -title: - zh-CN: 可关闭的警告提示 - en-US: Closable ---- - ## zh-CN 显示关闭按钮,点击可关闭警告提示。 @@ -12,32 +5,3 @@ title: ## en-US To show close button. - -```tsx -import { Alert } from 'antd'; -import React from 'react'; - -const onClose = (e: React.MouseEvent) => { - console.log(e, 'I was closed.'); -}; - -const App: React.FC = () => ( - <> - - - -); - -export default App; -``` diff --git a/components/alert/demo/closable.tsx b/components/alert/demo/closable.tsx new file mode 100644 index 0000000000..2d04f4ebaf --- /dev/null +++ b/components/alert/demo/closable.tsx @@ -0,0 +1,26 @@ +import React from 'react'; +import { Alert, Space } from 'antd'; + +const onClose = (e: React.MouseEvent) => { + console.log(e, 'I was closed.'); +}; + +const App: React.FC = () => ( + + + + +); + +export default App; diff --git a/components/alert/demo/close-text.md b/components/alert/demo/close-text.md index 18d62691e1..969bff8f4c 100644 --- a/components/alert/demo/close-text.md +++ b/components/alert/demo/close-text.md @@ -1,10 +1,3 @@ ---- -order: 5 -title: - zh-CN: 自定义关闭 - en-US: Customized Close Text ---- - ## zh-CN 可以自定义关闭,自定义的文字会替换原先的关闭 `Icon`。 @@ -12,12 +5,3 @@ title: ## en-US Replace the default icon with customized text. - -```tsx -import { Alert } from 'antd'; -import React from 'react'; - -const App: React.FC = () => ; - -export default App; -``` diff --git a/components/alert/demo/close-text.tsx b/components/alert/demo/close-text.tsx new file mode 100644 index 0000000000..f5517f7247 --- /dev/null +++ b/components/alert/demo/close-text.tsx @@ -0,0 +1,6 @@ +import React from 'react'; +import { Alert } from 'antd'; + +const App: React.FC = () => ; + +export default App; diff --git a/components/alert/demo/custom-icon.md b/components/alert/demo/custom-icon.md index 1cad6ab1e7..3720762aa7 100644 --- a/components/alert/demo/custom-icon.md +++ b/components/alert/demo/custom-icon.md @@ -1,11 +1,3 @@ ---- -order: 12 -debug: true -title: - zh-CN: 自定义图标 - en-US: Custom Icon ---- - ## zh-CN 可口的图标让信息类型更加醒目。 @@ -13,51 +5,3 @@ title: ## en-US A relevant icon makes information clearer and more friendly. - -```tsx -import { SmileOutlined } from '@ant-design/icons'; -import { Alert } from 'antd'; -import React from 'react'; - -const icon = ; - -const App: React.FC = () => ( - <> - - - - - - - - - - -); - -export default App; -``` diff --git a/components/alert/demo/custom-icon.tsx b/components/alert/demo/custom-icon.tsx new file mode 100644 index 0000000000..e1767a937e --- /dev/null +++ b/components/alert/demo/custom-icon.tsx @@ -0,0 +1,45 @@ +import React from 'react'; +import { SmileOutlined } from '@ant-design/icons'; +import { Alert, Space } from 'antd'; + +const icon = ; + +const App: React.FC = () => ( + + + + + + + + + + + +); + +export default App; diff --git a/components/alert/demo/description.md b/components/alert/demo/description.md index e89b13355f..cec3c6cbef 100644 --- a/components/alert/demo/description.md +++ b/components/alert/demo/description.md @@ -1,10 +1,3 @@ ---- -order: 3 -title: - zh-CN: 含有辅助性文字介绍 - en-US: Description ---- - ## zh-CN 含有辅助性文字介绍的警告提示。 @@ -12,35 +5,3 @@ title: ## en-US Additional description for alert message. - -```tsx -import { Alert } from 'antd'; -import React from 'react'; - -const App: React.FC = () => ( - <> - - - - - -); - -export default App; -``` diff --git a/components/alert/demo/description.tsx b/components/alert/demo/description.tsx new file mode 100644 index 0000000000..0abd7b08b1 --- /dev/null +++ b/components/alert/demo/description.tsx @@ -0,0 +1,29 @@ +import React from 'react'; +import { Alert, Space } from 'antd'; + +const App: React.FC = () => ( + + + + + + +); + +export default App; diff --git a/components/alert/demo/error-boundary.md b/components/alert/demo/error-boundary.md index aa9f74085f..96d00b8c5a 100644 --- a/components/alert/demo/error-boundary.md +++ b/components/alert/demo/error-boundary.md @@ -1,10 +1,3 @@ ---- -order: 8 -title: - zh-CN: React 错误处理 - en-US: ErrorBoundary ---- - ## zh-CN 友好的 [React 错误处理](https://reactjs.org/blog/2017/07/26/error-handling-in-react-16.html) 包裹组件。 @@ -12,33 +5,3 @@ title: ## en-US ErrorBoundary Component for making error handling easier in [React](https://reactjs.org/blog/2017/07/26/error-handling-in-react-16.html). - -```tsx -import { Alert, Button } from 'antd'; -import React, { useState } from 'react'; - -const { ErrorBoundary } = Alert; -const ThrowError: React.FC = () => { - const [error, setError] = useState(); - const onClick = () => { - setError(new Error('An Uncaught Error')); - }; - - if (error) { - throw error; - } - return ( - - ); -}; - -const App: React.FC = () => ( - - - -); - -export default App; -``` diff --git a/components/alert/demo/error-boundary.tsx b/components/alert/demo/error-boundary.tsx new file mode 100644 index 0000000000..9d90043f8a --- /dev/null +++ b/components/alert/demo/error-boundary.tsx @@ -0,0 +1,27 @@ +import React, { useState } from 'react'; +import { Alert, Button } from 'antd'; + +const { ErrorBoundary } = Alert; +const ThrowError: React.FC = () => { + const [error, setError] = useState(); + const onClick = () => { + setError(new Error('An Uncaught Error')); + }; + + if (error) { + throw error; + } + return ( + + ); +}; + +const App: React.FC = () => ( + + + +); + +export default App; diff --git a/components/alert/demo/icon.md b/components/alert/demo/icon.md index 14e6c0b2ed..8e7b724cfa 100644 --- a/components/alert/demo/icon.md +++ b/components/alert/demo/icon.md @@ -1,10 +1,3 @@ ---- -order: 4 -title: - zh-CN: 图标 - en-US: Icon ---- - ## zh-CN 可口的图标让信息类型更加醒目。 @@ -12,44 +5,3 @@ title: ## en-US A relevant icon will make information clearer and more friendly. - -```tsx -import { Alert } from 'antd'; -import React from 'react'; - -const App: React.FC = () => ( - <> - - - - - - - - - -); - -export default App; -``` diff --git a/components/alert/demo/icon.tsx b/components/alert/demo/icon.tsx new file mode 100644 index 0000000000..bc9baca870 --- /dev/null +++ b/components/alert/demo/icon.tsx @@ -0,0 +1,38 @@ +import React from 'react'; +import { Alert, Space } from 'antd'; + +const App: React.FC = () => ( + + + + + + + + + + +); + +export default App; diff --git a/components/alert/demo/loop-banner.md b/components/alert/demo/loop-banner.md index 02c0b9abac..beff760ef1 100644 --- a/components/alert/demo/loop-banner.md +++ b/components/alert/demo/loop-banner.md @@ -1,10 +1,3 @@ ---- -order: 6.1 -title: - zh-CN: 轮播的公告 - en-US: Loop Banner ---- - ## zh-CN 配合 [react-text-loop-next](https://npmjs.com/package/react-text-loop-next) 或 [react-fast-marquee](https://npmjs.com/package/react-fast-marquee) 实现消息轮播通知栏。 @@ -12,22 +5,3 @@ title: ## en-US Show a loop banner by using with [react-text-loop-next](https://npmjs.com/package/react-text-loop-next) or [react-fast-marquee](https://npmjs.com/package/react-fast-marquee). - -```tsx -import { Alert } from 'antd'; -import React from 'react'; -import Marquee from 'react-fast-marquee'; - -const App: React.FC = () => ( - - I can be a React component, multiple React components, or just some text. - - } - /> -); - -export default App; -``` diff --git a/components/alert/demo/loop-banner.tsx b/components/alert/demo/loop-banner.tsx new file mode 100644 index 0000000000..1f9a460d31 --- /dev/null +++ b/components/alert/demo/loop-banner.tsx @@ -0,0 +1,16 @@ +import React from 'react'; +import { Alert } from 'antd'; +import Marquee from 'react-fast-marquee'; + +const App: React.FC = () => ( + + I can be a React component, multiple React components, or just some text. + + } + /> +); + +export default App; diff --git a/components/alert/demo/smooth-closed.md b/components/alert/demo/smooth-closed.md index c3472104c1..cdc8eb969c 100644 --- a/components/alert/demo/smooth-closed.md +++ b/components/alert/demo/smooth-closed.md @@ -1,10 +1,3 @@ ---- -order: 7 -title: - zh-CN: 平滑地卸载 - en-US: Smoothly Unmount ---- - ## zh-CN 平滑、自然的卸载提示。 @@ -12,27 +5,3 @@ title: ## en-US Smoothly unmount Alert upon close. - -```tsx -import { Alert } from 'antd'; -import React, { useState } from 'react'; - -const App: React.FC = () => { - const [visible, setVisible] = useState(true); - - const handleClose = () => { - setVisible(false); - }; - - return ( -
- {visible ? ( - - ) : null} -

placeholder text here

-
- ); -}; - -export default App; -``` diff --git a/components/alert/demo/smooth-closed.tsx b/components/alert/demo/smooth-closed.tsx new file mode 100644 index 0000000000..e132c1b144 --- /dev/null +++ b/components/alert/demo/smooth-closed.tsx @@ -0,0 +1,22 @@ +import React, { useState } from 'react'; +import { Alert, Switch, Space } from 'antd'; + +const App: React.FC = () => { + const [visible, setVisible] = useState(true); + + const handleClose = () => { + setVisible(false); + }; + + return ( + + {visible && ( + + )} +

click the close button to see the effect

+ +
+ ); +}; + +export default App; diff --git a/components/alert/demo/style.md b/components/alert/demo/style.md index 1817683db3..968d87e0ba 100644 --- a/components/alert/demo/style.md +++ b/components/alert/demo/style.md @@ -1,10 +1,3 @@ ---- -order: 1 -title: - zh-CN: 四种样式 - en-US: More types ---- - ## zh-CN 共有四种样式 `success`、`info`、`warning`、`error`。 @@ -12,25 +5,3 @@ title: ## en-US There are 4 types of Alert: `success`, `info`, `warning`, `error`. - -```tsx -import { Alert } from 'antd'; -import React from 'react'; - -const App: React.FC = () => ( - <> - - - - - -); - -export default App; -``` - - diff --git a/components/alert/demo/style.tsx b/components/alert/demo/style.tsx new file mode 100644 index 0000000000..f89bc3f879 --- /dev/null +++ b/components/alert/demo/style.tsx @@ -0,0 +1,13 @@ +import React from 'react'; +import { Alert, Space } from 'antd'; + +const App: React.FC = () => ( + + + + + + +); + +export default App; diff --git a/components/alert/index.en-US.md b/components/alert/index.en-US.md index 982057c3cd..c14387ac9a 100644 --- a/components/alert/index.en-US.md +++ b/components/alert/index.en-US.md @@ -1,8 +1,12 @@ --- category: Components -type: Feedback title: Alert cover: https://gw.alipayobjects.com/zos/alicdn/8emPa3fjl/Alert.svg +demo: + cols: 2 +group: + title: Feedback + order: 6 --- Alert component for feedback. @@ -12,6 +16,22 @@ Alert component for feedback. - When you need to show alert messages to users. - When you need a persistent static container which is closable by user actions. +## Examples + + +Basic +More types +Closable +Description +Icon +Customized Close Text +Banner +Loop Banner +Smoothly Unmount +ErrorBoundary +Custom Icon +Custom action + ## API | Property | Description | Type | Default | Version | diff --git a/components/alert/index.tsx b/components/alert/index.tsx index cbf7e00407..493ae0f760 100644 --- a/components/alert/index.tsx +++ b/components/alert/index.tsx @@ -1,12 +1,8 @@ import CheckCircleFilled from '@ant-design/icons/CheckCircleFilled'; -import CheckCircleOutlined from '@ant-design/icons/CheckCircleOutlined'; import CloseCircleFilled from '@ant-design/icons/CloseCircleFilled'; -import CloseCircleOutlined from '@ant-design/icons/CloseCircleOutlined'; import CloseOutlined from '@ant-design/icons/CloseOutlined'; import ExclamationCircleFilled from '@ant-design/icons/ExclamationCircleFilled'; -import ExclamationCircleOutlined from '@ant-design/icons/ExclamationCircleOutlined'; import InfoCircleFilled from '@ant-design/icons/InfoCircleFilled'; -import InfoCircleOutlined from '@ant-design/icons/InfoCircleOutlined'; import classNames from 'classnames'; import CSSMotion from 'rc-motion'; import type { ReactElement } from 'react'; @@ -16,6 +12,9 @@ import getDataOrAriaProps from '../_util/getDataOrAriaProps'; import { replaceElement } from '../_util/reactNode'; import ErrorBoundary from './ErrorBoundary'; +// CSSINJS +import useStyle from './style'; + export interface AlertProps { /** Type of Alert styles, options:`success`, `info`, `warning`, `error` */ type?: 'success' | 'info' | 'warning' | 'error'; @@ -55,13 +54,6 @@ const iconMapFilled = { warning: ExclamationCircleFilled, }; -const iconMapOutlined = { - success: CheckCircleOutlined, - info: InfoCircleOutlined, - error: CloseCircleOutlined, - warning: ExclamationCircleOutlined, -}; - interface IconNodeProps { type: AlertProps['type']; icon: AlertProps['icon']; @@ -70,8 +62,8 @@ interface IconNodeProps { } const IconNode: React.FC = props => { - const { description, icon, prefixCls, type } = props; - const iconType = (description ? iconMapOutlined : iconMapFilled)[type!] || null; + const { icon, prefixCls, type } = props; + const iconType = iconMapFilled[type!] || null; if (icon) { return replaceElement(icon, {icon}, () => ({ className: classNames(`${prefixCls}-icon`, { @@ -126,6 +118,7 @@ const Alert: AlertInterface = ({ const ref = React.useRef(); const { getPrefixCls, direction } = React.useContext(ConfigContext); const prefixCls = getPrefixCls('alert', customizePrefixCls); + const [wrapSSR, hashId] = useStyle(prefixCls); const handleClose = (e: React.MouseEvent) => { setClosed(true); @@ -158,11 +151,12 @@ const Alert: AlertInterface = ({ [`${prefixCls}-rtl`]: direction === 'rtl', }, className, + hashId, ); const dataOrAriaProps = getDataOrAriaProps(props); - return ( + return wrapSSR( )} - + , ); }; diff --git a/components/alert/index.zh-CN.md b/components/alert/index.zh-CN.md index ee0a63de85..64aac10e9d 100644 --- a/components/alert/index.zh-CN.md +++ b/components/alert/index.zh-CN.md @@ -1,9 +1,13 @@ --- category: Components subtitle: 警告提示 -type: 反馈 title: Alert cover: https://gw.alipayobjects.com/zos/alicdn/8emPa3fjl/Alert.svg +demo: + cols: 2 +group: + title: 反馈 + order: 6 --- 警告提示,展现需要关注的信息。 @@ -13,26 +17,41 @@ cover: https://gw.alipayobjects.com/zos/alicdn/8emPa3fjl/Alert.svg - 当某个页面需要向用户显示警告的信息时。 - 非浮层的静态展现形式,始终展现,不会自动消失,用户可以点击关闭。 +## 代码演示 + +基本 +四种样式 +可关闭的警告提示 +含有辅助性文字介绍 +图标 +自定义关闭 +顶部公告 +轮播的公告 +平滑地卸载 +React 错误处理 +自定义图标 +操作 + ## API -| 参数 | 说明 | 类型 | 默认值 | 版本 | -| --- | --- | --- | --- | --- | -| action | 自定义操作项 | ReactNode | - | 4.9.0 | -| afterClose | 关闭动画结束后触发的回调函数 | () => void | - | | -| banner | 是否用作顶部公告 | boolean | false | | -| closable | 默认不显示关闭按钮 | boolean | - | | -| closeText | 自定义关闭按钮 | ReactNode | - | | -| closeIcon | 自定义关闭 Icon | ReactNode | `` | 4.18.0 | -| description | 警告提示的辅助性文字介绍 | ReactNode | - | | -| icon | 自定义图标,`showIcon` 为 true 时有效 | ReactNode | - | | -| message | 警告提示内容 | ReactNode | - | | -| showIcon | 是否显示辅助图标 | boolean | false,`banner` 模式下默认值为 true | | -| type | 指定警告提示的样式,有四种选择 `success`、`info`、`warning`、`error` | string | `info`,`banner` 模式下默认值为 `warning` | | -| onClose | 关闭时触发的回调函数 | (e: MouseEvent) => void | - | | +| 参数 | 说明 | 类型 | 默认值 | 版本 | +| ----------- | -------------------------------------------------------------------- | ----------------------- | ----------------------------------------- | ------ | +| action | 自定义操作项 | ReactNode | - | 4.9.0 | +| afterClose | 关闭动画结束后触发的回调函数 | () => void | - | | +| banner | 是否用作顶部公告 | boolean | false | | +| closable | 默认不显示关闭按钮 | boolean | - | | +| closeText | 自定义关闭按钮 | ReactNode | - | | +| closeIcon | 自定义关闭 Icon | ReactNode | `` | 4.18.0 | +| description | 警告提示的辅助性文字介绍 | ReactNode | - | | +| icon | 自定义图标,`showIcon` 为 true 时有效 | ReactNode | - | | +| message | 警告提示内容 | ReactNode | - | | +| showIcon | 是否显示辅助图标 | boolean | false,`banner` 模式下默认值为 true | | +| type | 指定警告提示的样式,有四种选择 `success`、`info`、`warning`、`error` | string | `info`,`banner` 模式下默认值为 `warning` | | +| onClose | 关闭时触发的回调函数 | (e: MouseEvent) => void | - | | ### Alert.ErrorBoundary -| 参数 | 说明 | 类型 | 默认值 | 版本 | -| --- | --- | --- | --- | --- | -| description | 自定义错误内容,如果未指定会展示报错堆栈 | ReactNode | {{ error stack }} | | -| message | 自定义错误标题,如果未指定会展示原生报错信息 | ReactNode | {{ error }} | | +| 参数 | 说明 | 类型 | 默认值 | 版本 | +| ----------- | -------------------------------------------- | --------- | ----------------- | ---- | +| description | 自定义错误内容,如果未指定会展示报错堆栈 | ReactNode | {{ error stack }} | | +| message | 自定义错误标题,如果未指定会展示原生报错信息 | ReactNode | {{ error }} | | diff --git a/components/alert/style/index.less b/components/alert/style/index.less deleted file mode 100644 index 9fa3fa367d..0000000000 --- a/components/alert/style/index.less +++ /dev/null @@ -1,155 +0,0 @@ -@import '../../style/themes/index'; -@import '../../style/mixins/index'; - -@alert-prefix-cls: ~'@{ant-prefix}-alert'; - -.@{alert-prefix-cls} { - .reset-component(); - - position: relative; - display: flex; - align-items: center; - padding: @alert-padding-vertical @alert-padding-horizontal; - word-wrap: break-word; - border-radius: @border-radius-base; - - &-content { - flex: 1; - min-width: 0; - } - - &-icon { - margin-right: @margin-xs; - } - - &-description { - display: none; - font-size: @font-size-base; - line-height: @font-size-base + 8px; - } - - &-success { - background-color: @alert-success-bg-color; - border: @border-width-base @border-style-base @alert-success-border-color; - .@{alert-prefix-cls}-icon { - color: @alert-success-icon-color; - } - } - - &-info { - background-color: @alert-info-bg-color; - border: @border-width-base @border-style-base @alert-info-border-color; - .@{alert-prefix-cls}-icon { - color: @alert-info-icon-color; - } - } - - &-warning { - background-color: @alert-warning-bg-color; - border: @border-width-base @border-style-base @alert-warning-border-color; - .@{alert-prefix-cls}-icon { - color: @alert-warning-icon-color; - } - } - - &-error { - background-color: @alert-error-bg-color; - border: @border-width-base @border-style-base @alert-error-border-color; - - .@{alert-prefix-cls}-icon { - color: @alert-error-icon-color; - } - - .@{alert-prefix-cls}-description > pre { - margin: 0; - padding: 0; - } - } - - &-action { - margin-left: @margin-xs; - } - - &-close-icon { - margin-left: @margin-xs; - padding: 0; - overflow: hidden; - font-size: @font-size-sm; - line-height: @font-size-sm; - background-color: transparent; - border: none; - outline: none; - cursor: pointer; - - .@{iconfont-css-prefix}-close { - color: @alert-close-color; - transition: color 0.3s; - - &:hover { - color: @alert-close-hover-color; - } - } - } - - &-close-text { - color: @alert-close-color; - transition: color 0.3s; - - &:hover { - color: @alert-close-hover-color; - } - } - - &-with-description { - align-items: flex-start; - padding: @alert-with-description-padding; - } - - &-with-description&-no-icon { - padding: @alert-with-description-no-icon-padding-vertical 15px; - } - - &-with-description &-icon { - margin-right: @alert-with-description-padding-vertical; - font-size: @alert-with-description-icon-size; - } - - &-with-description &-message { - display: block; - margin-bottom: 4px; - color: @alert-message-color; - font-size: @font-size-lg; - } - - &-message { - color: @alert-message-color; - } - - &-with-description &-description { - display: block; - } - - &&-motion-leave { - overflow: hidden; - opacity: 1; - transition: max-height 0.3s @ease-in-out-circ, opacity 0.3s @ease-in-out-circ, - padding-top 0.3s @ease-in-out-circ, padding-bottom 0.3s @ease-in-out-circ, - margin-bottom 0.3s @ease-in-out-circ; - } - - &&-motion-leave-active { - max-height: 0; - margin-bottom: 0 !important; - padding-top: 0; - padding-bottom: 0; - opacity: 0; - } - - &-banner { - margin-bottom: 0; - border: 0; - border-radius: 0; - } -} - -@import './rtl'; diff --git a/components/alert/style/index.tsx b/components/alert/style/index.tsx index 3a3ab0de59..0d71fa0824 100644 --- a/components/alert/style/index.tsx +++ b/components/alert/style/index.tsx @@ -1,2 +1,240 @@ -import '../../style/index.less'; -import './index.less'; +import type { CSSInterpolation, CSSObject } from '@ant-design/cssinjs'; +import type { FullToken, GenerateStyle } from '../../theme'; +import { genComponentStyleHook, mergeToken } from '../../theme'; +import { resetComponent } from '../../style'; + +export interface ComponentToken {} + +type AlertToken = FullToken<'Alert'> & { + alertIconSizeLG: number; + alertPaddingHorizontal: number; +}; + +const genAlertTypeStyle = ( + bgColor: string, + borderColor: string, + iconColor: string, + token: AlertToken, + alertCls: string, +): CSSObject => ({ + backgroundColor: bgColor, + border: `${token.lineWidth}px ${token.lineType} ${borderColor}`, + [`${alertCls}-icon`]: { + color: iconColor, + }, +}); + +export const genBaseStyle: GenerateStyle = (token: AlertToken): CSSObject => { + const { + componentCls, + motionDurationSlow: duration, + marginXS, + marginSM, + fontSize, + fontSizeLG, + lineHeight, + borderRadiusLG: borderRadius, + motionEaseInOutCirc, + alertIconSizeLG, + colorText, + paddingContentVerticalSM, + alertPaddingHorizontal, + paddingMD, + paddingContentHorizontalLG, + } = token; + + return { + [componentCls]: { + ...resetComponent(token), + position: 'relative', + display: 'flex', + alignItems: 'center', + padding: `${paddingContentVerticalSM}px ${alertPaddingHorizontal}px`, // Fixed horizontal padding here. + wordWrap: 'break-word', + borderRadius, + + '&&-rtl': { + direction: 'rtl', + }, + + [`${componentCls}-content`]: { + flex: 1, + minWidth: 0, + }, + + [`${componentCls}-icon`]: { + marginInlineEnd: marginXS, + lineHeight: 0, + }, + + [`&-description`]: { + display: 'none', + fontSize, + lineHeight, + }, + + '&-message': { + color: colorText, + }, + + '&&-motion-leave': { + overflow: 'hidden', + opacity: 1, + transition: `max-height ${duration} ${motionEaseInOutCirc}, opacity ${duration} ${motionEaseInOutCirc}, + padding-top ${duration} ${motionEaseInOutCirc}, padding-bottom ${duration} ${motionEaseInOutCirc}, + margin-bottom ${duration} ${motionEaseInOutCirc}`, + }, + + '&&-motion-leave-active': { + maxHeight: 0, + marginBottom: '0 !important', + paddingTop: 0, + paddingBottom: 0, + opacity: 0, + }, + }, + + [`${componentCls}-with-description`]: { + alignItems: 'flex-start', + paddingInline: paddingContentHorizontalLG, + paddingBlock: paddingMD, + + [`${componentCls}-icon`]: { + marginInlineEnd: marginSM, + fontSize: alertIconSizeLG, + lineHeight: 0, + }, + + [`${componentCls}-message`]: { + display: 'block', + marginBottom: marginXS, + color: colorText, + fontSize: fontSizeLG, + }, + + [`${componentCls}-description`]: { + display: 'block', + }, + }, + + [`${componentCls}-banner`]: { + marginBottom: 0, + border: '0 !important', + borderRadius: 0, + }, + }; +}; + +export const genTypeStyle: GenerateStyle = (token: AlertToken): CSSObject => { + const { + componentCls, + + colorSuccess, + colorSuccessBorder, + colorSuccessBg, + + colorWarning, + colorWarningBorder, + colorWarningBg, + + colorError, + colorErrorBorder, + colorErrorBg, + + colorInfo, + colorInfoBorder, + colorInfoBg, + } = token; + + return { + [componentCls]: { + '&-success': genAlertTypeStyle( + colorSuccessBg, + colorSuccessBorder, + colorSuccess, + token, + componentCls, + ), + '&-info': genAlertTypeStyle(colorInfoBg, colorInfoBorder, colorInfo, token, componentCls), + '&-warning': genAlertTypeStyle( + colorWarningBg, + colorWarningBorder, + colorWarning, + token, + componentCls, + ), + '&-error': { + ...genAlertTypeStyle(colorErrorBg, colorErrorBorder, colorError, token, componentCls), + [`${componentCls}-description > pre`]: { + margin: 0, + padding: 0, + }, + }, + }, + }; +}; + +export const genActionStyle: GenerateStyle = (token: AlertToken): CSSObject => { + const { + componentCls, + iconCls, + motionDurationMid, + marginXS, + fontSizeIcon, + colorIcon, + colorIconHover, + } = token; + + return { + [componentCls]: { + [`&-action`]: { + marginInlineStart: marginXS, + }, + + [`${componentCls}-close-icon`]: { + marginInlineStart: marginXS, + padding: 0, + overflow: 'hidden', + fontSize: fontSizeIcon, + lineHeight: `${fontSizeIcon}px`, + backgroundColor: 'transparent', + border: 'none', + outline: 'none', + cursor: 'pointer', + + [`${iconCls}-close`]: { + color: colorIcon, + transition: `color ${motionDurationMid}`, + '&:hover': { + color: colorIconHover, + }, + }, + }, + + '&-close-text': { + color: colorIcon, + transition: `color ${motionDurationMid}`, + '&:hover': { + color: colorIconHover, + }, + }, + }, + }; +}; + +export const genAlertStyle: GenerateStyle = (token: AlertToken): CSSInterpolation => [ + genBaseStyle(token), + genTypeStyle(token), + genActionStyle(token), +]; + +export default genComponentStyleHook('Alert', (token) => { + const { fontSizeHeading3 } = token; + + const alertToken = mergeToken(token, { + alertIconSizeLG: fontSizeHeading3, + alertPaddingHorizontal: 12, // Fixed value here. + }); + + return [genAlertStyle(alertToken)]; +}); diff --git a/components/alert/style/rtl.less b/components/alert/style/rtl.less deleted file mode 100644 index 31d63146c0..0000000000 --- a/components/alert/style/rtl.less +++ /dev/null @@ -1,40 +0,0 @@ -.@{alert-prefix-cls} { - &&-rtl { - direction: rtl; - } - - &-icon { - .@{alert-prefix-cls}-rtl & { - margin-right: auto; - margin-left: @margin-xs; - } - } - - &-action { - .@{alert-prefix-cls}-rtl & { - margin-right: @margin-xs; - margin-left: auto; - } - } - - &-close-icon { - .@{alert-prefix-cls}-rtl & { - margin-right: @margin-xs; - margin-left: auto; - } - } - - &-with-description { - .@{alert-prefix-cls}-rtl& { - padding-right: @alert-with-description-icon-size; - padding-left: @alert-with-description-padding-vertical; - } - - .@{alert-prefix-cls}-icon { - .@{alert-prefix-cls}-rtl& { - margin-right: auto; - margin-left: @alert-with-description-padding-vertical; - } - } - } -} diff --git a/components/anchor/Anchor.tsx b/components/anchor/Anchor.tsx index 1064dbec99..9c377a3324 100644 --- a/components/anchor/Anchor.tsx +++ b/components/anchor/Anchor.tsx @@ -8,6 +8,8 @@ import getScroll from '../_util/getScroll'; import scrollTo from '../_util/scrollTo'; import AnchorContext from './context'; +import useStyle from './style'; + export type AnchorContainer = HTMLElement | Window; function getDefaultContainer() { @@ -63,6 +65,7 @@ export interface AnchorProps { interface InternalAnchorProps extends AnchorProps { anchorPrefixCls: string; + rootClassName: string; } export interface AnchorState { @@ -87,8 +90,9 @@ export interface AntAnchor { ) => void; } -const AnchorContent: React.FC = props => { +const AnchorContent: React.FC = (props) => { const { + rootClassName, anchorPrefixCls: prefixCls, className = '', style, @@ -119,18 +123,18 @@ const AnchorContent: React.FC = props => { const dependencyListItem: React.DependencyList[number] = JSON.stringify(links); const registerLink = React.useCallback( - link => { + (link) => { if (!links.includes(link)) { - setLinks(prev => [...prev, link]); + setLinks((prev) => [...prev, link]); } }, [dependencyListItem], ); const unregisterLink = React.useCallback( - link => { + (link) => { if (links.includes(link)) { - setLinks(prev => prev.filter(i => i !== link)); + setLinks((prev) => prev.filter((i) => i !== link)); } }, [dependencyListItem], @@ -141,14 +145,15 @@ const AnchorContent: React.FC = props => { `.${prefixCls}-link-title-active`, ); if (linkNode && spanLinkNode.current) { - spanLinkNode.current.style.top = `${linkNode.offsetTop + linkNode.clientHeight / 2 - 4.5}px`; + spanLinkNode.current.style.top = `${linkNode.offsetTop + linkNode.clientHeight / 2}px`; + spanLinkNode.current.style.height = `${linkNode.clientHeight}px`; } }; const getInternalCurrentAnchor = (_links: string[], _offsetTop = 0, _bounds = 5): string => { const linkSections: Section[] = []; const container = getCurrentContainer(); - _links.forEach(link => { + _links.forEach((link) => { const sharpLinkMatch = sharpMatcherRegx.exec(link?.toString()); if (!sharpLinkMatch) { return; @@ -200,7 +205,7 @@ const AnchorContent: React.FC = props => { }, [dependencyListItem, targetOffset, offsetTop]); const handleScrollTo = React.useCallback<(link: string) => void>( - link => { + (link) => { setCurrentActiveLink(link); const container = getCurrentContainer(); const scrollTop = getScroll(container, true); @@ -235,6 +240,7 @@ const AnchorContent: React.FC = props => { ); const wrapperClass = classNames( + rootClassName, `${prefixCls}-wrapper`, { [`${prefixCls}-rtl`]: direction === 'rtl', @@ -305,11 +311,16 @@ const AnchorContent: React.FC = props => { ); }; -const Anchor: React.FC = props => { +const Anchor: React.FC = (props) => { const { prefixCls: customizePrefixCls } = props; const { getPrefixCls } = React.useContext(ConfigContext); const anchorPrefixCls = getPrefixCls('anchor', customizePrefixCls); - return ; + + const [wrapSSR, hashId] = useStyle(anchorPrefixCls); + + return wrapSSR( + , + ); }; export default Anchor; diff --git a/components/anchor/__tests__/__snapshots__/demo-extend.test.ts.snap b/components/anchor/__tests__/__snapshots__/demo-extend.test.ts.snap index 3d65031817..582b236f77 100644 --- a/components/anchor/__tests__/__snapshots__/demo-extend.test.ts.snap +++ b/components/anchor/__tests__/__snapshots__/demo-extend.test.ts.snap @@ -1,6 +1,6 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP -exports[`renders ./components/anchor/demo/basic.md extend context correctly 1`] = ` +exports[`renders ./components/anchor/demo/basic.tsx extend context correctly 1`] = `
`; -exports[`renders ./components/anchor/demo/customizeHighlight.md extend context correctly 1`] = ` +exports[`renders ./components/anchor/demo/customizeHighlight.tsx extend context correctly 1`] = ` `; -exports[`renders ./components/anchor/demo/onChange.md extend context correctly 1`] = ` +exports[`renders ./components/anchor/demo/onChange.tsx extend context correctly 1`] = ` `; -exports[`renders ./components/anchor/demo/onClick.md extend context correctly 1`] = ` +exports[`renders ./components/anchor/demo/onClick.tsx extend context correctly 1`] = ` `; -exports[`renders ./components/anchor/demo/static.md extend context correctly 1`] = ` +exports[`renders ./components/anchor/demo/static.tsx extend context correctly 1`] = ` `; -exports[`renders ./components/anchor/demo/targetOffset.md extend context correctly 1`] = ` +exports[`renders ./components/anchor/demo/targetOffset.tsx extend context correctly 1`] = `
API @@ -432,7 +432,7 @@ exports[`renders ./components/anchor/demo/targetOffset.md extend context correct > Anchor Props @@ -443,7 +443,7 @@ exports[`renders ./components/anchor/demo/targetOffset.md extend context correct > Link Props diff --git a/components/anchor/__tests__/__snapshots__/demo.test.ts.snap b/components/anchor/__tests__/__snapshots__/demo.test.ts.snap index b6daa7f7ad..2b3a5ebb0f 100644 --- a/components/anchor/__tests__/__snapshots__/demo.test.ts.snap +++ b/components/anchor/__tests__/__snapshots__/demo.test.ts.snap @@ -1,6 +1,6 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP -exports[`renders ./components/anchor/demo/basic.md correctly 1`] = ` +exports[`renders ./components/anchor/demo/basic.tsx correctly 1`] = `
`; -exports[`renders ./components/anchor/demo/customizeHighlight.md correctly 1`] = ` +exports[`renders ./components/anchor/demo/customizeHighlight.tsx correctly 1`] = ` `; -exports[`renders ./components/anchor/demo/onChange.md correctly 1`] = ` +exports[`renders ./components/anchor/demo/onChange.tsx correctly 1`] = ` `; -exports[`renders ./components/anchor/demo/onClick.md correctly 1`] = ` +exports[`renders ./components/anchor/demo/onClick.tsx correctly 1`] = ` `; -exports[`renders ./components/anchor/demo/static.md correctly 1`] = ` +exports[`renders ./components/anchor/demo/static.tsx correctly 1`] = ` `; -exports[`renders ./components/anchor/demo/targetOffset.md correctly 1`] = ` +exports[`renders ./components/anchor/demo/targetOffset.tsx correctly 1`] = `
API @@ -432,7 +432,7 @@ exports[`renders ./components/anchor/demo/targetOffset.md correctly 1`] = ` > Anchor Props @@ -443,7 +443,7 @@ exports[`renders ./components/anchor/demo/targetOffset.md correctly 1`] = ` > Link Props diff --git a/components/anchor/demo/basic.md b/components/anchor/demo/basic.md index 9fa6d7a78c..ab36144421 100644 --- a/components/anchor/demo/basic.md +++ b/components/anchor/demo/basic.md @@ -1,10 +1,3 @@ ---- -order: 0 -title: - zh-CN: 基本 - en-US: Basic ---- - ## zh-CN 最简单的用法。 @@ -13,26 +6,6 @@ title: The simplest usage. -```tsx -import { Anchor } from 'antd'; -import React from 'react'; - -const { Link } = Anchor; - -const App: React.FC = () => ( - - - - - - - - -); - -export default App; -``` - diff --git a/components/badge/demo/basic.tsx b/components/badge/demo/basic.tsx new file mode 100644 index 0000000000..eae6e0f1f7 --- /dev/null +++ b/components/badge/demo/basic.tsx @@ -0,0 +1,19 @@ +import React from 'react'; +import { ClockCircleOutlined } from '@ant-design/icons'; +import { Avatar, Badge, Space } from 'antd'; + +const App: React.FC = () => ( + + + + + + + + }> + + + +); + +export default App; diff --git a/components/badge/demo/change.md b/components/badge/demo/change.md index c9b4f43b93..d7a0e42631 100644 --- a/components/badge/demo/change.md +++ b/components/badge/demo/change.md @@ -1,10 +1,3 @@ ---- -order: 4 -title: - zh-CN: 动态 - en-US: Dynamic ---- - ## zh-CN 展示动态变化的效果。 @@ -12,63 +5,3 @@ title: ## en-US The count will be animated as it changes. - -```tsx -import { MinusOutlined, PlusOutlined, QuestionOutlined } from '@ant-design/icons'; -import { Avatar, Badge, Button, Divider, Switch } from 'antd'; -import React, { useState } from 'react'; - -const ButtonGroup = Button.Group; - -const App: React.FC = () => { - const [count, setCount] = useState(5); - const [show, setShow] = useState(true); - - const increase = () => { - setCount(count + 1); - }; - - const decline = () => { - let newCount = count - 1; - if (newCount < 0) { - newCount = 0; - } - setCount(newCount); - }; - - const random = () => { - const newCount = Math.floor(Math.random() * 100); - setCount(newCount); - }; - - const onChange = (checked: boolean) => { - setShow(checked); - }; - - return ( - <> - - - - - - - - - - - - - - - ); -}; - -export default App; -``` diff --git a/components/badge/demo/change.tsx b/components/badge/demo/change.tsx new file mode 100644 index 0000000000..aec8bbbf61 --- /dev/null +++ b/components/badge/demo/change.tsx @@ -0,0 +1,54 @@ +import React, { useState } from 'react'; +import { MinusOutlined, PlusOutlined, QuestionOutlined } from '@ant-design/icons'; +import { Avatar, Badge, Button, Switch, Space } from 'antd'; + +const ButtonGroup = Button.Group; + +const App: React.FC = () => { + const [count, setCount] = useState(5); + const [show, setShow] = useState(true); + + const increase = () => { + setCount(count + 1); + }; + + const decline = () => { + let newCount = count - 1; + if (newCount < 0) { + newCount = 0; + } + setCount(newCount); + }; + + const random = () => { + const newCount = Math.floor(Math.random() * 100); + setCount(newCount); + }; + + const onChange = (checked: boolean) => { + setShow(checked); + }; + + return ( + + + + + + + , - , - , -
, - , - , -] -`; - -exports[`renders ./components/button/demo/block.md extend context correctly 1`] = ` -Array [ - , - , - , - , -] -`; - -exports[`renders ./components/button/demo/chinese-chars-loading.md extend context correctly 1`] = ` -Array [ - , - , - , - , - , - , -] -`; - -exports[`renders ./components/button/demo/danger.md extend context correctly 1`] = ` -Array [ - , - , - , - , - , -] -`; - -exports[`renders ./components/button/demo/disabled.md extend context correctly 1`] = ` -Array [ - , - , -
, - , - , -
, - , - , -
, - , - , -
, - , - , -
, - , - , -
, - , - , -
, - , - , -
- - -
, -] -`; - -exports[`renders ./components/button/demo/ghost.md extend context correctly 1`] = ` +exports[`renders ./components/button/demo/basic.tsx extend context correctly 1`] = `
- - +
+
- - Default - - - +
+
- - Dashed - - - +
+
- - Danger - - + +
+
+ +
`; -exports[`renders ./components/button/demo/icon.md extend context correctly 1`] = ` +exports[`renders ./components/button/demo/block.tsx extend context correctly 1`] = ` +
+
+ +
+
+ +
+
+ +
+
+ +
+
+`; + +exports[`renders ./components/button/demo/chinese-chars-loading.tsx extend context correctly 1`] = ` +
+
+ +
+
+ +
+
+ +
+
+ +
+
+ +
+
+ +
+
+`; + +exports[`renders ./components/button/demo/danger.tsx extend context correctly 1`] = ` +
+
+ +
+
+ +
+
+ +
+
+ +
+
+ +
+
+`; + +exports[`renders ./components/button/demo/debug-icon.tsx extend context correctly 1`] = ` Array [ -
, + , -] +
+
`; -exports[`renders ./components/button/demo/multiple.md extend context correctly 1`] = ` -Array [ - , - , +exports[`renders ./components/button/demo/multiple.tsx extend context correctly 1`] = ` +
+
+
-
-
+
+
+ + +
+
, ] `; diff --git a/components/button/__tests__/__snapshots__/demo.test.ts.snap b/components/button/__tests__/__snapshots__/demo.test.ts.snap index e9daddc98e..24d85b4188 100644 --- a/components/button/__tests__/__snapshots__/demo.test.ts.snap +++ b/components/button/__tests__/__snapshots__/demo.test.ts.snap @@ -1,1008 +1,1508 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP -exports[`renders ./components/button/demo/basic.md correctly 1`] = ` -Array [ - , - , - , -
, - , - , -] -`; - -exports[`renders ./components/button/demo/block.md correctly 1`] = ` -Array [ - , - , - , - , -] -`; - -exports[`renders ./components/button/demo/chinese-chars-loading.md correctly 1`] = ` -Array [ - , - , - , - , - , - , -] -`; - -exports[`renders ./components/button/demo/danger.md correctly 1`] = ` -Array [ - , - , - , - , - , -] -`; - -exports[`renders ./components/button/demo/disabled.md correctly 1`] = ` -Array [ - , - , -
, - , - , -
, - , - , -
, - , - , -
, - , - , -
, - , - , -
, - , - , -
, - , - , +exports[`renders ./components/button/demo/basic.tsx correctly 1`] = ` +
+
+
+
+
+ +
+
+ +
+
+ +
+
+`; + +exports[`renders ./components/button/demo/block.tsx correctly 1`] = ` +
+
+ +
+
+ +
+
+ +
+
+ +
+
+`; + +exports[`renders ./components/button/demo/chinese-chars-loading.tsx correctly 1`] = ` +
+
+ +
+
+ +
+
+ +
+
+ +
+
+ +
+
+ +
+
+`; + +exports[`renders ./components/button/demo/danger.tsx correctly 1`] = ` +
+
+ +
+
+ +
+
+ +
+
+ +
+
+ +
+
+`; + +exports[`renders ./components/button/demo/debug-icon.tsx correctly 1`] = ` +Array [ +
+ + + +
, + , +
+
+
+
+ +
+
+ +
+
+ +
+
+ +
+
+ +
+
+
+
+
+
+ +
+
+ +
+
+ +
+
+ +
+ +
+
, ] `; -exports[`renders ./components/button/demo/ghost.md correctly 1`] = ` +exports[`renders ./components/button/demo/disabled.tsx correctly 1`] = `
- - +
+
+ +
+
+
+
- - Default - - - +
+
+ +
+
+
+
- - Dashed - - - +
+
+ +
+
+
+
- - Danger - - +
+
+ +
+
+ +
+
+
+
+
+
+ +
+
+ +
+
+
+
+
+
+ +
+
+ +
+
+
+
+
+
+ +
+
+ +
+
+
+
+
+
+ +
+
+ +
+
+
+
+
+
+ +
+
+ +
+
+
`; -exports[`renders ./components/button/demo/icon.md correctly 1`] = ` -Array [ - , - +
+
- - A - - , - , - +
+
- - - - , - +
+
- - - - - Search - - , -
, - , - , - , - , - - - - - , -
, -
, - , - , - , - , - , -
, - , - , - , - , - - - - - , -] + + Danger + + +
+ `; -exports[`renders ./components/button/demo/legacy-group.md correctly 1`] = ` +exports[`renders ./components/button/demo/icon.tsx correctly 1`] = ` +
+
+
+
+ +
+
+ +
+
+ +
+
+ +
+
+ +
+
+
+
+
+
+ +
+
+ +
+
+ +
+
+ +
+ +
+
+
+`; + +exports[`renders ./components/button/demo/legacy-group.tsx correctly 1`] = ` Array [
- -
-
- +
+
- - + + - - - Loading - - -
-
- +
+
- - + + - - + +
-
, +
- -
-
- -
-
- +
+
- - - - + + + + Click me! + + +
+
+ +
- , -] + + `; -exports[`renders ./components/button/demo/multiple.md correctly 1`] = ` -Array [ - , - , +exports[`renders ./components/button/demo/multiple.tsx correctly 1`] = ` +
+
+
-
, -] +
+
+
+ + +
+
+ `; -exports[`renders ./components/button/demo/size.md correctly 1`] = ` +exports[`renders ./components/button/demo/size.tsx correctly 1`] = ` Array [
, -
, -
, - , - , - , -
, - , -
, - , - , - + +
+ +
+
+ +
+ + +
- - - , - +
+
- - - - Download - - , - , +
+ +
+
+ +
+
+ +
+
+ +
+
+ +
+
+ + , ] `; diff --git a/components/button/__tests__/image.test.ts b/components/button/__tests__/image.test.ts index 69d7f68c78..d2c1421bec 100644 --- a/components/button/__tests__/image.test.ts +++ b/components/button/__tests__/image.test.ts @@ -1,5 +1,5 @@ import { imageDemoTest } from '../../../tests/shared/imageTest'; describe('Button image', () => { - imageDemoTest('button', { skip: ['loading.md'] }); + imageDemoTest('button', { skip: ['loading.tsx'] }); }); diff --git a/components/button/__tests__/wave.test.tsx b/components/button/__tests__/wave.test.tsx index 126e387be8..767d4e8b32 100644 --- a/components/button/__tests__/wave.test.tsx +++ b/components/button/__tests__/wave.test.tsx @@ -3,7 +3,7 @@ import Button from '..'; import { fireEvent, render, sleep, assertsExist } from '../../../tests/utils'; // Mock Wave ref -let waveInstanceMock: InstanceType | null; +let waveInstanceMock: any; jest.mock('../../_util/wave', () => { const Wave: typeof import('../../_util/wave') = jest.requireActual('../../_util/wave'); const WaveComponent = Wave.default; @@ -85,7 +85,6 @@ describe('click wave effect', () => { fireEvent.click(wrapper.container.querySelector('.ant-btn')!); await sleep(10); expect(resetEffect).toHaveBeenCalledTimes(2); - // @ts-expect-error: Property 'animationStart' is private and only accessible within class 'Wave'.ts(2341) waveInstanceMock.animationStart = false; fireEvent(wrapper.container.querySelector('.ant-btn')!, new Event('transitionstart')); expect(resetEffect).toHaveBeenCalledTimes(3); diff --git a/components/button/button-group.tsx b/components/button/button-group.tsx index 4344a2bee6..23e9a20788 100644 --- a/components/button/button-group.tsx +++ b/components/button/button-group.tsx @@ -2,6 +2,7 @@ import classNames from 'classnames'; import * as React from 'react'; import { ConfigContext } from '../config-provider'; import type { SizeType } from '../config-provider/SizeContext'; +import { useToken } from '../theme'; import warning from '../_util/warning'; export interface ButtonGroupProps { @@ -20,6 +21,9 @@ const ButtonGroup: React.FC = props => { const { prefixCls: customizePrefixCls, size, className, ...others } = props; const prefixCls = getPrefixCls('btn-group', customizePrefixCls); + // Here we only need hashId + const [, , hashId] = useToken(); + // large => lg // small => sm let sizeCls = ''; @@ -44,6 +48,7 @@ const ButtonGroup: React.FC = props => { [`${prefixCls}-rtl`]: direction === 'rtl', }, className, + hashId, ); return ( diff --git a/components/button/button.tsx b/components/button/button.tsx index c6be750513..04a5bbd785 100644 --- a/components/button/button.tsx +++ b/components/button/button.tsx @@ -15,6 +15,9 @@ import Wave from '../_util/wave'; import Group, { GroupSizeContext } from './button-group'; import LoadingIcon from './LoadingIcon'; +// CSSINJS +import useStyle from './style'; + const rxTwoCNChar = /^[\u4e00-\u9fa5]{2}$/; const isTwoCNChar = rxTwoCNChar.test.bind(rxTwoCNChar); function isString(str: any) { @@ -157,6 +160,12 @@ const InternalButton: React.ForwardRefRenderFunction = (pr ...rest } = props; + const { getPrefixCls, autoInsertSpaceInButton, direction } = React.useContext(ConfigContext); + const prefixCls = getPrefixCls('btn', customizePrefixCls); + + // Style + const [wrapSSR, hashId] = useStyle(prefixCls); + const size = React.useContext(SizeContext); // ===================== Disabled ===================== const disabled = React.useContext(DisabledContext); @@ -165,7 +174,6 @@ const InternalButton: React.ForwardRefRenderFunction = (pr const groupSize = React.useContext(GroupSizeContext); const [innerLoading, setLoading] = React.useState(!!loading); const [hasTwoCNChar, setHasTwoCNChar] = React.useState(false); - const { getPrefixCls, autoInsertSpaceInButton, direction } = React.useContext(ConfigContext); const buttonRef = (ref as any) || React.createRef(); const isNeedInserted = () => React.Children.count(children) === 1 && !icon && !isUnBorderedButtonType(type); @@ -234,7 +242,6 @@ const InternalButton: React.ForwardRefRenderFunction = (pr "`link` or `text` button can't be a `ghost` button.", ); - const prefixCls = getPrefixCls('btn', customizePrefixCls); const autoInsertSpace = autoInsertSpaceInButton !== false; const { compactSize, compactItemClassnames } = useCompactItemContext(prefixCls, direction); @@ -248,6 +255,7 @@ const InternalButton: React.ForwardRefRenderFunction = (pr const classes = classNames( prefixCls, + hashId, { [`${prefixCls}-${shape}`]: shape !== 'default' && shape, // Note: Shape also has `default` [`${prefixCls}-${type}`]: type, @@ -278,15 +286,15 @@ const InternalButton: React.ForwardRefRenderFunction = (pr : null; if (linkButtonRestProps.href !== undefined) { - return ( + return wrapSSR( {iconNode} {kids} - + , ); } - const buttonNode = ( + let buttonNode = ( ); - if (isUnBorderedButtonType(type)) { - return buttonNode; + if (!isUnBorderedButtonType(type)) { + buttonNode = {buttonNode}; } - return {buttonNode}; + return wrapSSR(buttonNode); }; const Button = React.forwardRef(InternalButton) as CompoundedComponent; diff --git a/components/button/demo/basic.md b/components/button/demo/basic.md index 5084b0bf75..f42f59988c 100644 --- a/components/button/demo/basic.md +++ b/components/button/demo/basic.md @@ -1,10 +1,3 @@ ---- -order: 0 -title: - zh-CN: 按钮类型 - en-US: Type ---- - ## zh-CN 按钮有五种类型:主按钮、次按钮、虚线按钮、文本按钮和链接按钮。主按钮在同一个操作区域最多出现一次。 @@ -12,21 +5,3 @@ title: ## en-US There are `primary` button, `default` button, `dashed` button, `text` button and `link` button in antd. - -```tsx -import { Button } from 'antd'; -import React from 'react'; - -const App: React.FC = () => ( - <> - - - -
- - - -); - -export default App; -``` diff --git a/components/button/demo/basic.tsx b/components/button/demo/basic.tsx new file mode 100644 index 0000000000..da2b2fc5bd --- /dev/null +++ b/components/button/demo/basic.tsx @@ -0,0 +1,14 @@ +import React from 'react'; +import { Button, Space } from 'antd'; + +const App: React.FC = () => ( + + + + + + + +); + +export default App; diff --git a/components/button/demo/block.md b/components/button/demo/block.md index a642647506..746d5c3664 100644 --- a/components/button/demo/block.md +++ b/components/button/demo/block.md @@ -1,10 +1,3 @@ ---- -order: 10 -title: - zh-CN: Block 按钮 - en-US: Block Button ---- - ## zh-CN `block` 属性将使按钮适合其父宽度。 @@ -12,25 +5,3 @@ title: ## en-US `block` property will make the button fit to its parent width. - -```tsx -import { Button } from 'antd'; -import React from 'react'; - -const App: React.FC = () => ( - <> - - - - - -); - -export default App; -``` diff --git a/components/button/demo/block.tsx b/components/button/demo/block.tsx new file mode 100644 index 0000000000..89d9a2136f --- /dev/null +++ b/components/button/demo/block.tsx @@ -0,0 +1,19 @@ +import React from 'react'; +import { Button, Space } from 'antd'; + +const App: React.FC = () => ( + + + + + + +); + +export default App; diff --git a/components/button/demo/chinese-chars-loading.md b/components/button/demo/chinese-chars-loading.md index e72d8bc89d..71cdb2191f 100644 --- a/components/button/demo/chinese-chars-loading.md +++ b/components/button/demo/chinese-chars-loading.md @@ -1,11 +1,3 @@ ---- -order: 100 -title: - zh-CN: 加载中状态 bug 还原 - en-US: Loading style bug -debug: true ---- - ## zh-CN https://github.com/ant-design/ant-design/issues/36165 @@ -13,34 +5,3 @@ https://github.com/ant-design/ant-design/issues/36165 ## en-US https://github.com/ant-design/ant-design/issues/36165 - -```jsx -import { PoweroffOutlined } from '@ant-design/icons'; -import { Button } from 'antd'; -import React from 'react'; - -const Text1 = () => '部署'; -const Text2 = () => 部署; -const Text3 = () => 'Submit'; - -const App = () => ( - <> - - - - - - - -); - -export default App; -``` diff --git a/components/button/demo/chinese-chars-loading.tsx b/components/button/demo/chinese-chars-loading.tsx new file mode 100644 index 0000000000..635513f039 --- /dev/null +++ b/components/button/demo/chinese-chars-loading.tsx @@ -0,0 +1,28 @@ +import React from 'react'; +import { PoweroffOutlined } from '@ant-design/icons'; +import { Button, Space } from 'antd'; + +const Text1 = () => '部署'; +const Text2 = () => 部署; +const Text3 = () => 'Submit'; + +const App = () => ( + + + + + + + + +); + +export default App; diff --git a/components/button/demo/danger.md b/components/button/demo/danger.md index e12042bd8c..ca88b6475b 100644 --- a/components/button/demo/danger.md +++ b/components/button/demo/danger.md @@ -1,10 +1,3 @@ ---- -order: 9 -title: - zh-CN: 危险按钮 - en-US: Danger Buttons ---- - ## zh-CN 在 4.0 之后,危险成为一种按钮属性而不是按钮类型。 @@ -12,28 +5,3 @@ title: ## en-US `danger` is a property of button after antd 4.0. - -```tsx -import { Button } from 'antd'; -import React from 'react'; - -const App: React.FC = () => ( - <> - - - - - - -); - -export default App; -``` diff --git a/components/button/demo/danger.tsx b/components/button/demo/danger.tsx new file mode 100644 index 0000000000..c00046a9b7 --- /dev/null +++ b/components/button/demo/danger.tsx @@ -0,0 +1,22 @@ +import React from 'react'; +import { Button, Space } from 'antd'; + +const App: React.FC = () => ( + + + + + + + +); + +export default App; diff --git a/components/button/demo/debug-icon.md b/components/button/demo/debug-icon.md new file mode 100644 index 0000000000..c0c0fc5dc7 --- /dev/null +++ b/components/button/demo/debug-icon.md @@ -0,0 +1,7 @@ +## zh-CN + +调试使用 + +## en-US + +Debug usage diff --git a/components/button/demo/debug-icon.tsx b/components/button/demo/debug-icon.tsx new file mode 100644 index 0000000000..e1b6d6334c --- /dev/null +++ b/components/button/demo/debug-icon.tsx @@ -0,0 +1,55 @@ +import React, { useState } from 'react'; +import { SearchOutlined } from '@ant-design/icons'; +import { Button, Tooltip, ConfigProvider, Radio, Divider, Space } from 'antd'; +import type { SizeType } from 'antd/es/config-provider/SizeContext'; + +const App: React.FC = () => { + const [size, setSize] = useState('large'); + + return ( + <> + setSize(e.target.value)}> + Large + Default + Small + + + Preview + + + + + + + + + + + + + + + + - -
- - -
- - -
- - -
- - -
- - -
- - -
- - -
- - -
- -); - -export default App; -``` - -```css -.site-button-ghost-wrapper { - padding: 8px 8px 0 8px; - background: rgb(190, 200, 200); -} -``` diff --git a/components/button/demo/disabled.tsx b/components/button/demo/disabled.tsx new file mode 100644 index 0000000000..5469f64a27 --- /dev/null +++ b/components/button/demo/disabled.tsx @@ -0,0 +1,65 @@ +import React from 'react'; +import { Button, Space } from 'antd'; + +const App: React.FC = () => ( + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +); + +export default App; diff --git a/components/button/demo/ghost.md b/components/button/demo/ghost.md index d8a8ce80e5..4c0ebaaca8 100644 --- a/components/button/demo/ghost.md +++ b/components/button/demo/ghost.md @@ -1,10 +1,3 @@ ---- -order: 8 -title: - zh-CN: 幽灵按钮 - en-US: Ghost Button ---- - ## zh-CN 幽灵按钮将按钮的内容反色,背景变为透明,常用在有色背景上。 @@ -12,32 +5,3 @@ title: ## en-US `ghost` property will make button's background transparent, it is commonly used in colored background. - -```tsx -import { Button } from 'antd'; -import React from 'react'; - -const App: React.FC = () => ( -
- - - - -
-); - -export default App; -``` - -```css -.site-button-ghost-wrapper { - padding: 26px 16px 16px; - background: rgb(190, 200, 200); -} -``` diff --git a/components/button/demo/ghost.tsx b/components/button/demo/ghost.tsx new file mode 100644 index 0000000000..e6872a30f2 --- /dev/null +++ b/components/button/demo/ghost.tsx @@ -0,0 +1,19 @@ +import React from 'react'; +import { Button, Space } from 'antd'; + +const App: React.FC = () => ( + + + + + + +); + +export default App; diff --git a/components/button/demo/icon.md b/components/button/demo/icon.md index 0af4189acb..a41ab8ac51 100644 --- a/components/button/demo/icon.md +++ b/components/button/demo/icon.md @@ -1,10 +1,3 @@ ---- -order: 1 -title: - zh-CN: 图标按钮 - en-US: Icon ---- - ## zh-CN 当需要在 `Button` 内嵌入 `Icon` 时,可以设置 `icon` 属性,或者直接在 `Button` 内使用 `Icon` 组件。 @@ -16,72 +9,3 @@ title: `Button` components can contain an `Icon`. This is done by setting the `icon` property or placing an `Icon` component within the `Button`. If you want specific control over the positioning and placement of the `Icon`, then that should be done by placing the `Icon` component within the `Button` rather than using the `icon` property. - -```tsx -import { SearchOutlined } from '@ant-design/icons'; -import { Button, Tooltip } from 'antd'; -import React from 'react'; - -const App: React.FC = () => ( - <> - - - - - -
- - - - - - - - -
- - - - - + + + +
+ + + + + + - - - + + + - - - - + + + + - - Actions - -); - -export default App; -``` diff --git a/components/button/demo/multiple.tsx b/components/button/demo/multiple.tsx new file mode 100644 index 0000000000..f1e0cad130 --- /dev/null +++ b/components/button/demo/multiple.tsx @@ -0,0 +1,32 @@ +import React from 'react'; +import type { MenuProps } from 'antd'; +import { Button, Dropdown, Space } from 'antd'; + +const onMenuClick: MenuProps['onClick'] = (e) => { + console.log('click', e); +}; + +const items = [ + { + key: '1', + label: '1st item', + }, + { + key: '2', + label: '2nd item', + }, + { + key: '3', + label: '3rd item', + }, +]; + +const App: React.FC = () => ( + + + + Actions + +); + +export default App; diff --git a/components/button/demo/size.md b/components/button/demo/size.md index 9edd3015a5..118f1f64b1 100644 --- a/components/button/demo/size.md +++ b/components/button/demo/size.md @@ -1,10 +1,3 @@ ---- -order: 2 -title: - zh-CN: 按钮尺寸 - en-US: Size ---- - ## zh-CN 按钮有大、中、小三种尺寸。 @@ -16,49 +9,3 @@ title: Ant Design supports a default button size as well as a large and small size. If a large or small button is desired, set the `size` property to either `large` or `small` respectively. Omit the `size` property for a button with the default size. - -```tsx -import { DownloadOutlined } from '@ant-design/icons'; -import { Button, Radio } from 'antd'; -import type { SizeType } from 'antd/es/config-provider/SizeContext'; -import React, { useState } from 'react'; - -const App: React.FC = () => { - const [size, setSize] = useState('large'); - - return ( - <> - setSize(e.target.value)}> - Large - Default - Small - -
-
- - - -
- -
- - - - ); -}; - -export default App; -``` diff --git a/components/button/demo/size.tsx b/components/button/demo/size.tsx new file mode 100644 index 0000000000..db4fed605e --- /dev/null +++ b/components/button/demo/size.tsx @@ -0,0 +1,48 @@ +import React, { useState } from 'react'; +import { DownloadOutlined } from '@ant-design/icons'; +import { Button, Radio, Space, Divider } from 'antd'; +import type { SizeType } from 'antd/es/config-provider/SizeContext'; + +const App: React.FC = () => { + const [size, setSize] = useState('large'); // default is 'middle' + + return ( + <> + setSize(e.target.value)}> + Large + Default + Small + + + Preview + + + + + + + + + + + + + + + ); +}; + +export default App; diff --git a/components/button/index.en-US.md b/components/button/index.en-US.md index 391961a25a..8613141920 100644 --- a/components/button/index.en-US.md +++ b/components/button/index.en-US.md @@ -1,8 +1,12 @@ --- category: Components -type: General title: Button cover: https://gw.alipayobjects.com/zos/alicdn/fNUKzY1sk/Button.svg +demo: + cols: 2 +group: + title: General + order: 1 --- To trigger an operation. @@ -26,6 +30,22 @@ And 4 other properties additionally. - `disabled`: when actions are not available. - `loading`: add loading spinner in button, avoiding multiple submits too. +## Examples + + +Type +Icon +Debug Icon +Size +Disabled +Loading +Multiple Buttons +Ghost Button +Danger Buttons +Block Button +Deprecated Button Group +Loading style bug + ## API Different button styles can be generated by setting Button properties. The recommended order is: `type` -> `shape` -> `size` -> `loading` -> `disabled`. @@ -57,18 +77,11 @@ Following the Ant Design specification, we will add one space between if Button Button with two Chinese characters diff --git a/components/page-header/demo/basic.md b/components/page-header/demo/basic.md deleted file mode 100644 index ffa3392d7d..0000000000 --- a/components/page-header/demo/basic.md +++ /dev/null @@ -1,36 +0,0 @@ ---- -order: 1 -title: - zh-CN: 标准样式 - en-US: Basic Page Header ---- - -## zh-CN - -标准页头,适合使用在需要简单描述的场景。 - -## en-US - -Standard header, suitable for use in scenarios that require a brief description. - -```tsx -import { PageHeader } from 'antd'; -import React from 'react'; - -const App: React.FC = () => ( - null} - title="Title" - subTitle="This is a subtitle" - /> -); - -export default App; -``` - -```css -.site-page-header { - border: 1px solid rgb(235, 237, 240); -} -``` diff --git a/components/page-header/demo/breadcrumb.md b/components/page-header/demo/breadcrumb.md deleted file mode 100644 index 0a5ad0183e..0000000000 --- a/components/page-header/demo/breadcrumb.md +++ /dev/null @@ -1,45 +0,0 @@ ---- -order: 3 -title: - zh-CN: 带面包屑页头 - en-US: Use with breadcrumbs ---- - -## zh-CN - -带面包屑页头,适合层级比较深的页面,让用户可以快速导航。 - -## en-US - -With breadcrumbs, it is suitable for deeper pages, allowing users to navigate quickly. - -```tsx -import { PageHeader } from 'antd'; -import React from 'react'; - -const routes = [ - { - path: 'index', - breadcrumbName: 'First-level Menu', - }, - { - path: 'first', - breadcrumbName: 'Second-level Menu', - }, - { - path: 'second', - breadcrumbName: 'Third-level Menu', - }, -]; - -const App: React.FC = () => ( - -); - -export default App; -``` diff --git a/components/page-header/demo/content.md b/components/page-header/demo/content.md deleted file mode 100644 index 6c65f31578..0000000000 --- a/components/page-header/demo/content.md +++ /dev/null @@ -1,193 +0,0 @@ ---- -order: 4 -title: - zh-CN: 组合示例 - en-US: Complete example ---- - -## zh-CN - -使用了 PageHeader 提供的所有能力。 - -## en-US - -Show all props provided by PageHeader. - -```tsx -import { MoreOutlined } from '@ant-design/icons'; -import { Button, Dropdown, PageHeader, Row, Tag, Typography } from 'antd'; -import React from 'react'; - -const { Paragraph } = Typography; - -const items = [ - { - key: '1', - label: ( - - 1st menu item - - ), - }, - { - key: '2', - label: ( - - 2nd menu item - - ), - }, - { - key: '3', - label: ( - - 3rd menu item - - ), - }, -]; - -const DropdownMenu = () => ( - - , - , - , - , - ]} - avatar={{ src: 'https://avatars1.githubusercontent.com/u/8186664?s=460&v=4' }} - breadcrumb={{ routes }} - > - - } - > - {content} - - -); - -export default App; -``` - -```css -#components-page-header-demo-content .image { - display: flex; - align-items: center; - margin: 0 0 0 60px; -} - -#components-page-header-demo-content .ant-page-header-rtl .image { - margin: 0 60px 0 0; -} - -#components-page-header-demo-content .example-link { - margin-right: 16px; - line-height: 24px; -} -[data-theme='compact'] #components-page-header-demo-content .example-link { - line-height: 20px; -} -#components-page-header-demo-content .example-link-icon { - margin-right: 8px; -} - -[data-theme='compact'] #components-page-header-demo-content .example-link-icon { - width: 20px; - height: 20px; -} - -#components-page-header-demo-content .ant-page-header-rtl .example-link { - float: right; - margin-right: 0; - margin-left: 16px; -} -#components-page-header-demo-content .ant-page-header-rtl .example-link-icon { - margin-right: 0; - margin-left: 8px; -} - -@media (max-width: 768px) { - #components-page-header-demo-content .image { - flex: 100%; - margin: 24px 0 0; - } -} -``` diff --git a/components/page-header/demo/ghost.md b/components/page-header/demo/ghost.md deleted file mode 100644 index c48415866e..0000000000 --- a/components/page-header/demo/ghost.md +++ /dev/null @@ -1,58 +0,0 @@ ---- -order: 2 -title: - zh-CN: 白底模式 - en-US: white background mode ---- - -## zh-CN - -默认 PageHeader 是透明底色的。在某些情况下,PageHeader 需要自己的背景颜色。 - -## en-US - -The default PageHeader is a transparent background. In some cases, PageHeader needs its own background color. - -```tsx -import { Button, Descriptions, PageHeader } from 'antd'; -import React from 'react'; - -const App: React.FC = () => ( -
- window.history.back()} - title="Title" - subTitle="This is a subtitle" - extra={[ - , - , - , - ]} - > - - Lili Qu - - 421421 - - 2017-01-10 - 2017-10-10 - - Gonghu Road, Xihu District, Hangzhou, Zhejiang, China - - - -
-); - -export default App; -``` - -```css -.site-page-header-ghost-wrapper { - padding: 24px; - background-color: #f5f5f5; -} -``` diff --git a/components/page-header/demo/responsive.md b/components/page-header/demo/responsive.md deleted file mode 100644 index c19c506f19..0000000000 --- a/components/page-header/demo/responsive.md +++ /dev/null @@ -1,120 +0,0 @@ ---- -order: 6 -iframe: 228 -title: - zh-CN: 响应式 - en-US: responsive ---- - -## zh-CN - -在不同大小的屏幕下,应该有不同的表现。 - -## en-US - -Under different screen sizes, there should be different performance - -```tsx -import { Button, Descriptions, PageHeader, Statistic, Tabs } from 'antd'; -import React from 'react'; - -const { TabPane } = Tabs; - -const renderContent = (column = 2) => ( - - Lili Qu - - 421421 - - 2017-01-10 - 2017-10-10 - - Gonghu Road, Xihu District, Hangzhou, Zhejiang, China - - -); - -const extraContent = ( -
- - -
-); - -const Content: React.FC<{ children: React.ReactNode; extra: React.ReactNode }> = ({ - children, - extra, -}) => ( -
-
{children}
-
{extra}
-
-); - -const App: React.FC = () => ( - window.history.back()} - title="Title" - subTitle="This is a subtitle" - extra={[ - , - , - , - ]} - footer={ - - - - - } - > - {renderContent()} - -); - -export default App; -``` - - diff --git a/components/page-header/index.en-US.md b/components/page-header/index.en-US.md deleted file mode 100644 index d9024f1384..0000000000 --- a/components/page-header/index.en-US.md +++ /dev/null @@ -1,39 +0,0 @@ ---- -category: Components -type: Navigation -title: PageHeader -cols: 1 -subtitle: -cover: https://gw.alipayobjects.com/zos/alicdn/6bKE0Cq0R/PageHeader.svg ---- - -A header with common actions and design elements built in. - -## When To Use - -PageHeader can be used to highlight the page topic, display important information about the page, and carry the action items related to the current page (including page-level operations, inter-page navigation, etc.) It can also be used as inter-page navigation. - -## API - -| Param | Description | Type | Default value | Version | -| --- | --- | --- | --- | --- | -| avatar | Avatar next to the title bar | [AvatarProps](/components/avatar/) | - | | -| backIcon | Custom back icon, if false the back icon will not be displayed | ReactNode \| boolean | <ArrowLeft /> | | -| breadcrumb | Breadcrumb configuration | [Breadcrumb](/components/breadcrumb/) | - | | -| breadcrumbRender | Customize the content of the breadcrumb area | `(props, originBreadcrumb)=> ReactNode` | - | 4.11.0 | -| extra | Operating area, at the end of the line of the title line | ReactNode | - | | -| footer | PageHeader's footer, generally used to render TabBar | ReactNode | - | | -| ghost | PageHeader type, will change background color | boolean | true | | -| subTitle | Custom subtitle text | ReactNode | - | | -| tags | Tag list next to title | [Tag](/components/tag/)\[] \| [Tag](/components/tag/) | - | | -| title | Custom title text | ReactNode | - | | -| onBack | Back icon click event | () => void | - | | - - diff --git a/components/page-header/index.tsx b/components/page-header/index.tsx deleted file mode 100644 index cc38b6deaa..0000000000 --- a/components/page-header/index.tsx +++ /dev/null @@ -1,199 +0,0 @@ -import ArrowLeftOutlined from '@ant-design/icons/ArrowLeftOutlined'; -import ArrowRightOutlined from '@ant-design/icons/ArrowRightOutlined'; -import classNames from 'classnames'; -import ResizeObserver from 'rc-resize-observer'; -import useState from 'rc-util/lib/hooks/useState'; -import * as React from 'react'; -import type { AvatarProps } from '../avatar'; -import Avatar from '../avatar'; -import type { BreadcrumbProps } from '../breadcrumb'; -import Breadcrumb from '../breadcrumb'; -import type { ConfigConsumerProps, DirectionType } from '../config-provider'; -import { ConfigConsumer } from '../config-provider'; -import LocaleReceiver from '../locale-provider/LocaleReceiver'; -import Space from '../space'; -import type { TagType } from '../tag'; -import TransButton from '../_util/transButton'; - -export interface PageHeaderProps { - backIcon?: React.ReactNode; - prefixCls?: string; - title?: React.ReactNode; - subTitle?: React.ReactNode; - style?: React.CSSProperties; - breadcrumb?: BreadcrumbProps | React.ReactElement; - breadcrumbRender?: (props: PageHeaderProps, defaultDom: React.ReactNode) => React.ReactNode; - tags?: React.ReactElement | React.ReactElement[]; - footer?: React.ReactNode; - extra?: React.ReactNode; - avatar?: AvatarProps; - onBack?: (e?: React.MouseEvent) => void; - className?: string; - ghost?: boolean; - children?: React.ReactNode; -} - -const renderBack = ( - prefixCls: string, - backIcon?: React.ReactNode, - onBack?: (e?: React.MouseEvent) => void, -) => { - if (!backIcon || !onBack) { - return null; - } - return ( - - {contextLocale => ( -
- ) => { - onBack?.(e); - }} - className={`${prefixCls}-back-button`} - aria-label={contextLocale.back} - > - {backIcon} - -
- )} -
- ); -}; - -const renderBreadcrumb = (breadcrumb: BreadcrumbProps) => ; - -const getBackIcon = (props: PageHeaderProps, direction: DirectionType = 'ltr') => { - if (props.backIcon !== undefined) { - return props.backIcon; - } - return direction === 'rtl' ? : ; -}; - -const renderTitle = ( - prefixCls: string, - props: PageHeaderProps, - direction: DirectionType = 'ltr', -) => { - const { title, avatar, subTitle, tags, extra, onBack } = props; - const headingPrefixCls = `${prefixCls}-heading`; - const hasHeading = title || subTitle || tags || extra; - // If there is nothing, return a null - if (!hasHeading) { - return null; - } - const backIcon = getBackIcon(props, direction); - const backIconDom = renderBack(prefixCls, backIcon, onBack); - const hasTitle = backIconDom || avatar || hasHeading; - return ( -
- {hasTitle && ( -
- {backIconDom} - {avatar && } - {title && ( - - {title} - - )} - {subTitle && ( - - {subTitle} - - )} - {tags && {tags}} -
- )} - {extra && ( - - {extra} - - )} -
- ); -}; - -const renderFooter = (prefixCls: string, footer: React.ReactNode) => { - if (footer) { - return
{footer}
; - } - return null; -}; - -const renderChildren = (prefixCls: string, children: React.ReactNode) => ( -
{children}
-); - -const PageHeader: React.FC = props => { - const [compact, updateCompact] = useState(false); - const onResize = ({ width }: { width: number }) => { - updateCompact(width < 768, true); - }; - return ( - - {({ getPrefixCls, pageHeader, direction }: ConfigConsumerProps) => { - const { - prefixCls: customizePrefixCls, - style, - footer, - children, - breadcrumb, - breadcrumbRender, - className: customizeClassName, - } = props; - let ghost: undefined | boolean = true; - - // Use `ghost` from `props` or from `ConfigProvider` instead. - if ('ghost' in props) { - ghost = props.ghost; - } else if (pageHeader && 'ghost' in pageHeader) { - ghost = pageHeader.ghost; - } - - const prefixCls = getPrefixCls('page-header', customizePrefixCls); - - const getDefaultBreadcrumbDom = () => { - if ((breadcrumb as BreadcrumbProps)?.routes) { - return renderBreadcrumb(breadcrumb as BreadcrumbProps); - } - return null; - }; - - const defaultBreadcrumbDom = getDefaultBreadcrumbDom(); - - const isBreadcrumbComponent = breadcrumb && 'props' in breadcrumb; - // support breadcrumbRender function - const breadcrumbRenderDomFromProps = - breadcrumbRender?.(props, defaultBreadcrumbDom) ?? defaultBreadcrumbDom; - - const breadcrumbDom = isBreadcrumbComponent ? breadcrumb : breadcrumbRenderDomFromProps; - - const className = classNames(prefixCls, customizeClassName, { - 'has-breadcrumb': !!breadcrumbDom, - 'has-footer': !!footer, - [`${prefixCls}-ghost`]: ghost, - [`${prefixCls}-rtl`]: direction === 'rtl', - [`${prefixCls}-compact`]: compact, - }); - - return ( - -
- {breadcrumbDom} - {renderTitle(prefixCls, props, direction)} - {children && renderChildren(prefixCls, children)} - {renderFooter(prefixCls, footer)} -
-
- ); - }} -
- ); -}; - -export default PageHeader; diff --git a/components/page-header/index.zh-CN.md b/components/page-header/index.zh-CN.md deleted file mode 100644 index f4a61bab6c..0000000000 --- a/components/page-header/index.zh-CN.md +++ /dev/null @@ -1,39 +0,0 @@ ---- -category: Components -type: 导航 -title: PageHeader -cols: 1 -subtitle: 页头 -cover: https://gw.alipayobjects.com/zos/alicdn/6bKE0Cq0R/PageHeader.svg ---- - -页头位于页容器中,页容器顶部,起到了内容概览和引导页级操作的作用。包括由面包屑、标题、页面内容简介、页面级操作等、页面级导航组成。 - -## 何时使用 - -当需要使用户快速理解当前页是什么以及方便用户使用页面功能时使用,通常也可被用作页面间导航。 - -## API - -| 参数 | 说明 | 类型 | 默认值 | 版本 | -| --- | --- | --- | --- | --- | -| avatar | 标题栏旁的头像 | [AvatarProps](/components/avatar/) | - | | -| backIcon | 自定义 back icon ,如果为 false 不渲染 back icon | ReactNode \| boolean | <ArrowLeft /> | | -| breadcrumb | 面包屑的配置 | [Breadcrumb](/components/breadcrumb/) | - | | -| breadcrumbRender | 自定义面包屑区域的内容 | `(props, originBreadcrumb) => ReactNode` | - | 4.11.0 | -| extra | 操作区,位于 title 行的行尾 | ReactNode | - | | -| footer | PageHeader 的页脚,一般用于渲染 TabBar | ReactNode | - | | -| ghost | pageHeader 的类型,将会改变背景颜色 | boolean | true | | -| subTitle | 自定义的二级标题文字 | ReactNode | - | | -| tags | title 旁的 tag 列表 | [Tag](/components/tag/)\[] \| [Tag](/components/tag/) | - | | -| title | 自定义标题文字 | ReactNode | - | | -| onBack | 返回按钮的点击事件 | () => void | - | | - - diff --git a/components/page-header/style/index.less b/components/page-header/style/index.less deleted file mode 100644 index 46d413f126..0000000000 --- a/components/page-header/style/index.less +++ /dev/null @@ -1,122 +0,0 @@ -@import '../../style/themes/index'; -@import '../../style/mixins/index'; - -@pageheader-prefix-cls: ~'@{ant-prefix}-page-header'; - -.@{pageheader-prefix-cls} { - .reset-component(); - position: relative; - padding: @page-header-padding-vertical @page-header-padding; - background-color: @component-background; - - &-ghost { - background-color: @page-header-ghost-bg; - } - - &.has-breadcrumb { - padding-top: @page-header-padding-breadcrumb; - } - - &.has-footer { - padding-bottom: 0; - } - - &-back { - margin-right: @margin-md; - font-size: 16px; - line-height: 1; - - &-button { - .operation-unit(); - color: @page-header-back-color; - } - } - - .@{ant-prefix}-divider-vertical { - height: 14px; - margin: 0 @margin-sm; - vertical-align: middle; - } - - .@{ant-prefix}-breadcrumb + &-heading { - margin-top: @margin-xs; - } - - .text-overflow-ellipsis() { - overflow: hidden; - white-space: nowrap; - text-overflow: ellipsis; - } - - &-heading { - display: flex; - justify-content: space-between; - - &-left { - display: flex; - align-items: center; - margin: (@margin-xs / 2) 0; - overflow: hidden; - } - - &-title { - margin-right: @margin-sm; - margin-bottom: 0; - color: @heading-color; - font-weight: 600; - font-size: @page-header-heading-title; - line-height: @height-base; - .text-overflow-ellipsis(); - } - - .@{ant-prefix}-avatar { - margin-right: @margin-sm; - } - - &-sub-title { - margin-right: @margin-sm; - color: @text-color-secondary; - font-size: @page-header-heading-sub-title; - line-height: @line-height-base; - .text-overflow-ellipsis(); - } - - &-extra { - margin: (@margin-xs / 2) 0; - white-space: nowrap; - - > * { - white-space: unset; - } - } - } - - &-content { - padding-top: @page-header-content-padding-vertical; - } - - &-footer { - margin-top: @margin-md; - .@{ant-prefix}-tabs { - > .@{ant-prefix}-tabs-nav { - margin: 0; - - &::before { - border: none; - } - } - - .@{ant-prefix}-tabs-tab { - padding-top: @padding-xs; - padding-bottom: @padding-xs; - font-size: @page-header-tabs-tab-font-size; - } - } - } - - &-compact &-heading { - flex-wrap: wrap; - } -} - -@import './rtl'; diff --git a/components/page-header/style/index.tsx b/components/page-header/style/index.tsx deleted file mode 100644 index 5146a5fd56..0000000000 --- a/components/page-header/style/index.tsx +++ /dev/null @@ -1,6 +0,0 @@ -import './index.less'; - -// style dependencies -import '../../avatar/style'; -import '../../breadcrumb/style'; -import '../../space/style'; diff --git a/components/page-header/style/rtl.less b/components/page-header/style/rtl.less deleted file mode 100644 index 33e5c0d86b..0000000000 --- a/components/page-header/style/rtl.less +++ /dev/null @@ -1,77 +0,0 @@ -@import '../../style/themes/index'; -@import '../../style/mixins/index'; - -@pageheader-prefix-cls: ~'@{ant-prefix}-page-header'; - -.@{pageheader-prefix-cls} { - &-rtl { - direction: rtl; - } - - &-back { - .@{pageheader-prefix-cls}-rtl & { - float: right; - margin-right: 0; - margin-left: 16px; - } - } - - &-heading { - &-title { - .@{pageheader-prefix-cls}-rtl & { - margin-right: 0; - margin-left: @margin-sm; - } - } - - .@{ant-prefix}-avatar { - .@{pageheader-prefix-cls}-rtl & { - margin-right: 0; - margin-left: @margin-sm; - } - } - - &-sub-title { - .@{pageheader-prefix-cls}-rtl & { - float: right; - margin-right: 0; - margin-left: 12px; - } - } - - &-tags { - .@{pageheader-prefix-cls}-rtl & { - float: right; - } - } - - &-extra { - .@{pageheader-prefix-cls}-rtl & { - float: left; - } - - > * { - .@{pageheader-prefix-cls}-rtl & { - margin-right: @margin-sm; - margin-left: 0; - } - } - - > *:first-child { - .@{pageheader-prefix-cls}-rtl & { - margin-right: 0; - } - } - } - } - - &-footer { - .@{ant-prefix}-tabs-bar { - .@{ant-prefix}-tabs-nav { - .@{pageheader-prefix-cls}-rtl & { - float: right; - } - } - } - } -} diff --git a/components/pagination/Pagination.tsx b/components/pagination/Pagination.tsx index b76211fca1..36cecf5471 100644 --- a/components/pagination/Pagination.tsx +++ b/components/pagination/Pagination.tsx @@ -4,13 +4,14 @@ import LeftOutlined from '@ant-design/icons/LeftOutlined'; import RightOutlined from '@ant-design/icons/RightOutlined'; import classNames from 'classnames'; import type { PaginationProps as RcPaginationProps } from 'rc-pagination'; -import RcPagination, { PaginationLocale } from 'rc-pagination'; +import RcPagination, { type PaginationLocale } from 'rc-pagination'; import enUS from 'rc-pagination/lib/locale/en_US'; import * as React from 'react'; import { ConfigContext } from '../config-provider'; import useBreakpoint from '../grid/hooks/useBreakpoint'; import LocaleReceiver from '../locale-provider/LocaleReceiver'; import { MiddleSelect, MiniSelect } from './Select'; +import useStyle from './style'; export interface PaginationProps extends RcPaginationProps { showQuickJumper?: boolean | { goButton?: React.ReactNode }; @@ -26,7 +27,7 @@ export interface PaginationConfig extends PaginationProps { position?: PaginationPosition; } -export { PaginationLocale }; +export type { PaginationLocale }; const Pagination: React.FC = ({ prefixCls: customizePrefixCls, @@ -44,6 +45,9 @@ const Pagination: React.FC = ({ const { getPrefixCls, direction, pagination = {} } = React.useContext(ConfigContext); const prefixCls = getPrefixCls('pagination', customizePrefixCls); + // Style + const [wrapSSR, hashId] = useStyle(prefixCls); + const mergedShowSizeChanger = showSizeChanger ?? pagination.showSizeChanger; const getIconsProps = () => { @@ -101,9 +105,10 @@ const Pagination: React.FC = ({ [`${prefixCls}-rtl`]: direction === 'rtl', }, className, + hashId, ); - return ( + return wrapSSR( = ({ selectComponentClass={selectComponentClass || (isSmall ? MiniSelect : MiddleSelect)} locale={locale} showSizeChanger={mergedShowSizeChanger} - /> + />, ); }} diff --git a/components/pagination/__tests__/__snapshots__/demo-extend.test.ts.snap b/components/pagination/__tests__/__snapshots__/demo-extend.test.ts.snap index 0d441e2e82..f4fe028d69 100644 --- a/components/pagination/__tests__/__snapshots__/demo-extend.test.ts.snap +++ b/components/pagination/__tests__/__snapshots__/demo-extend.test.ts.snap @@ -1,6 +1,6 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP -exports[`renders ./components/pagination/demo/all.md extend context correctly 1`] = ` +exports[`renders ./components/pagination/demo/all.tsx extend context correctly 1`] = `
    @@ -373,7 +373,7 @@ exports[`renders ./components/pagination/demo/all.md extend context correctly 1`
`; -exports[`renders ./components/pagination/demo/basic.md extend context correctly 1`] = ` +exports[`renders ./components/pagination/demo/basic.tsx extend context correctly 1`] = `
    @@ -499,7 +499,7 @@ exports[`renders ./components/pagination/demo/basic.md extend context correctly
`; -exports[`renders ./components/pagination/demo/changer.md extend context correctly 1`] = ` +exports[`renders ./components/pagination/demo/changer.tsx extend context correctly 1`] = ` Array [
    @@ -1340,7 +1340,7 @@ exports[`renders ./components/pagination/demo/controlled.md extend context corre
`; -exports[`renders ./components/pagination/demo/itemRender.md extend context correctly 1`] = ` +exports[`renders ./components/pagination/demo/itemRender.tsx extend context correctly 1`] = `
    @@ -1654,7 +1654,7 @@ exports[`renders ./components/pagination/demo/itemRender.md extend context corre
`; -exports[`renders ./components/pagination/demo/jump.md extend context correctly 1`] = ` +exports[`renders ./components/pagination/demo/jump.tsx extend context correctly 1`] = ` Array [
    @@ -3693,7 +3693,7 @@ exports[`renders ./components/pagination/demo/more.md extend context correctly 1
`; -exports[`renders ./components/pagination/demo/simple.md extend context correctly 1`] = ` +exports[`renders ./components/pagination/demo/simple.tsx extend context correctly 1`] = ` Array [
    @@ -257,7 +257,7 @@ exports[`renders ./components/pagination/demo/all.md correctly 1`] = `
`; -exports[`renders ./components/pagination/demo/basic.md correctly 1`] = ` +exports[`renders ./components/pagination/demo/basic.tsx correctly 1`] = `
    @@ -383,7 +383,7 @@ exports[`renders ./components/pagination/demo/basic.md correctly 1`] = `
`; -exports[`renders ./components/pagination/demo/changer.md correctly 1`] = ` +exports[`renders ./components/pagination/demo/changer.tsx correctly 1`] = ` Array [
    @@ -992,7 +992,7 @@ exports[`renders ./components/pagination/demo/controlled.md correctly 1`] = `
`; -exports[`renders ./components/pagination/demo/itemRender.md correctly 1`] = ` +exports[`renders ./components/pagination/demo/itemRender.tsx correctly 1`] = `
    @@ -1190,7 +1190,7 @@ exports[`renders ./components/pagination/demo/itemRender.md correctly 1`] = `
`; -exports[`renders ./components/pagination/demo/jump.md correctly 1`] = ` +exports[`renders ./components/pagination/demo/jump.tsx correctly 1`] = ` Array [
    @@ -2649,7 +2649,7 @@ exports[`renders ./components/pagination/demo/more.md correctly 1`] = `
`; -exports[`renders ./components/pagination/demo/simple.md correctly 1`] = ` +exports[`renders ./components/pagination/demo/simple.tsx correctly 1`] = ` Array [
    { mountTest(Pagination); diff --git a/components/pagination/__tests__/simple.test.tsx b/components/pagination/__tests__/simple.test.tsx index 7f8ff4f3d4..765a911d5a 100644 --- a/components/pagination/__tests__/simple.test.tsx +++ b/components/pagination/__tests__/simple.test.tsx @@ -5,8 +5,14 @@ import { render } from '../../../tests/utils'; describe('Pagination simple mode', () => { it('should support showTotal in simple mode', () => { const { container } = render( - `${range[0]}-${range[1]} of ${total} items`} />, + `${range[0]}-${range[1]} of ${total} items`} + />, + ); + expect(container?.querySelector('.ant-pagination-total-text')).toHaveTextContent( + '1-10 of 100 items', ); - expect(container?.querySelector('.ant-pagination-total-text')).toHaveTextContent('1-10 of 100 items'); }); }); diff --git a/components/pagination/demo/all.md b/components/pagination/demo/all.md index 737d030c20..b1bced4fce 100644 --- a/components/pagination/demo/all.md +++ b/components/pagination/demo/all.md @@ -1,10 +1,3 @@ ---- -order: 10 -title: - zh-CN: 全部展示 - en-US: Show All ---- - ## zh-CN 展示所有配置选项。 @@ -12,19 +5,3 @@ title: ## en-US Show all configured prop. - -```tsx -import { Pagination } from 'antd'; -import React from 'react'; - -const App: React.FC = () => ( - `Total ${total} items`} - /> -); - -export default App; -``` diff --git a/components/pagination/demo/all.tsx b/components/pagination/demo/all.tsx new file mode 100644 index 0000000000..3097943b3a --- /dev/null +++ b/components/pagination/demo/all.tsx @@ -0,0 +1,13 @@ +import React from 'react'; +import { Pagination } from 'antd'; + +const App: React.FC = () => ( + `Total ${total} items`} + /> +); + +export default App; diff --git a/components/pagination/demo/basic.md b/components/pagination/demo/basic.md index ae8c842885..bde4fefacd 100644 --- a/components/pagination/demo/basic.md +++ b/components/pagination/demo/basic.md @@ -1,10 +1,3 @@ ---- -order: 0 -title: - zh-CN: 基本 - en-US: Basic ---- - ## zh-CN 基础分页。 @@ -12,12 +5,3 @@ title: ## en-US Basic pagination. - -```tsx -import { Pagination } from 'antd'; -import React from 'react'; - -const App: React.FC = () => ; - -export default App; -``` diff --git a/components/pagination/demo/basic.tsx b/components/pagination/demo/basic.tsx new file mode 100644 index 0000000000..5de41a7dab --- /dev/null +++ b/components/pagination/demo/basic.tsx @@ -0,0 +1,6 @@ +import React from 'react'; +import { Pagination } from 'antd'; + +const App: React.FC = () => ; + +export default App; diff --git a/components/pagination/demo/changer.md b/components/pagination/demo/changer.md index 4261a3ae35..982f2b3c87 100644 --- a/components/pagination/demo/changer.md +++ b/components/pagination/demo/changer.md @@ -1,10 +1,3 @@ ---- -order: 2 -title: - zh-CN: 改变 - en-US: Changer ---- - ## zh-CN 改变每页显示条目数。 @@ -12,34 +5,3 @@ title: ## en-US Change `pageSize`. - -```tsx -import type { PaginationProps } from 'antd'; -import { Pagination } from 'antd'; -import React from 'react'; - -const onShowSizeChange: PaginationProps['onShowSizeChange'] = (current, pageSize) => { - console.log(current, pageSize); -}; - -const App: React.FC = () => ( - <> - -
    - - -); - -export default App; -``` diff --git a/components/pagination/demo/changer.tsx b/components/pagination/demo/changer.tsx new file mode 100644 index 0000000000..a171292c61 --- /dev/null +++ b/components/pagination/demo/changer.tsx @@ -0,0 +1,28 @@ +import React from 'react'; +import type { PaginationProps } from 'antd'; +import { Pagination } from 'antd'; + +const onShowSizeChange: PaginationProps['onShowSizeChange'] = (current, pageSize) => { + console.log(current, pageSize); +}; + +const App: React.FC = () => ( + <> + +
    + + +); + +export default App; diff --git a/components/pagination/demo/controlled.md b/components/pagination/demo/controlled.md index 40d85203bc..f8ddffc2b1 100644 --- a/components/pagination/demo/controlled.md +++ b/components/pagination/demo/controlled.md @@ -1,10 +1,3 @@ ---- -order: 8 -title: - zh-CN: 受控 - en-US: Controlled ---- - ## zh-CN 受控制的页码。 @@ -12,22 +5,3 @@ title: ## en-US Controlled page number. - -```tsx -import type { PaginationProps } from 'antd'; -import { Pagination } from 'antd'; -import React, { useState } from 'react'; - -const App: React.FC = () => { - const [current, setCurrent] = useState(3); - - const onChange: PaginationProps['onChange'] = page => { - console.log(page); - setCurrent(page); - }; - - return ; -}; - -export default App; -``` diff --git a/components/pagination/demo/controlled.tsx b/components/pagination/demo/controlled.tsx new file mode 100644 index 0000000000..759c765027 --- /dev/null +++ b/components/pagination/demo/controlled.tsx @@ -0,0 +1,16 @@ +import React, { useState } from 'react'; +import type { PaginationProps } from 'antd'; +import { Pagination } from 'antd'; + +const App: React.FC = () => { + const [current, setCurrent] = useState(3); + + const onChange: PaginationProps['onChange'] = page => { + console.log(page); + setCurrent(page); + }; + + return ; +}; + +export default App; diff --git a/components/pagination/demo/itemRender.md b/components/pagination/demo/itemRender.md index dc954f0a65..44e2670293 100644 --- a/components/pagination/demo/itemRender.md +++ b/components/pagination/demo/itemRender.md @@ -1,10 +1,3 @@ ---- -order: 11 -title: - zh-CN: 上一步和下一步 - en-US: Prev and next ---- - ## zh-CN 修改上一步和下一步为文字链接。 @@ -12,23 +5,3 @@ title: ## en-US Use text link for prev and next button. - -```tsx -import type { PaginationProps } from 'antd'; -import { Pagination } from 'antd'; -import React from 'react'; - -const itemRender: PaginationProps['itemRender'] = (_, type, originalElement) => { - if (type === 'prev') { - return Previous; - } - if (type === 'next') { - return Next; - } - return originalElement; -}; - -const App: React.FC = () => ; - -export default App; -``` diff --git a/components/pagination/demo/itemRender.tsx b/components/pagination/demo/itemRender.tsx new file mode 100644 index 0000000000..700004e9cb --- /dev/null +++ b/components/pagination/demo/itemRender.tsx @@ -0,0 +1,17 @@ +import React from 'react'; +import type { PaginationProps } from 'antd'; +import { Pagination } from 'antd'; + +const itemRender: PaginationProps['itemRender'] = (_, type, originalElement) => { + if (type === 'prev') { + return Previous; + } + if (type === 'next') { + return Next; + } + return originalElement; +}; + +const App: React.FC = () => ; + +export default App; diff --git a/components/pagination/demo/jump.md b/components/pagination/demo/jump.md index 5732ad2794..1e2ed2c5aa 100644 --- a/components/pagination/demo/jump.md +++ b/components/pagination/demo/jump.md @@ -1,10 +1,3 @@ ---- -order: 3 -title: - zh-CN: 跳转 - en-US: Jumper ---- - ## zh-CN 快速跳转到某一页。 @@ -12,23 +5,3 @@ title: ## en-US Jump to a page directly. - -```tsx -import type { PaginationProps } from 'antd'; -import { Pagination } from 'antd'; -import React from 'react'; - -const onChange: PaginationProps['onChange'] = pageNumber => { - console.log('Page: ', pageNumber); -}; - -const App: React.FC = () => ( - <> - -
    - - -); - -export default App; -``` diff --git a/components/pagination/demo/jump.tsx b/components/pagination/demo/jump.tsx new file mode 100644 index 0000000000..70d1275271 --- /dev/null +++ b/components/pagination/demo/jump.tsx @@ -0,0 +1,17 @@ +import React from 'react'; +import type { PaginationProps } from 'antd'; +import { Pagination } from 'antd'; + +const onChange: PaginationProps['onChange'] = pageNumber => { + console.log('Page: ', pageNumber); +}; + +const App: React.FC = () => ( + <> + +
    + + +); + +export default App; diff --git a/components/pagination/demo/mini.md b/components/pagination/demo/mini.md index bfb1ce1ea6..7a2ade4ba4 100644 --- a/components/pagination/demo/mini.md +++ b/components/pagination/demo/mini.md @@ -1,10 +1,3 @@ ---- -order: 4 -title: - zh-CN: 迷你 - en-US: Mini size ---- - ## zh-CN 迷你版本。 @@ -13,32 +6,6 @@ title: Mini size pagination. -```tsx -import type { PaginationProps } from 'antd'; -import { Pagination } from 'antd'; -import React from 'react'; - -const showTotal: PaginationProps['showTotal'] = total => `Total ${total} items`; - -const App: React.FC = () => ( - <> - - - - - -); - -export default App; -``` - diff --git a/components/progress/demo/circle.tsx b/components/progress/demo/circle.tsx new file mode 100644 index 0000000000..3ec3c49b62 --- /dev/null +++ b/components/progress/demo/circle.tsx @@ -0,0 +1,12 @@ +import React from 'react'; +import { Progress } from 'antd'; + +const App: React.FC = () => ( + <> + + + + +); + +export default App; diff --git a/components/progress/demo/dashboard.md b/components/progress/demo/dashboard.md index 79a9d6e1e1..0c2e0c5d7f 100644 --- a/components/progress/demo/dashboard.md +++ b/components/progress/demo/dashboard.md @@ -1,10 +1,3 @@ ---- -order: 8 -title: - zh-CN: 仪表盘 - en-US: Dashboard ---- - ## zh-CN 通过设置 `type=dashboard`,可以很方便地实现仪表盘样式的进度条。若想要修改缺口的角度,可以设置 `gapDegree` 为你想要的值。 @@ -12,17 +5,3 @@ title: ## en-US By setting `type=dashboard`, you can get a dashboard style of progress easily. Modify `gapDegree` to set the degree of gap. - -```tsx -import { Progress } from 'antd'; -import React from 'react'; - -const App: React.FC = () => ( - <> - - - -); - -export default App; -``` diff --git a/components/progress/demo/dashboard.tsx b/components/progress/demo/dashboard.tsx new file mode 100644 index 0000000000..3929df9342 --- /dev/null +++ b/components/progress/demo/dashboard.tsx @@ -0,0 +1,11 @@ +import React from 'react'; +import { Progress } from 'antd'; + +const App: React.FC = () => ( + <> + + + +); + +export default App; diff --git a/components/progress/demo/dynamic.md b/components/progress/demo/dynamic.md index 349508bf55..4d413cfbf8 100644 --- a/components/progress/demo/dynamic.md +++ b/components/progress/demo/dynamic.md @@ -1,10 +1,3 @@ ---- -order: 4 -title: - zh-CN: 动态展示 - en-US: Dynamic ---- - ## zh-CN 会动的进度条才是好进度条。 @@ -12,41 +5,3 @@ title: ## en-US A dynamic progress bar is better. - -```tsx -import { MinusOutlined, PlusOutlined } from '@ant-design/icons'; -import { Button, Progress } from 'antd'; -import React, { useState } from 'react'; - -const App: React.FC = () => { - const [percent, setPercent] = useState(0); - - const increase = () => { - let newPercent = percent + 10; - if (newPercent > 100) { - newPercent = 100; - } - setPercent(newPercent); - }; - - const decline = () => { - let newPercent = percent - 10; - if (newPercent < 0) { - newPercent = 0; - } - setPercent(newPercent); - }; - - return ( - <> - - - - - ); -}; - -export default App; -``` diff --git a/components/radio/demo/disabled.tsx b/components/radio/demo/disabled.tsx new file mode 100644 index 0000000000..5d53f1f68a --- /dev/null +++ b/components/radio/demo/disabled.tsx @@ -0,0 +1,27 @@ +import React, { useState } from 'react'; +import { Button, Radio } from 'antd'; + +const App: React.FC = () => { + const [disabled, setDisabled] = useState(true); + + const toggleDisabled = () => { + setDisabled(!disabled); + }; + + return ( + <> + + Disabled + + + Disabled + +
    + + + ); +}; + +export default App; diff --git a/components/radio/demo/radiobutton-solid.md b/components/radio/demo/radiobutton-solid.md index 4ab0b9bd7f..ffcb50d73e 100644 --- a/components/radio/demo/radiobutton-solid.md +++ b/components/radio/demo/radiobutton-solid.md @@ -1,10 +1,3 @@ ---- -order: 6 -title: - zh-CN: 填底的按钮样式 - en-US: Solid radio button ---- - ## zh-CN 实色填底的单选按钮样式。 @@ -12,29 +5,3 @@ title: ## en-US Solid radio button style. - -```tsx -import { Radio } from 'antd'; -import React from 'react'; - -const App: React.FC = () => ( - <> - - Hangzhou - Shanghai - Beijing - Chengdu - - - Hangzhou - - Shanghai - - Beijing - Chengdu - - -); - -export default App; -``` diff --git a/components/radio/demo/radiobutton-solid.tsx b/components/radio/demo/radiobutton-solid.tsx new file mode 100644 index 0000000000..cae1ab222c --- /dev/null +++ b/components/radio/demo/radiobutton-solid.tsx @@ -0,0 +1,23 @@ +import React from 'react'; +import { Radio } from 'antd'; + +const App: React.FC = () => ( + <> + + Hangzhou + Shanghai + Beijing + Chengdu + + + Hangzhou + + Shanghai + + Beijing + Chengdu + + +); + +export default App; diff --git a/components/radio/demo/radiobutton.md b/components/radio/demo/radiobutton.md index e622e6a7e6..b3a30a48c8 100644 --- a/components/radio/demo/radiobutton.md +++ b/components/radio/demo/radiobutton.md @@ -1,10 +1,3 @@ ---- -order: 3 -title: - zh-CN: 按钮样式 - en-US: radio style ---- - ## zh-CN 按钮样式的单选组合。 @@ -12,40 +5,3 @@ title: ## en-US The combination of radio button style. - -```tsx -import type { RadioChangeEvent } from 'antd'; -import { Radio } from 'antd'; -import React from 'react'; - -const onChange = (e: RadioChangeEvent) => { - console.log(`radio checked:${e.target.value}`); -}; - -const App: React.FC = () => ( - <> - - Hangzhou - Shanghai - Beijing - Chengdu - - - Hangzhou - - Shanghai - - Beijing - Chengdu - - - Hangzhou - Shanghai - Beijing - Chengdu - - -); - -export default App; -``` diff --git a/components/radio/demo/radiobutton.tsx b/components/radio/demo/radiobutton.tsx new file mode 100644 index 0000000000..0abdc3806e --- /dev/null +++ b/components/radio/demo/radiobutton.tsx @@ -0,0 +1,34 @@ +import React from 'react'; +import type { RadioChangeEvent } from 'antd'; +import { Radio } from 'antd'; + +const onChange = (e: RadioChangeEvent) => { + console.log(`radio checked:${e.target.value}`); +}; + +const App: React.FC = () => ( + <> + + Hangzhou + Shanghai + Beijing + Chengdu + + + Hangzhou + + Shanghai + + Beijing + Chengdu + + + Hangzhou + Shanghai + Beijing + Chengdu + + +); + +export default App; diff --git a/components/radio/demo/radiogroup-more.md b/components/radio/demo/radiogroup-more.md index 699f00d40c..aba2491f57 100644 --- a/components/radio/demo/radiogroup-more.md +++ b/components/radio/demo/radiogroup-more.md @@ -1,10 +1,3 @@ ---- -order: 2 -title: - zh-CN: Radio.Group 垂直 - en-US: Vertical Radio.Group ---- - ## zh-CN 垂直的 Radio.Group,配合更多输入框选项。 @@ -12,34 +5,3 @@ title: ## en-US Vertical Radio.Group, with more radios. - -```tsx -import type { RadioChangeEvent } from 'antd'; -import { Input, Radio, Space } from 'antd'; -import React, { useState } from 'react'; - -const App: React.FC = () => { - const [value, setValue] = useState(1); - - const onChange = (e: RadioChangeEvent) => { - console.log('radio checked', e.target.value); - setValue(e.target.value); - }; - - return ( - - - Option A - Option B - Option C - - More... - {value === 4 ? : null} - - - - ); -}; - -export default App; -``` diff --git a/components/radio/demo/radiogroup-more.tsx b/components/radio/demo/radiogroup-more.tsx new file mode 100644 index 0000000000..759e0516af --- /dev/null +++ b/components/radio/demo/radiogroup-more.tsx @@ -0,0 +1,28 @@ +import React, { useState } from 'react'; +import type { RadioChangeEvent } from 'antd'; +import { Input, Radio, Space } from 'antd'; + +const App: React.FC = () => { + const [value, setValue] = useState(1); + + const onChange = (e: RadioChangeEvent) => { + console.log('radio checked', e.target.value); + setValue(e.target.value); + }; + + return ( + + + Option A + Option B + Option C + + More... + {value === 4 ? : null} + + + + ); +}; + +export default App; diff --git a/components/radio/demo/radiogroup-options.md b/components/radio/demo/radiogroup-options.md index a9f507f2bd..347f8e616d 100644 --- a/components/radio/demo/radiogroup-options.md +++ b/components/radio/demo/radiogroup-options.md @@ -1,10 +1,3 @@ ---- -order: 2 -title: - zh-CN: Radio.Group 组合 - 配置方式 - en-US: Radio.Group group - optional ---- - ## zh-CN 通过配置 `options` 参数来渲染单选框。也可通过 `optionType` 参数来设置 Radio 类型。 @@ -12,70 +5,3 @@ title: ## en-US Render radios by configuring `options`. Radio type can also be set through the `optionType` parameter. - -```tsx -import type { RadioChangeEvent } from 'antd'; -import { Radio } from 'antd'; -import React, { useState } from 'react'; - -const plainOptions = ['Apple', 'Pear', 'Orange']; -const options = [ - { label: 'Apple', value: 'Apple' }, - { label: 'Pear', value: 'Pear' }, - { label: 'Orange', value: 'Orange' }, -]; -const optionsWithDisabled = [ - { label: 'Apple', value: 'Apple' }, - { label: 'Pear', value: 'Pear' }, - { label: 'Orange', value: 'Orange', disabled: true }, -]; - -const App: React.FC = () => { - const [value1, setValue1] = useState('Apple'); - const [value2, setValue2] = useState('Apple'); - const [value3, setValue3] = useState('Apple'); - const [value4, setValue4] = useState('Apple'); - - const onChange1 = ({ target: { value } }: RadioChangeEvent) => { - console.log('radio1 checked', value); - setValue1(value); - }; - - const onChange2 = ({ target: { value } }: RadioChangeEvent) => { - console.log('radio2 checked', value); - setValue2(value); - }; - - const onChange3 = ({ target: { value } }: RadioChangeEvent) => { - console.log('radio3 checked', value); - setValue3(value); - }; - - const onChange4 = ({ target: { value } }: RadioChangeEvent) => { - console.log('radio4 checked', value); - setValue4(value); - }; - - return ( - <> - -
    - -
    -
    - -
    -
    - - - ); -}; - -export default App; -``` diff --git a/components/radio/demo/radiogroup-options.tsx b/components/radio/demo/radiogroup-options.tsx new file mode 100644 index 0000000000..85662aaa28 --- /dev/null +++ b/components/radio/demo/radiogroup-options.tsx @@ -0,0 +1,64 @@ +import React, { useState } from 'react'; +import type { RadioChangeEvent } from 'antd'; +import { Radio } from 'antd'; + +const plainOptions = ['Apple', 'Pear', 'Orange']; +const options = [ + { label: 'Apple', value: 'Apple' }, + { label: 'Pear', value: 'Pear' }, + { label: 'Orange', value: 'Orange' }, +]; +const optionsWithDisabled = [ + { label: 'Apple', value: 'Apple' }, + { label: 'Pear', value: 'Pear' }, + { label: 'Orange', value: 'Orange', disabled: true }, +]; + +const App: React.FC = () => { + const [value1, setValue1] = useState('Apple'); + const [value2, setValue2] = useState('Apple'); + const [value3, setValue3] = useState('Apple'); + const [value4, setValue4] = useState('Apple'); + + const onChange1 = ({ target: { value } }: RadioChangeEvent) => { + console.log('radio1 checked', value); + setValue1(value); + }; + + const onChange2 = ({ target: { value } }: RadioChangeEvent) => { + console.log('radio2 checked', value); + setValue2(value); + }; + + const onChange3 = ({ target: { value } }: RadioChangeEvent) => { + console.log('radio3 checked', value); + setValue3(value); + }; + + const onChange4 = ({ target: { value } }: RadioChangeEvent) => { + console.log('radio4 checked', value); + setValue4(value); + }; + + return ( + <> + +
    + +
    +
    + +
    +
    + + + ); +}; + +export default App; diff --git a/components/radio/demo/radiogroup-with-name.md b/components/radio/demo/radiogroup-with-name.md index 5e2829b351..393a9c4ebf 100644 --- a/components/radio/demo/radiogroup-with-name.md +++ b/components/radio/demo/radiogroup-with-name.md @@ -1,10 +1,3 @@ ---- -order: 4 -title: - zh-CN: 单选组合 - 配合 name 使用 - en-US: Radio.Group with name ---- - ## zh-CN 可以为 Radio.Group 配置 `name` 参数,为组合内的 input 元素赋予相同的 `name` 属性,使浏览器把 Radio.Group 下的 Radio 真正看作是一组(例如可以通过方向键始终**在同一组内**更改选项)。 @@ -12,19 +5,3 @@ title: ## en-US Passing the `name` property to all `input[type="radio"]` that are in the same Radio.Group. It is usually used to let the browser see your Radio.Group as a real "group" and keep the default behavior. For example, using left/right keyboard arrow to change your selection that in the same Radio.Group. - -```tsx -import { Radio } from 'antd'; -import React from 'react'; - -const App: React.FC = () => ( - - A - B - C - D - -); - -export default App; -``` diff --git a/components/radio/demo/radiogroup-with-name.tsx b/components/radio/demo/radiogroup-with-name.tsx new file mode 100644 index 0000000000..f379c4d932 --- /dev/null +++ b/components/radio/demo/radiogroup-with-name.tsx @@ -0,0 +1,13 @@ +import React from 'react'; +import { Radio } from 'antd'; + +const App: React.FC = () => ( + + A + B + C + D + +); + +export default App; diff --git a/components/radio/demo/radiogroup.md b/components/radio/demo/radiogroup.md index 37edd7e6b3..c226d30594 100644 --- a/components/radio/demo/radiogroup.md +++ b/components/radio/demo/radiogroup.md @@ -1,10 +1,3 @@ ---- -order: 1 -title: - zh-CN: 单选组合 - en-US: Radio Group ---- - ## zh-CN 一组互斥的 Radio 配合使用。 @@ -12,29 +5,3 @@ title: ## en-US A group of radio components. - -```tsx -import type { RadioChangeEvent } from 'antd'; -import { Radio } from 'antd'; -import React, { useState } from 'react'; - -const App: React.FC = () => { - const [value, setValue] = useState(1); - - const onChange = (e: RadioChangeEvent) => { - console.log('radio checked', e.target.value); - setValue(e.target.value); - }; - - return ( - - A - B - C - D - - ); -}; - -export default App; -``` diff --git a/components/radio/demo/radiogroup.tsx b/components/radio/demo/radiogroup.tsx new file mode 100644 index 0000000000..d8398e27a8 --- /dev/null +++ b/components/radio/demo/radiogroup.tsx @@ -0,0 +1,23 @@ +import React, { useState } from 'react'; +import type { RadioChangeEvent } from 'antd'; +import { Radio } from 'antd'; + +const App: React.FC = () => { + const [value, setValue] = useState(1); + + const onChange = (e: RadioChangeEvent) => { + console.log('radio checked', e.target.value); + setValue(e.target.value); + }; + + return ( + + A + B + C + D + + ); +}; + +export default App; diff --git a/components/radio/demo/size.md b/components/radio/demo/size.md index a899e9b0c2..47090e9017 100644 --- a/components/radio/demo/size.md +++ b/components/radio/demo/size.md @@ -1,10 +1,3 @@ ---- -order: 5 -title: - zh-CN: 大小 - en-US: Size ---- - ## zh-CN 大中小三种组合,可以和表单输入框进行对应配合。 @@ -12,33 +5,3 @@ title: ## en-US There are three sizes available: large, medium, and small. It can coordinate with input box. - -```tsx -import { Radio } from 'antd'; -import React from 'react'; - -const App: React.FC = () => ( - <> - - Hangzhou - Shanghai - Beijing - Chengdu - - - Hangzhou - Shanghai - Beijing - Chengdu - - - Hangzhou - Shanghai - Beijing - Chengdu - - -); - -export default App; -``` diff --git a/components/radio/demo/size.tsx b/components/radio/demo/size.tsx new file mode 100644 index 0000000000..fe002a1f82 --- /dev/null +++ b/components/radio/demo/size.tsx @@ -0,0 +1,27 @@ +import React from 'react'; +import { Radio } from 'antd'; + +const App: React.FC = () => ( + <> + + Hangzhou + Shanghai + Beijing + Chengdu + + + Hangzhou + Shanghai + Beijing + Chengdu + + + Hangzhou + Shanghai + Beijing + Chengdu + + +); + +export default App; diff --git a/components/radio/group.tsx b/components/radio/group.tsx index a934543b6c..a1311123dd 100644 --- a/components/radio/group.tsx +++ b/components/radio/group.tsx @@ -8,6 +8,8 @@ import { RadioGroupContextProvider } from './context'; import type { RadioChangeEvent, RadioGroupButtonStyle, RadioGroupProps } from './interface'; import Radio from './radio'; +import useStyle from './style'; + const RadioGroup = React.forwardRef((props, ref) => { const { getPrefixCls, direction } = React.useContext(ConfigContext); const size = React.useContext(SizeContext); @@ -45,6 +47,10 @@ const RadioGroup = React.forwardRef((props, ref } = props; const prefixCls = getPrefixCls('radio', customizePrefixCls); const groupPrefixCls = `${prefixCls}-group`; + + // Style + const [wrapSSR, hashId] = useStyle(prefixCls); + let childrenToRender = children; // 如果存在 options, 优先使用 if (options && options.length > 0) { @@ -88,8 +94,9 @@ const RadioGroup = React.forwardRef((props, ref [`${groupPrefixCls}-rtl`]: direction === 'rtl', }, className, + hashId, ); - return ( + return wrapSSR(
    ((props, ref > {childrenToRender} -
    + , ); }); diff --git a/components/radio/index.en-US.md b/components/radio/index.en-US.md index 7c896ceec6..ed8d941d42 100644 --- a/components/radio/index.en-US.md +++ b/components/radio/index.en-US.md @@ -1,8 +1,10 @@ --- category: Components -type: Data Entry +group: Data Entry title: Radio cover: https://gw.alipayobjects.com/zos/alicdn/8cYb5seNB/Radio.svg +demo: + cols: 2 --- Radio. @@ -12,6 +14,20 @@ Radio. - Used to select a single state from multiple options. - The difference from Select is that Radio is visible to the user and can facilitate the comparison of choice, which means there shouldn't be too many of them. +## Examples + + +Basic +disabled +Radio Group +Vertical Radio.Group +Radio.Group group - optional +radio style +Radio.Group with name +Size +Solid radio button +测试 Badge 的样式 + ## API ### Radio/Radio.Button diff --git a/components/radio/index.zh-CN.md b/components/radio/index.zh-CN.md index 404830a478..7257348adc 100644 --- a/components/radio/index.zh-CN.md +++ b/components/radio/index.zh-CN.md @@ -1,9 +1,11 @@ --- category: Components subtitle: 单选框 -type: 数据录入 +group: 数据录入 title: Radio cover: https://gw.alipayobjects.com/zos/alicdn/8cYb5seNB/Radio.svg +demo: + cols: 2 --- 单选框。 @@ -13,6 +15,20 @@ cover: https://gw.alipayobjects.com/zos/alicdn/8cYb5seNB/Radio.svg - 用于在多个备选项中选中单个状态。 - 和 Select 的区别是,Radio 所有选项默认可见,方便用户在比较中选择,因此选项不宜过多。 +## 代码演示 + + +基本 +不可用 +单选组合 +Radio.Group 垂直 +Radio.Group 组合 - 配置方式 +按钮样式 +单选组合 - 配合 name 使用 +大小 +填底的按钮样式 +Badge style + ## API ### Radio/Radio.Button diff --git a/components/radio/interface.tsx b/components/radio/interface.tsx index f3224e49e2..861ad33c53 100644 --- a/components/radio/interface.tsx +++ b/components/radio/interface.tsx @@ -38,7 +38,15 @@ export interface RadioGroupContextProps { optionType?: RadioGroupOptionType; } -export type RadioProps = AbstractCheckboxProps; +export interface RadioProps extends AbstractCheckboxProps { + /** + * Control the appearance for Radio to display as button or not + * + * @default 'default' + * @internal + */ + optionType?: RadioGroupOptionType; +} export interface RadioChangeEventTarget extends RadioProps { checked: boolean; diff --git a/components/radio/radio.tsx b/components/radio/radio.tsx index afe06a5856..c4a8bf02fc 100644 --- a/components/radio/radio.tsx +++ b/components/radio/radio.tsx @@ -10,6 +10,8 @@ import warning from '../_util/warning'; import RadioGroupContext, { RadioOptionTypeContext } from './context'; import type { RadioChangeEvent, RadioProps } from './interface'; +import useStyle from './style'; + const InternalRadio: React.ForwardRefRenderFunction = (props, ref) => { const groupContext = React.useContext(RadioGroupContext); const radioOptionTypeContext = React.useContext(RadioOptionTypeContext); @@ -40,6 +42,9 @@ const InternalRadio: React.ForwardRefRenderFunction = ( ? `${radioPrefixCls}-button` : radioPrefixCls; + // Style + const [wrapSSR, hashId] = useStyle(radioPrefixCls); + const radioProps: RadioProps = { ...restProps }; // ===================== Disabled ===================== @@ -61,9 +66,10 @@ const InternalRadio: React.ForwardRefRenderFunction = ( [`${prefixCls}-wrapper-in-form-item`]: isFormItemInput, }, className, + hashId, ); - return ( + return wrapSSR( // eslint-disable-next-line jsx-a11y/label-has-associated-control + , ); }; diff --git a/components/radio/style/index.less b/components/radio/style/index.less deleted file mode 100644 index 5b97f8d818..0000000000 --- a/components/radio/style/index.less +++ /dev/null @@ -1,371 +0,0 @@ -@import '../../style/themes/index'; -@import '../../style/mixins/index'; - -@radio-prefix-cls: ~'@{ant-prefix}-radio'; -@radio-group-prefix-cls: ~'@{radio-prefix-cls}-group'; -@radio-inner-prefix-cls: ~'@{radio-prefix-cls}-inner'; -@radio-duration: 0.3s; -@radio-focus-shadow: 0 0 0 3px @slider-handle-color-focus-shadow; -@radio-button-focus-shadow: @radio-focus-shadow; - -.@{radio-group-prefix-cls} { - .reset-component(); - - display: inline-block; - font-size: 0; - - .@{ant-prefix}-badge-count { - z-index: 1; - } - - > .@{ant-prefix}-badge:not(:first-child) > .@{radio-prefix-cls}-button-wrapper { - border-left: none; - } -} - -// 一般状态 -.@{radio-prefix-cls}-wrapper { - .reset-component(); - position: relative; - display: inline-flex; - align-items: baseline; - margin-right: @radio-wrapper-margin-right; - cursor: pointer; - - &-disabled { - cursor: not-allowed; - } - - &::after { - display: inline-block; - width: 0; - overflow: hidden; - content: '\a0'; - } - - &&-in-form-item { - input[type='radio'] { - width: 14px; - height: 14px; - } - } -} - -.@{radio-prefix-cls} { - .reset-component(); - - position: relative; - top: @radio-top; - display: inline-block; - outline: none; - cursor: pointer; - - .@{radio-prefix-cls}-wrapper:hover &, - &:hover .@{radio-inner-prefix-cls}, - &-input:focus + .@{radio-inner-prefix-cls} { - border-color: @radio-dot-color; - } - - &-input:focus + .@{radio-inner-prefix-cls} { - box-shadow: @radio-focus-shadow; - } - - &-checked::after { - position: absolute; - top: 0; - left: 0; - width: 100%; - height: 100%; - border: 1px solid @radio-dot-color; - border-radius: 50%; - visibility: hidden; - animation: antRadioEffect 0.36s ease-in-out; - animation-fill-mode: both; - content: ''; - } - - &:hover::after, - .@{radio-prefix-cls}-wrapper:hover &::after { - visibility: visible; - } - - &-inner { - &::after { - position: absolute; - top: 50%; - left: 50%; - display: block; - width: @radio-size; - height: @radio-size; - margin-top: -(@radio-size / 2); - margin-left: -(@radio-size / 2); - background-color: @radio-dot-color; - border-top: 0; - border-left: 0; - border-radius: @radio-size; - transform: scale(0); - opacity: 0; - transition: all @radio-duration @ease-in-out-circ; - content: ' '; - } - - position: relative; - top: 0; - left: 0; - display: block; - width: @radio-size; - height: @radio-size; - background-color: @radio-button-bg; - border-color: @border-color-base; - border-style: solid; - border-width: @radio-border-width; - border-radius: 50%; - transition: all @radio-duration; - } - - &-input { - position: absolute; - top: 0; - right: 0; - bottom: 0; - left: 0; - z-index: 1; - cursor: pointer; - opacity: 0; - } - - &&-disabled { - .@{radio-inner-prefix-cls} { - border-color: @border-color-base; - } - } -} - -// 选中状态 -.@{radio-prefix-cls}-checked { - .@{radio-inner-prefix-cls} { - border-color: @radio-dot-color; - - &::after { - transform: scale((unit(@radio-dot-size) / unit(@radio-size))); - opacity: 1; - transition: all @radio-duration @ease-in-out-circ; - } - } -} - -.@{radio-prefix-cls}-disabled { - cursor: not-allowed; - - .@{radio-inner-prefix-cls} { - background-color: @input-disabled-bg; - cursor: not-allowed; - - &::after { - background-color: @radio-dot-disabled-color; - } - } - - .@{radio-prefix-cls}-input { - cursor: not-allowed; - } - - & + span { - color: @disabled-color; - cursor: not-allowed; - } -} - -span.@{radio-prefix-cls} + * { - padding-right: 8px; - padding-left: 8px; -} - -.@{radio-prefix-cls}-button-wrapper { - position: relative; - display: inline-block; - height: @btn-height-base; - margin: 0; - padding: 0 @radio-button-padding-horizontal; - color: @radio-button-color; - font-size: @font-size-base; - line-height: @btn-height-base - 2px; - background: @radio-button-bg; - border: @border-width-base @border-style-base @border-color-base; - // strange align fix for chrome but works - // https://gw.alipayobjects.com/zos/rmsportal/VFTfKXJuogBAXcvfAUWJ.gif - border-top-width: @border-width-base + 0.02px; - border-left-width: 0; - cursor: pointer; - transition: color 0.3s, background 0.3s, border-color 0.3s, box-shadow 0.3s; - - a { - color: @radio-button-color; - } - - > .@{radio-prefix-cls}-button { - position: absolute; - top: 0; - left: 0; - z-index: -1; - width: 100%; - height: 100%; - } - - .@{radio-group-prefix-cls}-large & { - height: @input-height-lg; - font-size: @font-size-lg; - line-height: @input-height-lg - 2px; - } - - .@{radio-group-prefix-cls}-small & { - height: @input-height-sm; - padding: 0 @control-padding-horizontal-sm - 1px; - line-height: @input-height-sm - 2px; - } - - &:not(:first-child) { - &::before { - position: absolute; - top: @border-width-base * -1; - left: -1px; - display: block; - box-sizing: content-box; - width: 1px; - height: 100%; - padding: @border-width-base 0; - background-color: @border-color-base; - transition: background-color 0.3s; - content: ''; - } - } - - &:first-child { - border-left: @border-width-base @border-style-base @border-color-base; - border-radius: @border-radius-base 0 0 @border-radius-base; - } - - &:last-child { - border-radius: 0 @border-radius-base @border-radius-base 0; - } - - &:first-child:last-child { - border-radius: @border-radius-base; - } - - &:hover { - position: relative; - color: @radio-dot-color; - } - - &:focus-within { - box-shadow: @radio-button-focus-shadow; - } - - .@{radio-prefix-cls}-inner, - input[type='checkbox'], - input[type='radio'] { - width: 0; - height: 0; - opacity: 0; - pointer-events: none; - } - - &-checked:not(&-disabled) { - z-index: 1; - color: @radio-dot-color; - background: @radio-button-checked-bg; - border-color: @radio-dot-color; - - &::before { - background-color: @radio-dot-color; - } - - &:first-child { - border-color: @radio-dot-color; - } - - &:hover { - color: @radio-button-hover-color; - border-color: @radio-button-hover-color; - - &::before { - background-color: @radio-button-hover-color; - } - } - - &:active { - color: @radio-button-active-color; - border-color: @radio-button-active-color; - - &::before { - background-color: @radio-button-active-color; - } - } - - &:focus-within { - box-shadow: @radio-button-focus-shadow; - } - } - - .@{radio-group-prefix-cls}-solid &-checked:not(&-disabled) { - color: @radio-solid-checked-color; - background: @radio-dot-color; - border-color: @radio-dot-color; - - &:hover { - color: @radio-solid-checked-color; - background: @radio-button-hover-color; - border-color: @radio-button-hover-color; - } - - &:active { - color: @radio-solid-checked-color; - background: @radio-button-active-color; - border-color: @radio-button-active-color; - } - - &:focus-within { - box-shadow: @radio-button-focus-shadow; - } - } - - &-disabled { - color: @disabled-color; - background-color: @input-disabled-bg; - border-color: @border-color-base; - cursor: not-allowed; - - &:first-child, - &:hover { - color: @disabled-color; - background-color: @input-disabled-bg; - border-color: @border-color-base; - } - - &:first-child { - border-left-color: @border-color-base; - } - } - - &-disabled&-checked { - color: @radio-disabled-button-checked-color; - background-color: @radio-disabled-button-checked-bg; - border-color: @border-color-base; - box-shadow: none; - } -} - -@keyframes antRadioEffect { - 0% { - transform: scale(1); - opacity: 0.5; - } - - 100% { - transform: scale(1.6); - opacity: 0; - } -} - -@import './rtl'; diff --git a/components/radio/style/index.tsx b/components/radio/style/index.tsx index d53a9fa2e6..4e7a56880b 100644 --- a/components/radio/style/index.tsx +++ b/components/radio/style/index.tsx @@ -1,3 +1,548 @@ -import '../../style/index.less'; -import './index.less'; -// deps-lint-skip: form +import { Keyframes } from '@ant-design/cssinjs'; +import type { FullToken, GenerateStyle } from '../../theme'; +import { genComponentStyleHook, mergeToken } from '../../theme'; +import { genFocusOutline, resetComponent } from '../../style'; + +// ============================== Tokens ============================== +export interface ComponentToken {} + +interface RadioToken extends FullToken<'Radio'> { + radioFocusShadow: string; + radioButtonFocusShadow: string; + + radioSize: number; + radioTop: number; + radioDotSize: number; + radioDotDisabledSize: number; + radioCheckedColor: string; + radioDotDisabledColor: string; + radioSolidCheckedColor: string; + + radioButtonBg: string; + radioButtonCheckedBg: string; + radioButtonColor: string; + radioButtonHoverColor: string; + radioButtonActiveColor: string; + radioButtonPaddingHorizontal: number; + radioDisabledButtonCheckedBg: string; + radioDisabledButtonCheckedColor: string; + radioWrapperMarginRight: number; +} + +// ============================== Styles ============================== +const antRadioEffect = new Keyframes('antRadioEffect', { + '0%': { transform: 'scale(1)', opacity: 0.5 }, + '100%': { transform: 'scale(1.6)', opacity: 0 }, +}); + +// styles from RadioGroup only +const getGroupRadioStyle: GenerateStyle = (token) => { + const { componentCls, antCls } = token; + const groupPrefixCls = `${componentCls}-group`; + + return { + [groupPrefixCls]: { + ...resetComponent(token), + display: 'inline-block', + fontSize: 0, + + // RTL + '&&-rtl': { + direction: 'rtl', + }, + + [`${antCls}-badge ${antCls}-badge-count`]: { + zIndex: 1, + }, + + [`> ${antCls}-badge:not(:first-child) > ${antCls}-button-wrapper`]: { + borderInlineStart: 'none', + }, + }, + }; +}; + +// Styles from radio-wrapper +const getRadioBasicStyle: GenerateStyle = (token) => { + const { + componentCls, + radioWrapperMarginRight, + radioCheckedColor, + radioTop, + radioSize, + motionDurationSlow, + motionDurationMid, + motionEaseInOut, + motionEaseInOutCirc, + radioButtonBg, + colorBorder, + lineWidth, + radioDotSize, + colorBgContainerDisabled, + colorTextDisabled, + paddingXS, + radioDotDisabledColor, + lineType, + radioDotDisabledSize, + wireframe, + colorWhite, + } = token; + const radioInnerPrefixCls = `${componentCls}-inner`; + + return { + [`${componentCls}-wrapper`]: { + ...resetComponent(token), + position: 'relative', + display: 'inline-flex', + alignItems: 'baseline', + marginInlineStart: 0, + marginInlineEnd: radioWrapperMarginRight, + cursor: 'pointer', + + // RTL + '&&-rtl': { + direction: 'rtl', + }, + + '&-disabled': { + cursor: 'not-allowed', + }, + + '&::after': { + display: 'inline-block', + width: 0, + overflow: 'hidden', + content: '"\\a0"', + }, + + // hashId 在 wrapper 上,只能铺平 + [`${componentCls}-checked::after`]: { + position: 'absolute', + insetBlockStart: 0, + insetInlineStart: 0, + width: '100%', + height: '100%', + border: `${lineWidth}px ${lineType} ${radioCheckedColor}`, + borderRadius: '50%', + visibility: 'hidden', + animationName: antRadioEffect, + animationDuration: motionDurationSlow, + animationTimingFunction: motionEaseInOut, + animationFillMode: 'both', + content: '""', + }, + + [componentCls]: { + ...resetComponent(token), + position: 'relative', + insetBlockStart: radioTop, + display: 'inline-block', + outline: 'none', + cursor: 'pointer', + }, + + [`${componentCls}-wrapper:hover &, + &:hover ${radioInnerPrefixCls}`]: { + borderColor: radioCheckedColor, + }, + + [`${componentCls}-input:focus-visible + ${radioInnerPrefixCls}`]: { + ...genFocusOutline(token), + }, + + [`${componentCls}:hover::after, ${componentCls}-wrapper:hover &::after`]: { + visibility: 'visible', + }, + + [`${componentCls}-inner`]: { + '&::after': { + boxSizing: 'border-box', + position: 'absolute', + insetBlockStart: '50%', + insetInlineStart: '50%', + display: 'block', + width: radioSize, + height: radioSize, + marginBlockStart: radioSize / -2, + marginInlineStart: radioSize / -2, + backgroundColor: wireframe ? radioCheckedColor : colorWhite, + borderBlockStart: 0, + borderInlineStart: 0, + borderRadius: radioSize, + transform: 'scale(0)', + opacity: 0, + transition: `all ${motionDurationSlow} ${motionEaseInOutCirc}`, + content: '""', + }, + + boxSizing: 'border-box', + position: 'relative', + insetBlockStart: 0, + insetInlineStart: 0, + display: 'block', + width: radioSize, + height: radioSize, + backgroundColor: radioButtonBg, + borderColor: colorBorder, + borderStyle: 'solid', + borderWidth: lineWidth, + borderRadius: '50%', + transition: `all ${motionDurationMid}`, + }, + + [`${componentCls}-input`]: { + position: 'absolute', + insetBlockStart: 0, + insetInlineEnd: 0, + insetBlockEnd: 0, + insetInlineStart: 0, + zIndex: 1, + cursor: 'pointer', + opacity: 0, + }, + + // 选中状态 + [`${componentCls}-checked`]: { + [radioInnerPrefixCls]: { + borderColor: radioCheckedColor, + backgroundColor: wireframe ? radioButtonBg : radioCheckedColor, + + '&::after': { + transform: `scale(${radioDotSize / radioSize})`, + opacity: 1, + transition: `all ${motionDurationSlow} ${motionEaseInOutCirc}`, + }, + }, + }, + + [`${componentCls}-disabled`]: { + cursor: 'not-allowed', + + [radioInnerPrefixCls]: { + backgroundColor: colorBgContainerDisabled, + borderColor: colorBorder, + cursor: 'not-allowed', + + '&::after': { + backgroundColor: radioDotDisabledColor, + }, + }, + + '&-input': { + cursor: 'not-allowed', + }, + + [`${componentCls}-disabled + span`]: { + color: colorTextDisabled, + cursor: 'not-allowed', + }, + + [`&${componentCls}-checked`]: { + [radioInnerPrefixCls]: { + '&::after': { + transform: `scale(${radioDotDisabledSize / radioSize})`, + }, + }, + }, + }, + + [`span${componentCls} + *`]: { + paddingInlineStart: paddingXS, + paddingInlineEnd: paddingXS, + }, + }, + }; +}; + +// Styles from radio-button +const getRadioButtonStyle: GenerateStyle = (token) => { + const { + radioButtonColor, + controlHeight, + componentCls, + lineWidth, + lineType, + colorBorder, + motionDurationSlow, + motionDurationMid, + radioButtonPaddingHorizontal, + fontSize, + radioButtonBg, + fontSizeLG, + controlHeightLG, + controlHeightSM, + paddingXS, + borderRadius, + borderRadiusSM, + borderRadiusLG, + radioCheckedColor, + radioButtonCheckedBg, + radioButtonHoverColor, + radioButtonActiveColor, + radioSolidCheckedColor, + colorTextDisabled, + colorBgContainerDisabled, + radioDisabledButtonCheckedColor, + radioDisabledButtonCheckedBg, + } = token; + return { + [`${componentCls}-button-wrapper`]: { + position: 'relative', + display: 'inline-block', + height: controlHeight, + margin: 0, + paddingInline: radioButtonPaddingHorizontal, + paddingBlock: 0, + color: radioButtonColor, + fontSize, + lineHeight: `${controlHeight - lineWidth * 2}px`, + background: radioButtonBg, + border: `${lineWidth}px ${lineType} ${colorBorder}`, + // strange align fix for chrome but works + // https://gw.alipayobjects.com/zos/rmsportal/VFTfKXJuogBAXcvfAUWJ.gif + borderBlockStartWidth: lineWidth + 0.02, + borderInlineStartWidth: 0, + borderInlineEndWidth: lineWidth, + cursor: 'pointer', + transition: [ + `color ${motionDurationMid}`, + `background ${motionDurationMid}`, + `border-color ${motionDurationMid}`, + `box-shadow ${motionDurationMid}`, + ].join(','), + + a: { + color: radioButtonColor, + }, + + [`> ${componentCls}-button`]: { + position: 'absolute', + insetBlockStart: 0, + insetInlineStart: 0, + zIndex: -1, + width: '100%', + height: '100%', + }, + + '&:not(:first-child)': { + '&::before': { + position: 'absolute', + insetBlockStart: -lineWidth, + insetInlineStart: -lineWidth, + display: 'block', + boxSizing: 'content-box', + width: 1, + height: '100%', + paddingBlock: lineWidth, + paddingInline: 0, + backgroundColor: colorBorder, + transition: `background-color ${motionDurationSlow}`, + content: '""', + }, + }, + + '&:first-child': { + borderInlineStart: `${lineWidth}px ${lineType} ${colorBorder}`, + borderStartStartRadius: borderRadius, + borderEndStartRadius: borderRadius, + }, + + '&:last-child': { + borderStartEndRadius: borderRadius, + borderEndEndRadius: borderRadius, + }, + + '&:first-child:last-child': { + borderRadius, + }, + + [`${componentCls}-group-large &`]: { + height: controlHeightLG, + fontSize: fontSizeLG, + lineHeight: `${controlHeightLG - lineWidth * 2}px`, + + '&:first-child': { + borderStartStartRadius: borderRadiusLG, + borderEndStartRadius: borderRadiusLG, + }, + + '&:last-child': { + borderStartEndRadius: borderRadiusLG, + borderEndEndRadius: borderRadiusLG, + }, + }, + + [`${componentCls}-group-small &`]: { + height: controlHeightSM, + paddingInline: paddingXS - lineWidth, + paddingBlock: 0, + lineHeight: `${controlHeightSM - lineWidth * 2}px`, + + '&:first-child': { + borderStartStartRadius: borderRadiusSM, + borderEndStartRadius: borderRadiusSM, + }, + + '&:last-child': { + borderStartEndRadius: borderRadiusSM, + borderEndEndRadius: borderRadiusSM, + }, + }, + + '&:hover': { + position: 'relative', + color: radioCheckedColor, + }, + + '&:has(:focus-visible)': { + ...genFocusOutline(token), + }, + + [`${componentCls}-inner, input[type='checkbox'], input[type='radio']`]: { + width: 0, + height: 0, + opacity: 0, + pointerEvents: 'none', + }, + + '&-checked:not(&-disabled)': { + zIndex: 1, + color: radioCheckedColor, + background: radioButtonCheckedBg, + borderColor: radioCheckedColor, + + '&::before': { + backgroundColor: radioCheckedColor, + }, + + '&:first-child': { + borderColor: radioCheckedColor, + }, + + '&:hover': { + color: radioButtonHoverColor, + borderColor: radioButtonHoverColor, + + '&::before': { + backgroundColor: radioButtonHoverColor, + }, + }, + + '&:active': { + color: radioButtonActiveColor, + borderColor: radioButtonActiveColor, + + '&::before': { + backgroundColor: radioButtonActiveColor, + }, + }, + }, + + [`${componentCls}-group-solid &-checked:not(&-disabled)`]: { + color: radioSolidCheckedColor, + background: radioCheckedColor, + borderColor: radioCheckedColor, + + '&:hover': { + color: radioSolidCheckedColor, + background: radioButtonHoverColor, + borderColor: radioButtonHoverColor, + }, + + '&:active': { + color: radioSolidCheckedColor, + background: radioButtonActiveColor, + borderColor: radioButtonActiveColor, + }, + }, + + '&-disabled': { + color: colorTextDisabled, + backgroundColor: colorBgContainerDisabled, + borderColor: colorBorder, + cursor: 'not-allowed', + + '&:first-child, &:hover': { + color: colorTextDisabled, + backgroundColor: colorBgContainerDisabled, + borderColor: colorBorder, + }, + }, + + '&-disabled&-checked': { + color: radioDisabledButtonCheckedColor, + backgroundColor: radioDisabledButtonCheckedBg, + borderColor: colorBorder, + boxShadow: 'none', + }, + }, + }; +}; + +// ============================== Export ============================== +export default genComponentStyleHook('Radio', (token) => { + const { + padding, + lineWidth, + colorBgContainerDisabled, + colorTextDisabled, + colorBgContainer, + fontSize, + lineHeight, + fontSizeLG, + controlOutline, + colorPrimaryHover, + colorPrimaryActive, + colorText, + colorPrimary, + marginXS, + controlOutlineWidth, + wireframe, + } = token; + + // Radio + const radioFocusShadow = `0 0 0 ${controlOutlineWidth}px ${controlOutline}`; + const radioButtonFocusShadow = radioFocusShadow; + + const radioSize = fontSizeLG; + const radioTop = (Math.round(fontSize * lineHeight) - radioSize) / 2; + const dotPadding = 4; // Fixed value + const radioDotDisabledSize = radioSize - dotPadding * 2; + const radioDotSize = wireframe ? radioDotDisabledSize : radioSize - (dotPadding + lineWidth) * 2; + const radioCheckedColor = colorPrimary; + + // Radio buttons + const radioButtonColor = colorText; + const radioButtonHoverColor = colorPrimaryHover; + const radioButtonActiveColor = colorPrimaryActive; + const radioButtonPaddingHorizontal = padding - lineWidth; + const radioDisabledButtonCheckedColor = colorTextDisabled; + const radioWrapperMarginRight = marginXS; + + const radioToken = mergeToken(token, { + radioFocusShadow, + radioButtonFocusShadow, + radioSize, + radioTop, + radioDotSize, + radioDotDisabledSize, + radioCheckedColor, + radioDotDisabledColor: colorTextDisabled, + radioSolidCheckedColor: colorBgContainer, + radioButtonBg: colorBgContainer, + radioButtonCheckedBg: colorBgContainer, + radioButtonColor, + radioButtonHoverColor, + radioButtonActiveColor, + radioButtonPaddingHorizontal, + radioDisabledButtonCheckedBg: colorBgContainerDisabled, + radioDisabledButtonCheckedColor, + radioWrapperMarginRight, + }); + + return [ + getGroupRadioStyle(radioToken), + getRadioBasicStyle(radioToken), + getRadioButtonStyle(radioToken), + ]; +}); diff --git a/components/radio/style/rtl.less b/components/radio/style/rtl.less deleted file mode 100644 index c3c367ac18..0000000000 --- a/components/radio/style/rtl.less +++ /dev/null @@ -1,61 +0,0 @@ -@import '../../style/themes/index'; -@import '../../style/mixins/index'; - -@radio-prefix-cls: ~'@{ant-prefix}-radio'; -@radio-group-prefix-cls: ~'@{radio-prefix-cls}-group'; -@radio-prefix-cls-button-wrapper: ~'@{radio-prefix-cls}-button-wrapper'; - -.@{radio-group-prefix-cls} { - &&-rtl { - direction: rtl; - } -} - -// 一般状态 -.@{radio-prefix-cls}-wrapper { - &&-rtl { - margin-right: 0; - margin-left: @radio-wrapper-margin-right; - direction: rtl; - } -} - -.@{radio-prefix-cls-button-wrapper} { - &&-rtl { - border-right-width: 0; - border-left-width: @border-width-base; - } - - &:not(:first-child) { - &::before { - .@{radio-prefix-cls-button-wrapper}.@{radio-prefix-cls-button-wrapper}-rtl& { - right: -1px; - left: 0; - } - } - } - - &:first-child { - .@{radio-prefix-cls-button-wrapper}.@{radio-prefix-cls-button-wrapper}-rtl& { - border-right: @border-width-base @border-style-base @border-color-base; - border-radius: 0 @border-radius-base @border-radius-base 0; - } - .@{radio-prefix-cls-button-wrapper}-checked:not([class*=~"' @{radio-prefix-cls}-button-wrapper-disabled'"])& { - border-right-color: @radio-button-hover-color; - } - } - - &:last-child { - .@{radio-prefix-cls-button-wrapper}.@{radio-prefix-cls-button-wrapper}-rtl& { - border-radius: @border-radius-base 0 0 @border-radius-base; - } - } - - &-disabled { - &:first-child { - .@{radio-prefix-cls-button-wrapper}.@{radio-prefix-cls-button-wrapper}-rtl& { - border-right-color: @border-color-base; - } - } - } -} diff --git a/components/rate/__tests__/__snapshots__/demo-extend.test.ts.snap b/components/rate/__tests__/__snapshots__/demo-extend.test.ts.snap index 7d4de64637..f19499672f 100644 --- a/components/rate/__tests__/__snapshots__/demo-extend.test.ts.snap +++ b/components/rate/__tests__/__snapshots__/demo-extend.test.ts.snap @@ -1,6 +1,6 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP -exports[`renders ./components/rate/demo/basic.md extend context correctly 1`] = ` +exports[`renders ./components/rate/demo/basic.tsx extend context correctly 1`] = `
      `; -exports[`renders ./components/rate/demo/character.md extend context correctly 1`] = ` +exports[`renders ./components/rate/demo/character.tsx extend context correctly 1`] = ` Array [
        `; -exports[`renders ./components/rate/demo/half.md extend context correctly 1`] = ` +exports[`renders ./components/rate/demo/half.tsx extend context correctly 1`] = `
          `; -exports[`renders ./components/rate/demo/text.md extend context correctly 1`] = ` +exports[`renders ./components/rate/demo/text.tsx extend context correctly 1`] = `
            `; -exports[`renders ./components/rate/demo/character.md correctly 1`] = ` +exports[`renders ./components/rate/demo/character.tsx correctly 1`] = ` Array [
              `; -exports[`renders ./components/rate/demo/half.md correctly 1`] = ` +exports[`renders ./components/rate/demo/half.tsx correctly 1`] = `
                `; -exports[`renders ./components/rate/demo/text.md correctly 1`] = ` +exports[`renders ./components/rate/demo/text.tsx correctly 1`] = `
                  ; - -export default App; -``` diff --git a/components/rate/demo/basic.tsx b/components/rate/demo/basic.tsx new file mode 100644 index 0000000000..5666be18dc --- /dev/null +++ b/components/rate/demo/basic.tsx @@ -0,0 +1,6 @@ +import React from 'react'; +import { Rate } from 'antd'; + +const App: React.FC = () => ; + +export default App; diff --git a/components/rate/demo/character-function.md b/components/rate/demo/character-function.md index 59f8661fa7..a305b53a76 100644 --- a/components/rate/demo/character-function.md +++ b/components/rate/demo/character-function.md @@ -1,10 +1,3 @@ ---- -order: 6 -title: - zh-CN: 自定义字符 - en-US: Customize character ---- - ## zh-CN 可以使用 `(RateProps) => ReactNode` 的方式自定义每一个字符。 @@ -12,27 +5,3 @@ title: ## en-US Can customize each character using `(RateProps) => ReactNode`. - -```tsx -import { FrownOutlined, MehOutlined, SmileOutlined } from '@ant-design/icons'; -import { Rate } from 'antd'; -import React from 'react'; - -const customIcons: Record = { - 1: , - 2: , - 3: , - 4: , - 5: , -}; - -const App: React.FC = () => ( - <> - index + 1} /> -
                  - customIcons[index + 1]} /> - -); - -export default App; -``` diff --git a/components/rate/demo/character-function.tsx b/components/rate/demo/character-function.tsx new file mode 100644 index 0000000000..d36bd06454 --- /dev/null +++ b/components/rate/demo/character-function.tsx @@ -0,0 +1,21 @@ +import React from 'react'; +import { FrownOutlined, MehOutlined, SmileOutlined } from '@ant-design/icons'; +import { Rate } from 'antd'; + +const customIcons: Record = { + 1: , + 2: , + 3: , + 4: , + 5: , +}; + +const App: React.FC = () => ( + <> + index + 1} /> +
                  + customIcons[index + 1]} /> + +); + +export default App; diff --git a/components/rate/demo/character.md b/components/rate/demo/character.md index 72209fbefa..647e116d12 100644 --- a/components/rate/demo/character.md +++ b/components/rate/demo/character.md @@ -1,10 +1,3 @@ ---- -order: 5 -title: - zh-CN: 其他字符 - en-US: Other Character ---- - ## zh-CN 可以将星星替换为其他字符,比如字母,数字,字体图标甚至中文。 @@ -12,21 +5,3 @@ title: ## en-US Replace the default star to other character like alphabet, digit, iconfont or even Chinese word. - -```tsx -import { HeartOutlined } from '@ant-design/icons'; -import { Rate } from 'antd'; -import React from 'react'; - -const App: React.FC = () => ( - <> - } allowHalf /> -
                  - -
                  - - -); - -export default App; -``` diff --git a/components/rate/demo/character.tsx b/components/rate/demo/character.tsx new file mode 100644 index 0000000000..3ae2c3ae0a --- /dev/null +++ b/components/rate/demo/character.tsx @@ -0,0 +1,15 @@ +import React from 'react'; +import { HeartOutlined } from '@ant-design/icons'; +import { Rate } from 'antd'; + +const App: React.FC = () => ( + <> + } allowHalf /> +
                  + +
                  + + +); + +export default App; diff --git a/components/rate/demo/clear.md b/components/rate/demo/clear.md index f7ecd60420..ed5a38292c 100644 --- a/components/rate/demo/clear.md +++ b/components/rate/demo/clear.md @@ -1,10 +1,3 @@ ---- -order: 4 -title: - zh-CN: 清除 - en-US: Clear star ---- - ## zh-CN 支持允许或者禁用清除。 @@ -12,20 +5,3 @@ title: ## en-US Support set allow to clear star when click again. - -```tsx -import { Rate } from 'antd'; -import React from 'react'; - -const App: React.FC = () => ( - <> - - allowClear: true -
                  - - allowClear: false - -); - -export default App; -``` diff --git a/components/rate/demo/clear.tsx b/components/rate/demo/clear.tsx new file mode 100644 index 0000000000..845e31ee12 --- /dev/null +++ b/components/rate/demo/clear.tsx @@ -0,0 +1,14 @@ +import React from 'react'; +import { Rate } from 'antd'; + +const App: React.FC = () => ( + <> + + allowClear: true +
                  + + allowClear: false + +); + +export default App; diff --git a/components/rate/demo/disabled.md b/components/rate/demo/disabled.md index 18c630dc0e..e0c3a72b5f 100644 --- a/components/rate/demo/disabled.md +++ b/components/rate/demo/disabled.md @@ -1,10 +1,3 @@ ---- -order: 3 -title: - zh-CN: 只读 - en-US: Read only ---- - ## zh-CN 只读,无法进行鼠标交互。 @@ -12,12 +5,3 @@ title: ## en-US Read only, can't use mouse to interact. - -```tsx -import { Rate } from 'antd'; -import React from 'react'; - -const App: React.FC = () => ; - -export default App; -``` diff --git a/components/rate/demo/disabled.tsx b/components/rate/demo/disabled.tsx new file mode 100644 index 0000000000..00dc09ba03 --- /dev/null +++ b/components/rate/demo/disabled.tsx @@ -0,0 +1,6 @@ +import React from 'react'; +import { Rate } from 'antd'; + +const App: React.FC = () => ; + +export default App; diff --git a/components/rate/demo/half.md b/components/rate/demo/half.md index c34f3a79a1..3750ecae6f 100644 --- a/components/rate/demo/half.md +++ b/components/rate/demo/half.md @@ -1,10 +1,3 @@ ---- -order: 1 -title: - zh-CN: 半星 - en-US: Half star ---- - ## zh-CN 支持选中半星。 @@ -12,12 +5,3 @@ title: ## en-US Support select half star. - -```tsx -import { Rate } from 'antd'; -import React from 'react'; - -const App: React.FC = () => ; - -export default App; -``` diff --git a/components/rate/demo/half.tsx b/components/rate/demo/half.tsx new file mode 100644 index 0000000000..9bb125c9d6 --- /dev/null +++ b/components/rate/demo/half.tsx @@ -0,0 +1,6 @@ +import React from 'react'; +import { Rate } from 'antd'; + +const App: React.FC = () => ; + +export default App; diff --git a/components/rate/demo/text.md b/components/rate/demo/text.md index 3a1e6da46d..19e6094f66 100644 --- a/components/rate/demo/text.md +++ b/components/rate/demo/text.md @@ -1,10 +1,3 @@ ---- -order: 2 -title: - zh-CN: 文案展现 - en-US: Show copywriting ---- - ## zh-CN 给评分组件加上文案展示。 @@ -12,23 +5,3 @@ title: ## en-US Add copywriting in rate components. - -```tsx -import { Rate } from 'antd'; -import React, { useState } from 'react'; - -const desc = ['terrible', 'bad', 'normal', 'good', 'wonderful']; - -const App: React.FC = () => { - const [value, setValue] = useState(3); - - return ( - - - {value ? {desc[value - 1]} : ''} - - ); -}; - -export default App; -``` diff --git a/components/rate/demo/text.tsx b/components/rate/demo/text.tsx new file mode 100644 index 0000000000..a306d87fd4 --- /dev/null +++ b/components/rate/demo/text.tsx @@ -0,0 +1,17 @@ +import React, { useState } from 'react'; +import { Rate } from 'antd'; + +const desc = ['terrible', 'bad', 'normal', 'good', 'wonderful']; + +const App: React.FC = () => { + const [value, setValue] = useState(3); + + return ( + + + {value ? {desc[value - 1]} : ''} + + ); +}; + +export default App; diff --git a/components/rate/index.en-US.md b/components/rate/index.en-US.md index 45a9c42100..e063dcac77 100644 --- a/components/rate/index.en-US.md +++ b/components/rate/index.en-US.md @@ -1,8 +1,10 @@ --- category: Components -type: Data Entry +group: Data Entry title: Rate cover: https://gw.alipayobjects.com/zos/alicdn/R5uiIWmxe/Rate.svg +demo: + cols: 2 --- Rate component. @@ -12,6 +14,17 @@ Rate component. - Show evaluation. - A quick rating operation on something. +## Examples + + +Basic +Half star +Show copywriting +Read only +Clear star +Other Character +Customize character + ## API | Property | Description | type | Default | Version | @@ -35,7 +48,7 @@ Rate component. ## Methods -| Name | Description | -| --- | --- | -| blur() | Remove focus | -| focus() | Get focus | +| Name | Description | +| ------- | ------------ | +| blur() | Remove focus | +| focus() | Get focus | diff --git a/components/rate/index.tsx b/components/rate/index.tsx index db68c3b59d..7b53d695d9 100644 --- a/components/rate/index.tsx +++ b/components/rate/index.tsx @@ -1,9 +1,11 @@ import StarFilled from '@ant-design/icons/StarFilled'; +import classNames from 'classnames'; import RcRate from 'rc-rate'; import type { RateProps as RcRateProps } from 'rc-rate/lib/Rate'; import * as React from 'react'; import { ConfigContext } from '../config-provider'; import Tooltip from '../tooltip'; +import useStyle from './style'; export interface RateProps extends RcRateProps { tooltips?: Array; @@ -25,15 +27,19 @@ const Rate = React.forwardRef((props, ref) => { const { getPrefixCls, direction } = React.useContext(ConfigContext); const ratePrefixCls = getPrefixCls('rate', prefixCls); - return ( + // Style + const [wrapSSR, hashId] = useStyle(ratePrefixCls); + + return wrapSSR( + />, ); }); diff --git a/components/rate/index.zh-CN.md b/components/rate/index.zh-CN.md index c5be4b345e..949a5e4e5b 100644 --- a/components/rate/index.zh-CN.md +++ b/components/rate/index.zh-CN.md @@ -1,9 +1,11 @@ --- category: Components subtitle: 评分 -type: 数据录入 +group: 数据录入 title: Rate cover: https://gw.alipayobjects.com/zos/alicdn/R5uiIWmxe/Rate.svg +demo: + cols: 2 --- 评分组件。 @@ -13,6 +15,17 @@ cover: https://gw.alipayobjects.com/zos/alicdn/R5uiIWmxe/Rate.svg - 对评价进行展示。 - 对事物进行快速的评级操作。 +## 代码演示 + + +基本 +半星 +文案展现 +只读 +清除 +其他字符 +自定义字符 + ## API | 属性 | 说明 | 类型 | 默认值 | 版本 | @@ -36,7 +49,7 @@ cover: https://gw.alipayobjects.com/zos/alicdn/R5uiIWmxe/Rate.svg ## 方法 -| 名称 | 描述 | -| --- | --- | -| blur() | 移除焦点 | +| 名称 | 描述 | +| ------- | -------- | +| blur() | 移除焦点 | | focus() | 获取焦点 | diff --git a/components/rate/style/index.less b/components/rate/style/index.less deleted file mode 100644 index 56a274b1bf..0000000000 --- a/components/rate/style/index.less +++ /dev/null @@ -1,91 +0,0 @@ -@import '../../style/themes/index'; -@import '../../style/mixins/index'; - -@rate-prefix-cls: ~'@{ant-prefix}-rate'; - -.@{rate-prefix-cls} { - .reset-component(); - - display: inline-block; - margin: 0; - padding: 0; - color: @rate-star-color; - font-size: @rate-star-size; - line-height: unset; - list-style: none; - outline: none; - - &-disabled &-star { - cursor: default; - - > div:hover { - transform: scale(1); - } - } - - &-star { - position: relative; - display: inline-block; - color: inherit; - cursor: pointer; - - &:not(:last-child) { - margin-right: 8px; - } - - > div { - transition: all 0.3s, outline 0s; - - &:hover { - transform: @rate-star-hover-scale; - } - - &:focus { - outline: 0; - } - - &:focus-visible { - outline: 1px dashed @rate-star-color; - transform: @rate-star-hover-scale; - } - } - - &-first, - &-second { - color: @rate-star-bg; - transition: all 0.3s; - user-select: none; - .@{iconfont-css-prefix} { - vertical-align: middle; - } - } - - &-first { - position: absolute; - top: 0; - left: 0; - width: 50%; - height: 100%; - overflow: hidden; - opacity: 0; - } - - &-half &-first, - &-half &-second { - opacity: 1; - } - - &-half &-first, - &-full &-second { - color: inherit; - } - } - - &-text { - display: inline-block; - margin: 0 8px; - font-size: @font-size-base; - } -} - -@import './rtl'; diff --git a/components/rate/style/index.tsx b/components/rate/style/index.tsx index 7bd39f61bf..6a68f4e1bd 100644 --- a/components/rate/style/index.tsx +++ b/components/rate/style/index.tsx @@ -1,5 +1,135 @@ -import '../../style/index.less'; -import './index.less'; +import type { CSSObject } from '@ant-design/cssinjs'; +import type { FullToken, GenerateStyle } from '../../theme'; +import { genComponentStyleHook, mergeToken } from '../../theme'; +import { resetComponent } from '../../style'; -// style dependencies -import '../../tooltip/style'; +export type ComponentToken = {}; + +interface RateToken extends FullToken<'Rate'> { + rateStarColor: string; + rateStarSize: number; + rateStarHoverScale: CSSObject['transform']; + defaultColor: string; +} + +const genRateStarStyle: GenerateStyle = (token) => { + const { componentCls } = token; + + return { + [`${componentCls}-star`]: { + position: 'relative', + display: 'inline-block', + color: 'inherit', + cursor: 'pointer', + + '&:not(:last-child)': { + marginInlineEnd: token.marginXS, + }, + + '> div': { + transition: `all ${token.motionDurationMid}, outline 0s`, + + '&:hover': { + transform: token.rateStarHoverScale, + }, + + '&:focus': { + outline: 0, + }, + + '&:focus-visible': { + outline: `${token.lineWidth}px dashed ${token.rateStarColor}`, + transform: token.rateStarHoverScale, + }, + }, + + '&-first, &-second': { + color: token.defaultColor, + transition: `all ${token.motionDurationMid}`, + userSelect: 'none', + + [token.iconCls]: { + verticalAlign: 'middle', + }, + }, + + '&-first': { + position: 'absolute', + top: 0, + insetInlineStart: 0, + width: '50%', + height: '100%', + overflow: 'hidden', + opacity: 0, + }, + + [`&-half ${componentCls}-star-first, &-half ${componentCls}-star-second`]: { + opacity: 1, + }, + + [`&-half ${componentCls}-star-first, &-full ${componentCls}-star-second`]: { + color: 'inherit', + }, + }, + }; +}; + +const genRateRtlStyle = (token: RateToken): CSSObject => ({ + [`&-rtl${token.componentCls}`]: { + direction: 'rtl', + }, +}); + +const genRateStyle: GenerateStyle = (token) => { + const { componentCls } = token; + + return { + [componentCls]: { + ...resetComponent(token), + + display: 'inline-block', + margin: 0, + padding: 0, + color: token.rateStarColor, + fontSize: token.rateStarSize, + lineHeight: 'unset', + listStyle: 'none', + outline: 'none', + + // disable styles + [`&-disabled${componentCls} ${componentCls}-star`]: { + cursor: 'default', + + '&:hover': { + transform: 'scale(1)', + }, + }, + + // star styles + ...genRateStarStyle(token), + + // text styles + [`+ ${componentCls}-text`]: { + display: 'inline-block', + marginInlineStart: token.marginXS, + fontSize: token.fontSize, + }, + + // rtl styles + ...genRateRtlStyle(token), + }, + }; +}; + +// ============================== Export ============================== +export default genComponentStyleHook('Rate', (token) => { + const { colorFillContent } = token; + + const rateToken = mergeToken(token, { + rateStarColor: token['yellow-6'], + rateStarSize: token.controlHeightLG * 0.5, + rateStarHoverScale: 'scale(1.1)', + defaultColor: colorFillContent, + }); + return [genRateStyle(rateToken)]; +}); diff --git a/components/rate/style/rtl.less b/components/rate/style/rtl.less deleted file mode 100644 index 6a997955e5..0000000000 --- a/components/rate/style/rtl.less +++ /dev/null @@ -1,21 +0,0 @@ -.@{rate-prefix-cls} { - &-rtl { - direction: rtl; - } - - &-star { - &:not(:last-child) { - .@{rate-prefix-cls}-rtl & { - margin-right: 0; - margin-left: 8px; - } - } - - &-first { - .@{rate-prefix-cls}-rtl & { - right: 0; - left: auto; - } - } - } -} diff --git a/components/result/__tests__/__snapshots__/demo-extend.test.ts.snap b/components/result/__tests__/__snapshots__/demo-extend.test.ts.snap index ad65e7f817..bd35da1c35 100644 --- a/components/result/__tests__/__snapshots__/demo-extend.test.ts.snap +++ b/components/result/__tests__/__snapshots__/demo-extend.test.ts.snap @@ -1,6 +1,6 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP -exports[`renders ./components/result/demo/403.md extend context correctly 1`] = ` +exports[`renders ./components/result/demo/403.tsx extend context correctly 1`] = `
                  @@ -313,7 +313,7 @@ exports[`renders ./components/result/demo/403.md extend context correctly 1`] =
                  `; -exports[`renders ./components/result/demo/404.md extend context correctly 1`] = ` +exports[`renders ./components/result/demo/404.tsx extend context correctly 1`] = `
                  @@ -644,7 +644,7 @@ exports[`renders ./components/result/demo/404.md extend context correctly 1`] =
                  `; -exports[`renders ./components/result/demo/500.md extend context correctly 1`] = ` +exports[`renders ./components/result/demo/500.tsx extend context correctly 1`] = `
                  @@ -1022,7 +1022,7 @@ exports[`renders ./components/result/demo/500.md extend context correctly 1`] =
                  `; -exports[`renders ./components/result/demo/customIcon.md extend context correctly 1`] = ` +exports[`renders ./components/result/demo/customIcon.tsx extend context correctly 1`] = `
                  @@ -1069,7 +1069,7 @@ exports[`renders ./components/result/demo/customIcon.md extend context correctly
                  `; -exports[`renders ./components/result/demo/error.md extend context correctly 1`] = ` +exports[`renders ./components/result/demo/error.tsx extend context correctly 1`] = `
                  @@ -1209,7 +1209,7 @@ exports[`renders ./components/result/demo/error.md extend context correctly 1`]
                  `; -exports[`renders ./components/result/demo/info.md extend context correctly 1`] = ` +exports[`renders ./components/result/demo/info.tsx extend context correctly 1`] = `
                  @@ -1256,7 +1256,7 @@ exports[`renders ./components/result/demo/info.md extend context correctly 1`] =
                  `; -exports[`renders ./components/result/demo/success.md extend context correctly 1`] = ` +exports[`renders ./components/result/demo/success.tsx extend context correctly 1`] = `
                  @@ -1316,7 +1316,7 @@ exports[`renders ./components/result/demo/success.md extend context correctly 1`
                  `; -exports[`renders ./components/result/demo/warning.md extend context correctly 1`] = ` +exports[`renders ./components/result/demo/warning.tsx extend context correctly 1`] = `
                  diff --git a/components/result/__tests__/__snapshots__/demo.test.ts.snap b/components/result/__tests__/__snapshots__/demo.test.ts.snap index 63a85db568..b58f6c79cf 100644 --- a/components/result/__tests__/__snapshots__/demo.test.ts.snap +++ b/components/result/__tests__/__snapshots__/demo.test.ts.snap @@ -1,6 +1,6 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP -exports[`renders ./components/result/demo/403.md correctly 1`] = ` +exports[`renders ./components/result/demo/403.tsx correctly 1`] = `
                  @@ -313,7 +313,7 @@ exports[`renders ./components/result/demo/403.md correctly 1`] = `
                  `; -exports[`renders ./components/result/demo/404.md correctly 1`] = ` +exports[`renders ./components/result/demo/404.tsx correctly 1`] = `
                  @@ -644,7 +644,7 @@ exports[`renders ./components/result/demo/404.md correctly 1`] = `
                  `; -exports[`renders ./components/result/demo/500.md correctly 1`] = ` +exports[`renders ./components/result/demo/500.tsx correctly 1`] = `
                  @@ -1022,7 +1022,7 @@ exports[`renders ./components/result/demo/500.md correctly 1`] = `
                  `; -exports[`renders ./components/result/demo/customIcon.md correctly 1`] = ` +exports[`renders ./components/result/demo/customIcon.tsx correctly 1`] = `
                  @@ -1069,7 +1069,7 @@ exports[`renders ./components/result/demo/customIcon.md correctly 1`] = `
                  `; -exports[`renders ./components/result/demo/error.md correctly 1`] = ` +exports[`renders ./components/result/demo/error.tsx correctly 1`] = `
                  @@ -1209,7 +1209,7 @@ exports[`renders ./components/result/demo/error.md correctly 1`] = `
                  `; -exports[`renders ./components/result/demo/info.md correctly 1`] = ` +exports[`renders ./components/result/demo/info.tsx correctly 1`] = `
                  @@ -1256,7 +1256,7 @@ exports[`renders ./components/result/demo/info.md correctly 1`] = `
                  `; -exports[`renders ./components/result/demo/success.md correctly 1`] = ` +exports[`renders ./components/result/demo/success.tsx correctly 1`] = `
                  @@ -1316,7 +1316,7 @@ exports[`renders ./components/result/demo/success.md correctly 1`] = `
                  `; -exports[`renders ./components/result/demo/warning.md correctly 1`] = ` +exports[`renders ./components/result/demo/warning.tsx correctly 1`] = `
                  diff --git a/components/result/demo/403.md b/components/result/demo/403.md index 590ae7af24..ac10e3f793 100644 --- a/components/result/demo/403.md +++ b/components/result/demo/403.md @@ -1,10 +1,3 @@ ---- -order: 4 -title: - zh-CN: 403 - en-US: 403 ---- - ## zh-CN 你没有此页面的访问权限。 @@ -12,19 +5,3 @@ title: ## en-US you are not authorized to access this page. - -```tsx -import { Button, Result } from 'antd'; -import React from 'react'; - -const App: React.FC = () => ( - Back Home} - /> -); - -export default App; -``` diff --git a/components/result/demo/403.tsx b/components/result/demo/403.tsx new file mode 100644 index 0000000000..a42a680664 --- /dev/null +++ b/components/result/demo/403.tsx @@ -0,0 +1,13 @@ +import React from 'react'; +import { Button, Result } from 'antd'; + +const App: React.FC = () => ( + Back Home} + /> +); + +export default App; diff --git a/components/result/demo/404.md b/components/result/demo/404.md index bd9e835c22..389530ce1a 100644 --- a/components/result/demo/404.md +++ b/components/result/demo/404.md @@ -1,10 +1,3 @@ ---- -order: 5 -title: - zh-CN: 404 - en-US: 404 ---- - ## zh-CN 此页面未找到。 @@ -12,19 +5,3 @@ title: ## en-US The page you visited does not exist. - -```tsx -import { Button, Result } from 'antd'; -import React from 'react'; - -const App: React.FC = () => ( - Back Home} - /> -); - -export default App; -``` diff --git a/components/result/demo/404.tsx b/components/result/demo/404.tsx new file mode 100644 index 0000000000..2a00db7819 --- /dev/null +++ b/components/result/demo/404.tsx @@ -0,0 +1,13 @@ +import React from 'react'; +import { Button, Result } from 'antd'; + +const App: React.FC = () => ( + Back Home} + /> +); + +export default App; diff --git a/components/result/demo/500.md b/components/result/demo/500.md index cf06cce3d0..5360d9f30f 100644 --- a/components/result/demo/500.md +++ b/components/result/demo/500.md @@ -1,10 +1,3 @@ ---- -order: 6 -title: - zh-CN: 500 - en-US: 500 ---- - ## zh-CN 服务器发生了错误。 @@ -12,19 +5,3 @@ title: ## en-US Something went wrong on server. - -```tsx -import { Button, Result } from 'antd'; -import React from 'react'; - -const App: React.FC = () => ( - Back Home} - /> -); - -export default App; -``` diff --git a/components/result/demo/500.tsx b/components/result/demo/500.tsx new file mode 100644 index 0000000000..6724992d0d --- /dev/null +++ b/components/result/demo/500.tsx @@ -0,0 +1,13 @@ +import React from 'react'; +import { Button, Result } from 'antd'; + +const App: React.FC = () => ( + Back Home} + /> +); + +export default App; diff --git a/components/result/demo/customIcon.md b/components/result/demo/customIcon.md index d3f0b1d5a1..4588413903 100644 --- a/components/result/demo/customIcon.md +++ b/components/result/demo/customIcon.md @@ -1,10 +1,3 @@ ---- -order: 8 -title: - zh-CN: 自定义 icon - en-US: Custom icon ---- - ## zh-CN 自定义 icon。 @@ -12,19 +5,3 @@ title: ## en-US Custom icon. - -```tsx -import { SmileOutlined } from '@ant-design/icons'; -import { Button, Result } from 'antd'; -import React from 'react'; - -const App: React.FC = () => ( - } - title="Great, we have done all the operations!" - extra={} - /> -); - -export default App; -``` diff --git a/components/result/demo/customIcon.tsx b/components/result/demo/customIcon.tsx new file mode 100644 index 0000000000..8d6b6d2781 --- /dev/null +++ b/components/result/demo/customIcon.tsx @@ -0,0 +1,13 @@ +import React from 'react'; +import { SmileOutlined } from '@ant-design/icons'; +import { Button, Result } from 'antd'; + +const App: React.FC = () => ( + } + title="Great, we have done all the operations!" + extra={} + /> +); + +export default App; diff --git a/components/result/demo/error.md b/components/result/demo/error.md index 82b1df6039..6b31ff9776 100644 --- a/components/result/demo/error.md +++ b/components/result/demo/error.md @@ -1,10 +1,3 @@ ---- -order: 7 -title: - zh-CN: Error - en-US: Error ---- - ## zh-CN 复杂的错误反馈。 @@ -13,51 +6,6 @@ title: Complex error feedback. -```tsx -import { CloseCircleOutlined } from '@ant-design/icons'; -import { Button, Result, Typography } from 'antd'; -import React from 'react'; - -const { Paragraph, Text } = Typography; - -const App: React.FC = () => ( - - Go Console - , - , - ]} - > -
                  - - - The content you submitted has the following error: - - - - Your account has been - frozen. Thaw immediately > - - - Your account is not yet - eligible to apply. Apply Unlock > - -
                  -
                  -); - -export default App; -``` - ```css .site-result-demo-error-icon { color: red; diff --git a/components/result/demo/error.tsx b/components/result/demo/error.tsx new file mode 100644 index 0000000000..06e4a0f933 --- /dev/null +++ b/components/result/demo/error.tsx @@ -0,0 +1,42 @@ +import React from 'react'; +import { CloseCircleOutlined } from '@ant-design/icons'; +import { Button, Result, Typography } from 'antd'; + +const { Paragraph, Text } = Typography; + +const App: React.FC = () => ( + + Go Console + , + , + ]} + > +
                  + + + The content you submitted has the following error: + + + + Your account has been + frozen. Thaw immediately > + + + Your account is not yet + eligible to apply. Apply Unlock > + +
                  +
                  +); + +export default App; diff --git a/components/result/demo/info.md b/components/result/demo/info.md index 13d2c35a66..a4463f3a6d 100644 --- a/components/result/demo/info.md +++ b/components/result/demo/info.md @@ -1,10 +1,3 @@ ---- -order: 1 -title: - zh-CN: Info - en-US: Info ---- - ## zh-CN 展示处理结果。 @@ -12,21 +5,3 @@ title: ## en-US Show processing results. - -```tsx -import { Button, Result } from 'antd'; -import React from 'react'; - -const App: React.FC = () => ( - - Go Console - - } - /> -); - -export default App; -``` diff --git a/components/result/demo/info.tsx b/components/result/demo/info.tsx new file mode 100644 index 0000000000..1a898e5472 --- /dev/null +++ b/components/result/demo/info.tsx @@ -0,0 +1,15 @@ +import React from 'react'; +import { Button, Result } from 'antd'; + +const App: React.FC = () => ( + + Go Console + + } + /> +); + +export default App; diff --git a/components/result/demo/success.md b/components/result/demo/success.md index c55766518a..4166c0a046 100644 --- a/components/result/demo/success.md +++ b/components/result/demo/success.md @@ -1,10 +1,3 @@ ---- -order: 0 -title: - zh-CN: Success - en-US: Success ---- - ## zh-CN 成功的结果。 @@ -12,24 +5,3 @@ title: ## en-US Show successful results. - -```tsx -import { Button, Result } from 'antd'; -import React from 'react'; - -const App: React.FC = () => ( - - Go Console - , - , - ]} - /> -); - -export default App; -``` diff --git a/components/result/demo/success.tsx b/components/result/demo/success.tsx new file mode 100644 index 0000000000..6cbb03468a --- /dev/null +++ b/components/result/demo/success.tsx @@ -0,0 +1,18 @@ +import React from 'react'; +import { Button, Result } from 'antd'; + +const App: React.FC = () => ( + + Go Console + , + , + ]} + /> +); + +export default App; diff --git a/components/result/demo/warning.md b/components/result/demo/warning.md index 9cf231a2e9..52f949efeb 100644 --- a/components/result/demo/warning.md +++ b/components/result/demo/warning.md @@ -1,10 +1,3 @@ ---- -order: 2 -title: - zh-CN: Warning - en-US: Warning ---- - ## zh-CN 警告类型的结果。 @@ -12,22 +5,3 @@ title: ## en-US The result of the warning. - -```tsx -import { Button, Result } from 'antd'; -import React from 'react'; - -const App: React.FC = () => ( - - Go Console - - } - /> -); - -export default App; -``` diff --git a/components/result/demo/warning.tsx b/components/result/demo/warning.tsx new file mode 100644 index 0000000000..3e85dfa170 --- /dev/null +++ b/components/result/demo/warning.tsx @@ -0,0 +1,16 @@ +import React from 'react'; +import { Button, Result } from 'antd'; + +const App: React.FC = () => ( + + Go Console + + } + /> +); + +export default App; diff --git a/components/result/index.en-US.md b/components/result/index.en-US.md index 6bc28aa93d..0158217d88 100644 --- a/components/result/index.en-US.md +++ b/components/result/index.en-US.md @@ -1,8 +1,7 @@ --- -type: Feedback +group: Feedback category: Components title: Result -cols: 1 cover: https://gw.alipayobjects.com/zos/alicdn/9nepwjaLa/Result.svg --- @@ -12,6 +11,18 @@ Used to feed back the results of a series of operational tasks. Use when important operations need to inform the user to process the results and the feedback is more complicated. +## Examples + + +Success +Info +Warning +403 +404 +500 +Error +Custom icon + ## API | Property | Description | Type | Default | diff --git a/components/result/index.tsx b/components/result/index.tsx index fd42c07a01..ab7ce20828 100644 --- a/components/result/index.tsx +++ b/components/result/index.tsx @@ -12,6 +12,8 @@ import noFound from './noFound'; import serverError from './serverError'; import unauthorized from './unauthorized'; +import useStyle from './style'; + export const IconMap = { success: CheckCircleFilled, error: CloseCircleFilled, @@ -117,17 +119,26 @@ const Result: ResultType = ({ const { getPrefixCls, direction } = React.useContext(ConfigContext); const prefixCls = getPrefixCls('result', customizePrefixCls); - const className = classNames(prefixCls, `${prefixCls}-${status}`, customizeClassName, { - [`${prefixCls}-rtl`]: direction === 'rtl', - }); - return ( + + // Style + const [wrapSSR, hashId] = useStyle(prefixCls); + + const className = classNames( + prefixCls, + `${prefixCls}-${status}`, + customizeClassName, + { [`${prefixCls}-rtl`]: direction === 'rtl' }, + hashId, + ); + + return wrapSSR(
                  {title}
                  {subTitle &&
                  {subTitle}
                  } {children &&
                  {children}
                  } -
                  +
                  , ); }; diff --git a/components/result/index.zh-CN.md b/components/result/index.zh-CN.md index 9c8c7cdc70..4702086f37 100644 --- a/components/result/index.zh-CN.md +++ b/components/result/index.zh-CN.md @@ -1,8 +1,7 @@ --- -type: 反馈 +group: 反馈 category: Components title: Result -cols: 1 subtitle: 结果 cover: https://gw.alipayobjects.com/zos/alicdn/9nepwjaLa/Result.svg --- @@ -13,6 +12,18 @@ cover: https://gw.alipayobjects.com/zos/alicdn/9nepwjaLa/Result.svg 当有重要操作需告知用户处理结果,且反馈内容较为复杂时使用。 +## 代码演示 + + +Success +Info +Warning +403 +404 +500 +Error +自定义 icon + ## API | 参数 | 说明 | 类型 | 默认值 | diff --git a/components/result/style/index.less b/components/result/style/index.less deleted file mode 100644 index 78cb70b827..0000000000 --- a/components/result/style/index.less +++ /dev/null @@ -1,75 +0,0 @@ -@import '../../style/themes/index'; -@import '../../style/mixins/index'; - -@result-prefix-cls: ~'@{ant-prefix}-result'; - -.@{result-prefix-cls} { - padding: 48px 32px; - // status color - &-success &-icon > .@{iconfont-css-prefix} { - color: @success-color; - } - - &-error &-icon > .@{iconfont-css-prefix} { - color: @error-color; - } - - &-info &-icon > .@{iconfont-css-prefix} { - color: @info-color; - } - - &-warning &-icon > .@{iconfont-css-prefix} { - color: @warning-color; - } - - // Exception Status image - &-image { - width: 250px; - height: 295px; - margin: auto; - } - - &-icon { - margin-bottom: 24px; - text-align: center; - - > .@{iconfont-css-prefix} { - font-size: @result-icon-font-size; - } - } - - &-title { - color: @heading-color; - font-size: @result-title-font-size; - line-height: 1.8; - text-align: center; - } - - &-subtitle { - color: @text-color-secondary; - font-size: @result-subtitle-font-size; - line-height: 1.6; - text-align: center; - } - - &-extra { - margin: @result-extra-margin; - text-align: center; - - > * { - margin-right: 8px; - - &:last-child { - margin-right: 0; - } - } - } - - &-content { - margin-top: 24px; - padding: 24px 40px; - background-color: @background-color-light; - } -} - -@import './rtl'; diff --git a/components/result/style/index.tsx b/components/result/style/index.tsx index 3a3ab0de59..8914f44d5d 100644 --- a/components/result/style/index.tsx +++ b/components/result/style/index.tsx @@ -1,2 +1,157 @@ -import '../../style/index.less'; -import './index.less'; +import type { CSSObject } from '@ant-design/cssinjs'; +import type { FullToken, GenerateStyle } from '../../theme'; +import { genComponentStyleHook, mergeToken } from '../../theme'; + +export interface ComponentToken { + imageWidth: number; + imageHeight: number; +} + +interface ResultToken extends FullToken<'Result'> { + resultTitleFontSize: number; + resultSubtitleFontSize: number; + resultIconFontSize: number; + + resultExtraMargin: string; + + resultInfoIconColor: string; + resultSuccessIconColor: string; + resultWarningIconColor: string; + resultErrorIconColor: string; +} + +// ============================== Styles ============================== +const genBaseStyle: GenerateStyle = (token): CSSObject => { + const { + componentCls, + lineHeightHeading3, + iconCls, + padding, + paddingXL, + paddingXS, + paddingLG, + marginXS, + lineHeight, + } = token; + + return { + // Result + [componentCls]: { + padding: `${paddingLG * 2}px ${paddingXL}px`, + + // RTL + '&-rtl': { + direction: 'rtl', + }, + }, + + // Exception Status image + [`${componentCls} ${componentCls}-image`]: { + width: token.imageWidth, + height: token.imageHeight, + margin: 'auto', + }, + + [`${componentCls} ${componentCls}-icon`]: { + marginBottom: paddingLG, + textAlign: 'center', + + [`& > ${iconCls}`]: { + fontSize: token.resultIconFontSize, + }, + }, + + [`${componentCls} ${componentCls}-title`]: { + color: token.colorTextHeading, + fontSize: token.resultTitleFontSize, + lineHeight: lineHeightHeading3, + marginBlock: marginXS, + textAlign: 'center', + }, + + [`${componentCls} ${componentCls}-subtitle`]: { + color: token.colorTextDescription, + fontSize: token.resultSubtitleFontSize, + lineHeight, + textAlign: 'center', + }, + + [`${componentCls} ${componentCls}-content`]: { + marginTop: paddingLG, + padding: `${paddingLG}px ${padding * 2.5}px`, + backgroundColor: token.colorFillAlter, + }, + + [`${componentCls} ${componentCls}-extra`]: { + margin: token.resultExtraMargin, + textAlign: 'center', + + '& > *': { + marginInlineEnd: paddingXS, + + '&:last-child': { + marginInlineEnd: 0, + }, + }, + }, + }; +}; + +const genStatusIconStyle: GenerateStyle = token => { + const { componentCls, iconCls } = token; + + return { + [`${componentCls}-success ${componentCls}-icon > ${iconCls}`]: { + color: token.resultSuccessIconColor, + }, + [`${componentCls}-error ${componentCls}-icon > ${iconCls}`]: { + color: token.resultErrorIconColor, + }, + [`${componentCls}-info ${componentCls}-icon > ${iconCls}`]: { + color: token.resultInfoIconColor, + }, + [`${componentCls}-warning ${componentCls}-icon > ${iconCls}`]: { + color: token.resultWarningIconColor, + }, + }; +}; + +const genResultStyle: GenerateStyle = token => [ + genBaseStyle(token), + genStatusIconStyle(token), +]; + +// ============================== Export ============================== +const getStyle: GenerateStyle = token => genResultStyle(token); + +export default genComponentStyleHook( + 'Result', + token => { + const { paddingLG, fontSizeHeading3 } = token; + + const resultSubtitleFontSize = token.fontSize; + const resultExtraMargin = `${paddingLG}px 0 0 0`; + + const resultInfoIconColor = token.colorInfo; + const resultErrorIconColor = token.colorError; + const resultSuccessIconColor = token.colorSuccess; + const resultWarningIconColor = token.colorWarning; + + const resultToken = mergeToken(token, { + resultTitleFontSize: fontSizeHeading3, + resultSubtitleFontSize, + resultIconFontSize: fontSizeHeading3 * 3, + resultExtraMargin, + resultInfoIconColor, + resultErrorIconColor, + resultSuccessIconColor, + resultWarningIconColor, + }); + + return [getStyle(resultToken)]; + }, + { + imageWidth: 250, + imageHeight: 295, + }, +); diff --git a/components/result/style/rtl.less b/components/result/style/rtl.less deleted file mode 100644 index a2eea06d28..0000000000 --- a/components/result/style/rtl.less +++ /dev/null @@ -1,25 +0,0 @@ -@import '../../style/themes/index'; -@import '../../style/mixins/index'; - -@result-prefix-cls: ~'@{ant-prefix}-result'; - -.@{result-prefix-cls} { - &-rtl { - direction: rtl; - } - - &-extra { - > * { - .@{result-prefix-cls}-rtl & { - margin-right: 0; - margin-left: 8px; - } - - &:last-child { - .@{result-prefix-cls}-rtl & { - margin-left: 0; - } - } - } - } -} diff --git a/components/row/index.tsx b/components/row/index.tsx index fd66497f40..81cf63c1f2 100644 --- a/components/row/index.tsx +++ b/components/row/index.tsx @@ -1,4 +1,4 @@ -import { Row, RowProps } from '../grid'; +import { Row, type RowProps } from '../grid'; -export { RowProps }; +export type { RowProps }; export default Row; diff --git a/components/row/style/index.tsx b/components/row/style/index.tsx index 2127e6804d..a491223046 100644 --- a/components/row/style/index.tsx +++ b/components/row/style/index.tsx @@ -1,5 +1,4 @@ -import '../../style/index.less'; +// Compatible for babel-plugin-import -// style dependencies -// deps-lint-skip: grid -import '../../grid/style'; +/* istanbul ignore next */ +export default {}; diff --git a/components/segmented/__tests__/__snapshots__/demo-extend.test.ts.snap b/components/segmented/__tests__/__snapshots__/demo-extend.test.ts.snap index 090797e9e6..5b2a3492f8 100644 --- a/components/segmented/__tests__/__snapshots__/demo-extend.test.ts.snap +++ b/components/segmented/__tests__/__snapshots__/demo-extend.test.ts.snap @@ -1,6 +1,6 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP -exports[`renders ./components/segmented/demo/basic.md extend context correctly 1`] = ` +exports[`renders ./components/segmented/demo/basic.tsx extend context correctly 1`] = `
                  @@ -82,7 +82,7 @@ exports[`renders ./components/segmented/demo/basic.md extend context correctly 1
                  `; -exports[`renders ./components/segmented/demo/block.md extend context correctly 1`] = ` +exports[`renders ./components/segmented/demo/block.tsx extend context correctly 1`] = `
                  @@ -136,7 +136,7 @@ exports[`renders ./components/segmented/demo/block.md extend context correctly 1
                  `; -exports[`renders ./components/segmented/demo/controlled.md extend context correctly 1`] = ` +exports[`renders ./components/segmented/demo/controlled.tsx extend context correctly 1`] = `
                  @@ -190,7 +190,7 @@ exports[`renders ./components/segmented/demo/controlled.md extend context correc
                  `; -exports[`renders ./components/segmented/demo/controlled-two.md extend context correctly 1`] = ` +exports[`renders ./components/segmented/demo/controlled-two.tsx extend context correctly 1`] = ` Array [
                  @@ -797,7 +797,7 @@ exports[`renders ./components/segmented/demo/icon-only.md extend context correct
                  `; -exports[`renders ./components/segmented/demo/size.md extend context correctly 1`] = ` +exports[`renders ./components/segmented/demo/size.tsx extend context correctly 1`] = ` Array [
                  diff --git a/components/segmented/__tests__/__snapshots__/demo.test.ts.snap b/components/segmented/__tests__/__snapshots__/demo.test.ts.snap index c86e319a1f..67fed0f8b7 100644 --- a/components/segmented/__tests__/__snapshots__/demo.test.ts.snap +++ b/components/segmented/__tests__/__snapshots__/demo.test.ts.snap @@ -1,6 +1,6 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP -exports[`renders ./components/segmented/demo/basic.md correctly 1`] = ` +exports[`renders ./components/segmented/demo/basic.tsx correctly 1`] = `
                  @@ -82,7 +82,7 @@ exports[`renders ./components/segmented/demo/basic.md correctly 1`] = `
                  `; -exports[`renders ./components/segmented/demo/block.md correctly 1`] = ` +exports[`renders ./components/segmented/demo/block.tsx correctly 1`] = `
                  @@ -136,7 +136,7 @@ exports[`renders ./components/segmented/demo/block.md correctly 1`] = `
                  `; -exports[`renders ./components/segmented/demo/controlled.md correctly 1`] = ` +exports[`renders ./components/segmented/demo/controlled.tsx correctly 1`] = `
                  @@ -190,7 +190,7 @@ exports[`renders ./components/segmented/demo/controlled.md correctly 1`] = `
                  `; -exports[`renders ./components/segmented/demo/controlled-two.md correctly 1`] = ` +exports[`renders ./components/segmented/demo/controlled-two.tsx correctly 1`] = ` Array [
                  @@ -797,7 +797,7 @@ exports[`renders ./components/segmented/demo/icon-only.md correctly 1`] = `
                  `; -exports[`renders ./components/segmented/demo/size.md correctly 1`] = ` +exports[`renders ./components/segmented/demo/size.tsx correctly 1`] = ` Array [
                  diff --git a/components/segmented/demo/basic.md b/components/segmented/demo/basic.md index 3bae8b1f66..b617aa1f8f 100644 --- a/components/segmented/demo/basic.md +++ b/components/segmented/demo/basic.md @@ -1,10 +1,3 @@ ---- -order: 0 -title: - zh-CN: 基本 - en-US: Basic ---- - ## zh-CN 最简单的用法。 @@ -13,12 +6,6 @@ title: The most basic usage. -```jsx -import { Segmented } from 'antd'; - -export default () => ; -``` - ```css .code-box-demo { overflow-x: auto; diff --git a/components/segmented/demo/basic.tsx b/components/segmented/demo/basic.tsx new file mode 100644 index 0000000000..c9ab040ffd --- /dev/null +++ b/components/segmented/demo/basic.tsx @@ -0,0 +1,4 @@ +import React from 'react';import { Segmented } from 'antd'; + +export default () => ; + diff --git a/components/segmented/demo/block.md b/components/segmented/demo/block.md index 16335a173a..19287edd64 100644 --- a/components/segmented/demo/block.md +++ b/components/segmented/demo/block.md @@ -1,10 +1,3 @@ ---- -order: 1 -title: - zh-CN: Block 分段选择器 - en-US: Block Segmented ---- - ## zh-CN `block` 属性使其适合父元素宽度。 @@ -12,11 +5,3 @@ title: ## en-US `block` property will make the `Segmented` fit to its parent width. - -```jsx -import { Segmented } from 'antd'; - -export default () => ( - -); -``` diff --git a/components/segmented/demo/block.tsx b/components/segmented/demo/block.tsx new file mode 100644 index 0000000000..02a45a4f70 --- /dev/null +++ b/components/segmented/demo/block.tsx @@ -0,0 +1,6 @@ +import React from 'react';import { Segmented } from 'antd'; + +export default () => ( + +); + diff --git a/components/segmented/demo/controlled-two.md b/components/segmented/demo/controlled-two.md index bd476ce9f2..db6484ab02 100644 --- a/components/segmented/demo/controlled-two.md +++ b/components/segmented/demo/controlled-two.md @@ -1,11 +1,3 @@ ---- -order: 99 -title: - zh-CN: 受控同步模式 - en-US: Controlled Synced mode -debug: true ---- - ## zh-CN 测试受控模式下两个 Segmented 同步 state。 @@ -13,21 +5,3 @@ debug: true ## en-US Tests two Segmented synchronized states in controlled mode. - -```jsx -import { useState } from 'react'; -import { Segmented } from 'antd'; - -const Demo = () => { - const [foo, setFoo] = useState('AND'); - return ( - <> - -    - setFoo(value)} /> - - ); -}; - -export default Demo; -``` diff --git a/components/segmented/demo/controlled-two.tsx b/components/segmented/demo/controlled-two.tsx new file mode 100644 index 0000000000..2462246469 --- /dev/null +++ b/components/segmented/demo/controlled-two.tsx @@ -0,0 +1,15 @@ +import React, { useState } from 'react'; +import { Segmented } from 'antd'; + +const Demo = () => { + const [foo, setFoo] = useState('AND'); + return ( + <> + +    + setFoo(value)} /> + + ); +}; + +export default Demo; diff --git a/components/segmented/demo/controlled.md b/components/segmented/demo/controlled.md index 0f051d366e..daee4022c2 100644 --- a/components/segmented/demo/controlled.md +++ b/components/segmented/demo/controlled.md @@ -1,10 +1,3 @@ ---- -order: 3 -title: - zh-CN: 受控模式 - en-US: Controlled mode ---- - ## zh-CN 受控的 Segmented。 @@ -12,16 +5,3 @@ title: ## en-US Controlled Segmented. - -```tsx -import React, { useState } from 'react'; -import { Segmented } from 'antd'; - -const Demo: React.FC = () => { - const [value, setValue] = useState('Map'); - - return ; -}; - -export default Demo; -``` diff --git a/components/segmented/demo/controlled.tsx b/components/segmented/demo/controlled.tsx new file mode 100644 index 0000000000..1f21170ac7 --- /dev/null +++ b/components/segmented/demo/controlled.tsx @@ -0,0 +1,10 @@ +import React, { useState } from 'react'; +import { Segmented } from 'antd'; + +const Demo: React.FC = () => { + const [value, setValue] = useState('Map'); + + return ; +}; + +export default Demo; diff --git a/components/segmented/demo/custom.md b/components/segmented/demo/custom.md index 60d0702126..28e2645e97 100644 --- a/components/segmented/demo/custom.md +++ b/components/segmented/demo/custom.md @@ -1,10 +1,3 @@ ---- -order: 4 -title: - zh-CN: 自定义渲染 - en-US: Custom Render ---- - ## zh-CN 使用 ReactNode 自定义渲染每一个 Segmented Item。 @@ -12,85 +5,3 @@ title: ## en-US Custom each Segmented Item by ReactNode. - -```jsx -import { Avatar, Segmented } from 'antd'; -import { UserOutlined } from '@ant-design/icons'; - -export default () => ( - <> - - -
                  User 1
                  -
                  - ), - value: 'user1', - }, - { - label: ( -
                  - K -
                  User 2
                  -
                  - ), - value: 'user2', - }, - { - label: ( -
                  - } /> -
                  User 3
                  -
                  - ), - value: 'user3', - }, - ]} - /> -
                  - -
                  Spring
                  -
                  Jan-Mar
                  -
                  - ), - value: 'spring', - }, - { - label: ( -
                  -
                  Summer
                  -
                  Apr-Jun
                  -
                  - ), - value: 'summer', - }, - { - label: ( -
                  -
                  Autumn
                  -
                  Jul-Sept
                  -
                  - ), - value: 'autumn', - }, - { - label: ( -
                  -
                  Winter
                  -
                  Oct-Dec
                  -
                  - ), - value: 'winter', - }, - ]} - /> - -); -``` diff --git a/components/segmented/demo/custom.tsx b/components/segmented/demo/custom.tsx new file mode 100644 index 0000000000..36fa753fb4 --- /dev/null +++ b/components/segmented/demo/custom.tsx @@ -0,0 +1,80 @@ +import React from 'react';import { Avatar, Segmented } from 'antd'; +import { UserOutlined } from '@ant-design/icons'; + +export default () => ( + <> + + +
                  User 1
                  +
                  + ), + value: 'user1', + }, + { + label: ( +
                  + K +
                  User 2
                  +
                  + ), + value: 'user2', + }, + { + label: ( +
                  + } /> +
                  User 3
                  +
                  + ), + value: 'user3', + }, + ]} + /> +
                  + +
                  Spring
                  +
                  Jan-Mar
                  +
                  + ), + value: 'spring', + }, + { + label: ( +
                  +
                  Summer
                  +
                  Apr-Jun
                  +
                  + ), + value: 'summer', + }, + { + label: ( +
                  +
                  Autumn
                  +
                  Jul-Sept
                  +
                  + ), + value: 'autumn', + }, + { + label: ( +
                  +
                  Winter
                  +
                  Oct-Dec
                  +
                  + ), + value: 'winter', + }, + ]} + /> + +); + diff --git a/components/segmented/demo/disabled.md b/components/segmented/demo/disabled.md index cc0875bd4e..38761ec38b 100644 --- a/components/segmented/demo/disabled.md +++ b/components/segmented/demo/disabled.md @@ -1,10 +1,3 @@ ---- -order: 2 -title: - zh-CN: 不可用 - en-US: Basic ---- - ## zh-CN Segmented 不可用。 @@ -12,23 +5,3 @@ Segmented 不可用。 ## en-US Disabled Segmented. - -```jsx -import { Segmented } from 'antd'; - -export default () => ( - <> - -
                  - - -); -``` diff --git a/components/segmented/demo/disabled.tsx b/components/segmented/demo/disabled.tsx new file mode 100644 index 0000000000..bd3932435b --- /dev/null +++ b/components/segmented/demo/disabled.tsx @@ -0,0 +1,18 @@ +import React from 'react';import { Segmented } from 'antd'; + +export default () => ( + <> + +
                  + + +); + diff --git a/components/segmented/demo/dynamic.md b/components/segmented/demo/dynamic.md index f03475e036..d44423fcd3 100644 --- a/components/segmented/demo/dynamic.md +++ b/components/segmented/demo/dynamic.md @@ -1,10 +1,3 @@ ---- -order: 5 -title: - zh-CN: 动态数据 - en-US: Dynamic ---- - ## zh-CN 动态加载数据。 @@ -12,32 +5,3 @@ title: ## en-US Load `options` dynamically. - -```tsx -import React, { useState } from 'react'; -import { Segmented, Button } from 'antd'; - -const defaultOptions = ['Daily', 'Weekly', 'Monthly']; - -const Demo: React.FC = () => { - const [options, setOptions] = useState(defaultOptions); - const [moreLoaded, setMoreLoaded] = useState(false); - - const handleLoadOptions = () => { - setOptions([...defaultOptions, 'Quarterly', 'Yearly']); - setMoreLoaded(true); - }; - - return ( - <> - -
                  - - - ); -}; - -export default Demo; -``` diff --git a/components/segmented/demo/dynamic.tsx b/components/segmented/demo/dynamic.tsx new file mode 100644 index 0000000000..f1d02a04e8 --- /dev/null +++ b/components/segmented/demo/dynamic.tsx @@ -0,0 +1,26 @@ +import React, { useState } from 'react'; +import { Segmented, Button } from 'antd'; + +const defaultOptions = ['Daily', 'Weekly', 'Monthly']; + +const Demo: React.FC = () => { + const [options, setOptions] = useState(defaultOptions); + const [moreLoaded, setMoreLoaded] = useState(false); + + const handleLoadOptions = () => { + setOptions([...defaultOptions, 'Quarterly', 'Yearly']); + setMoreLoaded(true); + }; + + return ( + <> + +
                  + + + ); +}; + +export default Demo; diff --git a/components/segmented/demo/icon-only.md b/components/segmented/demo/icon-only.md index 62cd10e0c9..7620fb49e3 100644 --- a/components/segmented/demo/icon-only.md +++ b/components/segmented/demo/icon-only.md @@ -1,10 +1,3 @@ ---- -order: 8 -title: - zh-CN: 只设置图标 - en-US: With Icon only ---- - ## zh-CN 在 Segmented Item 选项中只设置 Icon。 @@ -12,23 +5,3 @@ title: ## en-US Set `icon` without `label` for Segmented Item. - -```jsx -import { Segmented } from 'antd'; -import { AppstoreOutlined, BarsOutlined } from '@ant-design/icons'; - -export default () => ( - , - }, - { - value: 'Kanban', - icon: , - }, - ]} - /> -); -``` diff --git a/components/segmented/demo/icon-only.tsx b/components/segmented/demo/icon-only.tsx new file mode 100644 index 0000000000..4f54629eb8 --- /dev/null +++ b/components/segmented/demo/icon-only.tsx @@ -0,0 +1,18 @@ +import React from 'react';import { Segmented } from 'antd'; +import { AppstoreOutlined, BarsOutlined } from '@ant-design/icons'; + +export default () => ( + , + }, + { + value: 'Kanban', + icon: , + }, + ]} + /> +); + diff --git a/components/segmented/demo/size-consistent.md b/components/segmented/demo/size-consistent.md index cd409b3cb1..e33729c04b 100644 --- a/components/segmented/demo/size-consistent.md +++ b/components/segmented/demo/size-consistent.md @@ -1,11 +1,3 @@ ---- -order: 99 -title: - zh-CN: 统一高度 - en-US: Consistent height -debug: true ---- - ## zh-CN 与其他组件保持统一高度。 @@ -13,28 +5,3 @@ debug: true ## en-US Keep consistent height with other components. - -```jsx -import { Button, Input, Select, Segmented } from 'antd'; - -export default () => ( - <> -
                  - - -
                  -
                  - - -
                  -
                  - - -
                  - -); -``` diff --git a/components/segmented/demo/size-consistent.tsx b/components/segmented/demo/size-consistent.tsx new file mode 100644 index 0000000000..1988fb2253 --- /dev/null +++ b/components/segmented/demo/size-consistent.tsx @@ -0,0 +1,23 @@ +import React from 'react';import { Button, Input, Select, Segmented } from 'antd'; + +export default () => ( + <> +
                  + + +
                  +
                  + + +
                  +
                  + + +
                  + +); + diff --git a/components/segmented/demo/size.md b/components/segmented/demo/size.md index d9fa0cb760..6d0f5c34a8 100644 --- a/components/segmented/demo/size.md +++ b/components/segmented/demo/size.md @@ -1,10 +1,3 @@ ---- -order: 6 -title: - zh-CN: 三种大小 - en-US: Three sizes of Segmented ---- - ## zh-CN 我们为 `` 组件定义了三种尺寸(大、默认、小),高度分别为 `40px`、`32px` 和 `24px`。 @@ -12,17 +5,3 @@ title: ## en-US There are three sizes of an Segmented: `large` (40px), `default` (32px) and `small` (24px). - -```jsx -import { Segmented } from 'antd'; - -export default () => ( - <> - -
                  - -
                  - - -); -``` diff --git a/components/segmented/demo/size.tsx b/components/segmented/demo/size.tsx new file mode 100644 index 0000000000..60b47f8f1f --- /dev/null +++ b/components/segmented/demo/size.tsx @@ -0,0 +1,12 @@ +import React from 'react';import { Segmented } from 'antd'; + +export default () => ( + <> + +
                  + +
                  + + +); + diff --git a/components/segmented/demo/with-icon.md b/components/segmented/demo/with-icon.md index 20214c7e22..17497a3f0c 100644 --- a/components/segmented/demo/with-icon.md +++ b/components/segmented/demo/with-icon.md @@ -1,10 +1,3 @@ ---- -order: 7 -title: - zh-CN: 设置图标 - en-US: With Icon ---- - ## zh-CN 给 Segmented Item 设置 Icon。 @@ -12,25 +5,3 @@ title: ## en-US Set `icon` for Segmented Item. - -```jsx -import { Segmented } from 'antd'; -import { AppstoreOutlined, BarsOutlined } from '@ant-design/icons'; - -export default () => ( - , - }, - { - label: 'Kanban', - value: 'Kanban', - icon: , - }, - ]} - /> -); -``` diff --git a/components/segmented/demo/with-icon.tsx b/components/segmented/demo/with-icon.tsx new file mode 100644 index 0000000000..7402335a66 --- /dev/null +++ b/components/segmented/demo/with-icon.tsx @@ -0,0 +1,20 @@ +import React from 'react';import { Segmented } from 'antd'; +import { AppstoreOutlined, BarsOutlined } from '@ant-design/icons'; + +export default () => ( + , + }, + { + label: 'Kanban', + value: 'Kanban', + icon: , + }, + ]} + /> +); + diff --git a/components/segmented/index.en-US.md b/components/segmented/index.en-US.md index d9bb97bfc3..33ad671533 100644 --- a/components/segmented/index.en-US.md +++ b/components/segmented/index.en-US.md @@ -1,8 +1,10 @@ --- category: Components -type: Data Display +group: Data Display title: Segmented cover: https://gw.alipayobjects.com/zos/bmw-prod/a3ff040f-24ba-43e0-92e9-c845df1612ad.svg +demo: + cols: 2 --- Segmented Controls. This component is available since `antd@4.20.0`. @@ -12,6 +14,21 @@ Segmented Controls. This component is available since `antd@4.20.0`. - When displaying multiple options and user can select a single option; - When switching the selected option, the content of the associated area changes. +## Examples + + +Basic +Block Segmented +Basic +Controlled mode +Custom Render +Dynamic +Three sizes of Segmented +With Icon +With Icon only +Controlled Synced mode +Consistent height + ## API > This component is available since `antd@4.20.0` diff --git a/components/segmented/index.tsx b/components/segmented/index.tsx index 0c54910acf..4fdaaacf74 100644 --- a/components/segmented/index.tsx +++ b/components/segmented/index.tsx @@ -11,6 +11,8 @@ import { ConfigContext } from '../config-provider'; import type { SizeType } from '../config-provider/SizeContext'; import SizeContext from '../config-provider/SizeContext'; +import useStyle from './style'; + export type { SegmentedValue } from 'rc-segmented'; interface SegmentedLabeledOptionWithoutIcon extends RcSegmentedLabeledOption { @@ -53,6 +55,8 @@ const Segmented = React.forwardRef((props, ref) const { getPrefixCls, direction } = React.useContext(ConfigContext); const prefixCls = getPrefixCls('segmented', customizePrefixCls); + // Style + const [wrapSSR, hashId] = useStyle(prefixCls); // ===================== Size ===================== const size = React.useContext(SizeContext); @@ -79,19 +83,23 @@ const Segmented = React.forwardRef((props, ref) [options, prefixCls], ); - return ( + return wrapSSR( + />, ); }); diff --git a/components/segmented/index.zh-CN.md b/components/segmented/index.zh-CN.md index df42c9969d..4de70a6147 100644 --- a/components/segmented/index.zh-CN.md +++ b/components/segmented/index.zh-CN.md @@ -1,9 +1,11 @@ --- category: Components subtitle: 分段控制器 -type: 数据展示 +group: 数据展示 title: Segmented cover: https://gw.alipayobjects.com/zos/bmw-prod/a3ff040f-24ba-43e0-92e9-c845df1612ad.svg +demo: + cols: 2 --- 分段控制器。自 `antd@4.20.0` 版本开始提供该组件。 @@ -13,6 +15,21 @@ cover: https://gw.alipayobjects.com/zos/bmw-prod/a3ff040f-24ba-43e0-92e9-c845df1 - 用于展示多个选项并允许用户选择其中单个选项; - 当切换选中选项时,关联区域的内容会发生变化。 +## 代码演示 + + +基本 +Block 分段选择器 +不可用 +受控模式 +自定义渲染 +动态数据 +三种大小 +设置图标 +只设置图标 +受控同步模式 +统一高度 + ## API > 自 `antd@4.20.0` 版本开始提供该组件。 diff --git a/components/segmented/style/index.less b/components/segmented/style/index.less deleted file mode 100644 index e9fb3bbf66..0000000000 --- a/components/segmented/style/index.less +++ /dev/null @@ -1,122 +0,0 @@ -@import '../../style/themes/index'; -@import '../../style/mixins/index'; -@import './mixins.less'; - -@segmented-prefix-cls: ~'@{ant-prefix}-segmented'; - -@segmented-container-padding: 2px; - -.@{segmented-prefix-cls} { - .reset-component(); - display: inline-block; - padding: @segmented-container-padding; - color: @segmented-label-color; - background-color: @segmented-bg; - border-radius: @border-radius-base; - transition: all 0.3s @ease-in-out; - - &-group { - position: relative; - display: flex; - align-items: stretch; - justify-items: flex-start; - width: 100%; - } - - // block styles - &&-block { - display: flex; - } - - &&-block &-item { - flex: 1; - min-width: 0; - } - - // hover/focus styles - &:not(&-disabled) { - &:hover, - &:focus { - background-color: @segmented-hover-bg; - } - } - - // item styles - &-item { - position: relative; - text-align: center; - cursor: pointer; - transition: color 0.3s @ease-in-out; - - &-selected { - .segmented-item-selected(); - color: @segmented-label-hover-color; - } - - &:hover, - &:focus { - color: @segmented-label-hover-color; - } - - &-label { - min-height: @input-height-base - @segmented-container-padding * 2; - padding: 0 @input-padding-horizontal-base; - line-height: @input-height-base - @segmented-container-padding * 2; - .segmented-text-ellipsis(); - } - - // syntactic sugar to add `icon` for Segmented Item - &-icon + * { - margin-left: (@margin-sm / 2); - } - - &-input { - position: absolute; - top: 0; - left: 0; - width: 0; - height: 0; - opacity: 0; - pointer-events: none; - } - } - - // size styles - &&-lg &-item-label { - min-height: @input-height-lg - @segmented-container-padding * 2; - padding: 0 @input-padding-horizontal-lg; - font-size: @font-size-lg; - line-height: @input-height-lg - @segmented-container-padding * 2; - } - - &&-sm &-item-label { - min-height: @input-height-sm - @segmented-container-padding * 2; - padding: 0 @input-padding-horizontal-sm; - line-height: @input-height-sm - @segmented-container-padding * 2; - } - - // disabled styles - &-item-disabled { - .segmented-disabled-item(); - } - - // thumb styles - &-thumb { - .segmented-item-selected(); - - position: absolute; - top: 0; - left: 0; - width: 0; - height: 100%; - padding: 4px 0; - } - - // transition effect when `appear-active` - &-thumb-motion-appear-active { - transition: transform 0.3s @ease-in-out, width 0.3s @ease-in-out; - will-change: transform, width; - } -} - -@import './rtl'; diff --git a/components/segmented/style/index.tsx b/components/segmented/style/index.tsx index 3a3ab0de59..8d9760fdca 100644 --- a/components/segmented/style/index.tsx +++ b/components/segmented/style/index.tsx @@ -1,2 +1,215 @@ -import '../../style/index.less'; -import './index.less'; +import type { CSSObject } from '@ant-design/cssinjs'; +import type { FullToken, GenerateStyle } from '../../theme'; +import { genComponentStyleHook, mergeToken } from '../../theme'; +import { resetComponent, textEllipsis } from '../../style'; + +export interface ComponentToken {} + +interface SegmentedToken extends FullToken<'Segmented'> { + segmentedPaddingHorizontal: number; + segmentedPaddingHorizontalSM: number; + segmentedContainerPadding: number; + labelColor: string; + labelColorHover: string; + bgColor: string; + bgColorHover: string; + bgColorSelected: string; +} + +// ============================== Mixins ============================== +function segmentedDisabledItem(cls: string, token: SegmentedToken): CSSObject { + return { + [`${cls}, ${cls}:hover, ${cls}:focus`]: { + color: token.colorTextDisabled, + cursor: 'not-allowed', + }, + }; +} + +function getSegmentedItemSelectedStyle(token: SegmentedToken): CSSObject { + return { + backgroundColor: token.bgColorSelected, + boxShadow: token.boxShadow, + }; +} + +const segmentedTextEllipsisCss: CSSObject = { + overflow: 'hidden', + // handle text ellipsis + ...textEllipsis, +}; + +// ============================== Styles ============================== +const genSegmentedStyle: GenerateStyle = (token: SegmentedToken) => { + const { componentCls } = token; + + return { + [componentCls]: { + ...resetComponent(token), + + display: 'inline-block', + padding: token.segmentedContainerPadding, + color: token.labelColor, + backgroundColor: token.bgColor, + borderRadius: token.borderRadius, + transition: `all ${token.motionDurationMid} ${token.motionEaseInOut}`, + + [`${componentCls}-group`]: { + position: 'relative', + display: 'flex', + alignItems: 'stretch', + justifyItems: 'flex-start', + width: '100%', + }, + + // RTL styles + '&&-rtl': { + direction: 'rtl', + }, + + // block styles + '&&-block': { + display: 'flex', + }, + + [`&&-block ${componentCls}-item`]: { + flex: 1, + minWidth: 0, + }, + + // item styles + [`${componentCls}-item`]: { + position: 'relative', + textAlign: 'center', + cursor: 'pointer', + transition: `color ${token.motionDurationMid} ${token.motionEaseInOut}`, + borderRadius: token.borderRadiusSM, + + '&-selected': { + ...getSegmentedItemSelectedStyle(token), + color: token.labelColorHover, + }, + + '&::after': { + content: '""', + position: 'absolute', + width: '100%', + height: '100%', + top: 0, + insetInlineStart: 0, + borderRadius: token.borderRadiusSM, + transition: `background-color ${token.motionDurationMid}`, + }, + + [`&:hover:not(${componentCls}-item-selected):not(${componentCls}-item-disabled)`]: { + color: token.labelColorHover, + + '&::after': { + backgroundColor: token.bgColorHover, + }, + }, + + '&-label': { + minHeight: token.controlHeight - token.segmentedContainerPadding * 2, + lineHeight: `${token.controlHeight - token.segmentedContainerPadding * 2}px`, + padding: `0 ${token.segmentedPaddingHorizontal}px`, + ...segmentedTextEllipsisCss, + }, + + // syntactic sugar to add `icon` for Segmented Item + '&-icon + *': { + marginInlineEnd: token.marginSM / 2, + }, + + '&-input': { + position: 'absolute', + insetBlockStart: 0, + insetInlineStart: 0, + width: 0, + height: 0, + opacity: 0, + pointerEvents: 'none', + }, + }, + + // size styles + '&&-lg': { + borderRadius: token.borderRadiusLG, + [`${componentCls}-item-label`]: { + minHeight: token.controlHeightLG - token.segmentedContainerPadding * 2, + lineHeight: `${token.controlHeightLG - token.segmentedContainerPadding * 2}px`, + padding: `0 ${token.segmentedPaddingHorizontal}px`, + fontSize: token.fontSizeLG, + }, + [`${componentCls}-item-selected`]: { + borderRadius: token.borderRadius, + }, + }, + + '&&-sm': { + borderRadius: token.borderRadiusSM, + [`${componentCls}-item-label`]: { + minHeight: token.controlHeightSM - token.segmentedContainerPadding * 2, + lineHeight: `${token.controlHeightSM - token.segmentedContainerPadding * 2}px`, + padding: `0 ${token.segmentedPaddingHorizontalSM}px`, + }, + [`${componentCls}-item-selected`]: { + borderRadius: token.borderRadiusXS, + }, + }, + + // disabled styles + ...segmentedDisabledItem(`&-disabled ${componentCls}-item`, token), + ...segmentedDisabledItem(`${componentCls}-item-disabled`, token), + + // thumb styles + [`${componentCls}-thumb`]: { + ...getSegmentedItemSelectedStyle(token), + + position: 'absolute', + insetBlockStart: 0, + insetInlineStart: 0, + width: 0, + height: '100%', + padding: `${token.paddingXXS}px 0`, + borderRadius: token.borderRadiusSM, + + [`& ~ ${componentCls}-item:not(${componentCls}-item-selected):not(${componentCls}-item-disabled)::after`]: + { + backgroundColor: 'transparent', + }, + }, + + // transition effect when `appear-active` + [`${componentCls}-thumb-motion-appear-active`]: { + transition: `transform ${token.motionDurationSlow} ${token.motionEaseInOut}, width ${token.motionDurationSlow} ${token.motionEaseInOut}`, + willChange: 'transform, width', + }, + }, + }; +}; + +// ============================== Export ============================== +export default genComponentStyleHook('Segmented', (token) => { + const { + lineWidthBold, + lineWidth, + colorTextLabel, + colorText, + colorFillSecondary, + colorBgLayout, + colorBgElevated, + } = token; + + const segmentedToken = mergeToken(token, { + segmentedPaddingHorizontal: token.controlPaddingHorizontal - lineWidth, + segmentedPaddingHorizontalSM: token.controlPaddingHorizontalSM - lineWidth, + segmentedContainerPadding: lineWidthBold, + labelColor: colorTextLabel, + labelColorHover: colorText, + bgColor: colorBgLayout, + bgColorHover: colorFillSecondary, + bgColorSelected: colorBgElevated, + }); + return [genSegmentedStyle(segmentedToken)]; +}); diff --git a/components/segmented/style/mixins.less b/components/segmented/style/mixins.less deleted file mode 100644 index 0c6839af74..0000000000 --- a/components/segmented/style/mixins.less +++ /dev/null @@ -1,24 +0,0 @@ -// mixins -.segmented-disabled-item { - &, - &:hover, - &:focus { - color: @disabled-color; - cursor: not-allowed; - } -} - -.segmented-item-selected { - background-color: @segmented-selected-bg; - border-radius: @border-radius-base; - box-shadow: 0 2px 8px -2px fade(@black, 5%), 0 1px 4px -1px fade(@black, 7%), - 0 0 1px 0 fade(@black, 8%); -} - -.segmented-text-ellipsis { - overflow: hidden; - // handle text ellipsis - white-space: nowrap; - text-overflow: ellipsis; - word-break: keep-all; -} diff --git a/components/segmented/style/rtl.less b/components/segmented/style/rtl.less deleted file mode 100644 index c459bf035e..0000000000 --- a/components/segmented/style/rtl.less +++ /dev/null @@ -1,15 +0,0 @@ -@import '../../style/themes/index'; -@import '../../style/mixins/index'; - -@segmented-prefix-cls: ~'@{ant-prefix}-segmented'; - -.@{segmented-prefix-cls} { - &&-rtl { - direction: rtl; - } - - &&-rtl &-item-icon { - margin-right: 0; - margin-left: 6px; - } -} diff --git a/components/select/__tests__/__snapshots__/demo-extend.test.ts.snap b/components/select/__tests__/__snapshots__/demo-extend.test.ts.snap index cb13709ae7..9d4c652dd0 100644 --- a/components/select/__tests__/__snapshots__/demo-extend.test.ts.snap +++ b/components/select/__tests__/__snapshots__/demo-extend.test.ts.snap @@ -1,6 +1,6 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP -exports[`renders ./components/select/demo/automatic-tokenization.md extend context correctly 1`] = ` +exports[`renders ./components/select/demo/automatic-tokenization.tsx extend context correctly 1`] = `