diff --git a/.dumi/components/SemanticPreview.tsx b/.dumi/components/SemanticPreview.tsx new file mode 100644 index 0000000000..6a60c52fab --- /dev/null +++ b/.dumi/components/SemanticPreview.tsx @@ -0,0 +1,171 @@ +import * as React from 'react'; +import { Col, ConfigProvider, Flex, Row, Tag, theme, Typography } from 'antd'; + +export interface SemanticPreviewProps { + semantics: { name: string; desc: string; version?: string }[]; + children: React.ReactElement; + height?: number; +} + +const SemanticPreview = (props: SemanticPreviewProps) => { + const { semantics = [], children, height } = props; + const { token } = theme.useToken(); + + // ======================= Semantic ======================= + const getMarkClassName = React.useCallback( + (semanticKey: string) => `semantic-mark-${semanticKey}`, + [], + ); + + const semanticClassNames = React.useMemo(() => { + const classNames: Record = {}; + + semantics.forEach((semantic) => { + classNames[semantic.name] = getMarkClassName(semantic.name); + }); + + return classNames; + }, [semantics]); + + const cloneNode = React.cloneElement(children, { + classNames: semanticClassNames, + }); + + // ======================== Hover ========================= + const MARK_BORDER_SIZE = 2; + const containerRef = React.useRef(null); + + const [positionMotion, setPositionMotion] = React.useState(false); + + const [hoverSemantic, setHoverSemantic] = React.useState(null); + const [markPos, setMarkPos] = React.useState< + [left: number, top: number, width: number, height: number] + >([0, 0, 0, 0]); + + React.useEffect(() => { + if (hoverSemantic) { + const targetClassName = getMarkClassName(hoverSemantic); + const targetElement = containerRef.current?.querySelector(`.${targetClassName}`); + + const containerRect = containerRef.current?.getBoundingClientRect(); + const targetRect = targetElement?.getBoundingClientRect(); + + setMarkPos([ + (targetRect?.left || 0) - (containerRect?.left || 0), + (targetRect?.top || 0) - (containerRect?.top || 0), + targetRect?.width || 0, + targetRect?.height || 0, + ]); + + setTimeout(() => { + setPositionMotion(true); + }, 10); + } else { + const timeout = setTimeout(() => { + setPositionMotion(false); + }, 500); + + return () => { + clearTimeout(timeout); + }; + } + }, [hoverSemantic]); + + // ======================== Render ======================== + return ( +
+ + + + {cloneNode} + + + +
    + {semantics.map((semantic, index) => ( +
  • { + setHoverSemantic(semantic.name); + }} + onMouseLeave={() => { + setHoverSemantic(null); + }} + > + + + + {semantic.name} + + {semantic.version && {semantic.version}} + + + {semantic.desc} + {' '} + +
  • + ))} +
+ +
+ +
+
+ ); +}; + +export default SemanticPreview; diff --git a/.dumi/theme/builtins/Previewer/CodePreviewer.tsx b/.dumi/theme/builtins/Previewer/CodePreviewer.tsx index 7ec1d07455..ab30ff4172 100644 --- a/.dumi/theme/builtins/Previewer/CodePreviewer.tsx +++ b/.dumi/theme/builtins/Previewer/CodePreviewer.tsx @@ -5,8 +5,9 @@ import stackblitzSdk from '@stackblitz/sdk'; import { Alert, Badge, Flex, Tooltip } from 'antd'; import { createStyles, css } from 'antd-style'; import classNames from 'classnames'; -import { FormattedMessage, useSiteData, useLiveDemo } from 'dumi'; +import { FormattedMessage, useLiveDemo, useSiteData } from 'dumi'; import LZString from 'lz-string'; + import useLocation from '../../../hooks/useLocation'; import BrowserFrame from '../../common/BrowserFrame'; import ClientOnly from '../../common/ClientOnly'; @@ -99,6 +100,7 @@ const CodePreviewer: React.FC = (props) => { background, filename, version, + simplify, clientOnly, pkgDependencyList, } = props; @@ -177,6 +179,7 @@ const CodePreviewer: React.FC = (props) => { const codeBoxClass = classNames('code-box', { expand: codeExpand, 'code-box-debug': originDebug, + 'code-box-simplify': simplify, }); const localizedTitle = title; @@ -378,139 +381,148 @@ createRoot(document.getElementById('container')).render(); )} -
-
- : ''}> - - {localizedTitle} - - - } filename={filename} /> -
- {description && ( -
- )} - - {showOnlineUrl && ( - }> - - + {!simplify && ( +
+
+ : ''}> + + {localizedTitle} + } + filename={filename} + /> +
+ {description && ( +
)} - {showRiddleButton ? ( + + + {showOnlineUrl && ( + }> + + + + + )} + {showRiddleButton ? ( +
{ + track({ type: 'riddle', demo: asset.id }); + riddleIconRef.current?.submit(); + }} + > + + }> + + +
+ ) : null}
{ - track({ type: 'riddle', demo: asset.id }); - riddleIconRef.current?.submit(); + track({ type: 'codesandbox', demo: asset.id }); + codeSandboxIconRef.current?.submit(); }} > - - }> - + + }> + - ) : null} -
{ - track({ type: 'codesandbox', demo: asset.id }); - codeSandboxIconRef.current?.submit(); - }} - > - - }> - - -
-
{ - track({ type: 'codepen', demo: asset.id }); - codepenIconRef.current?.submit(); - }} - > - - - - }> - - -
- }> - { - track({ type: 'stackblitz', demo: asset.id }); - stackblitzSdk.openProject(stackblitzPrefillConfig, { - openFile: [`demo.${suffix}`], - }); + track({ type: 'codepen', demo: asset.id }); + codepenIconRef.current?.submit(); }} > - - - - }> - + + + }> + + + + }> + { + track({ type: 'stackblitz', demo: asset.id }); + stackblitzSdk.openProject(stackblitzPrefillConfig, { + openFile: [`demo.${suffix}`], + }); + }} + > + + + + }> + + + + + } > - - - - } - > -
- expand code handleCodeExpand(asset.id)} - /> - expand code handleCodeExpand(asset.id)} - /> -
-
-
-
+
+ expand code handleCodeExpand(asset.id)} + /> + expand code handleCodeExpand(asset.id)} + /> +
+
+
+
+ )} {codeExpand && (
{ border-radius: ${token.borderRadiusLG}px; transition: all 0.2s; + &.code-box-simplify { + border-radius: 0; + margin-bottom: 0; + + .code-box-demo { + padding: 0; + border-bottom: 0; + } + } + .code-box-title { &, a { diff --git a/components/__tests__/node.test.tsx b/components/__tests__/node.test.tsx index cc63dd37e4..2a63b03b49 100644 --- a/components/__tests__/node.test.tsx +++ b/components/__tests__/node.test.tsx @@ -1,6 +1,7 @@ -import { globSync } from 'glob'; import * as React from 'react'; +import { globSync } from 'glob'; import { renderToString } from 'react-dom/server'; + import type { Options } from '../../tests/shared/demoTest'; (global as any).testConfig = {}; @@ -28,7 +29,9 @@ describe('node', () => { // Test for ssr describe(componentName, () => { - const demoList = globSync(`./components/${componentName}/demo/*.tsx`); + const demoList = globSync(`./components/${componentName}/demo/*.tsx`).filter( + (file) => !file.includes('_semantic'), + ); // Use mock to get config require(`../../${componentTestFile}`); // eslint-disable-line global-require, import/no-dynamic-require diff --git a/components/badge/demo/_semantic.tsx b/components/badge/demo/_semantic.tsx new file mode 100644 index 0000000000..a6efa13893 --- /dev/null +++ b/components/badge/demo/_semantic.tsx @@ -0,0 +1,43 @@ +import React from 'react'; +import { Avatar, Badge } from 'antd'; + +import SemanticPreview from '../../../.dumi/components/SemanticPreview'; +import useLocale from '../../../.dumi/hooks/useLocale'; + +const locales = { + cn: { + root: '根节点', + indicator: '指示器节点', + }, + en: { + root: 'Root element', + indicator: 'Indicator element', + }, +}; + +const App: React.FC = () => { + const [locale] = useLocale(locales); + + return ( + + + + + + ); +}; + +export default App; diff --git a/components/badge/index.en-US.md b/components/badge/index.en-US.md index 87da9a7c3e..1635d8614c 100644 --- a/components/badge/index.en-US.md +++ b/components/badge/index.en-US.md @@ -44,14 +44,14 @@ Common props ref:[Common props](/docs/react/common-props) | --- | --- | --- | --- | --- | | color | Customize Badge dot color | string | - | | | count | Number to show in badge | ReactNode | - | | -| classNames | Semantic DOM class | Record | - | 5.7.0 | +| classNames | Semantic DOM class | [Record](#semantic-dom) | - | 5.7.0 | | dot | Whether to display a red dot instead of `count` | boolean | false | | | offset | Set offset of the badge dot | \[number, number] | - | | | overflowCount | Max count to show | number | 99 | | | showZero | Whether to show badge when `count` is zero | boolean | false | | | size | If `count` is set, `size` sets the size of badge | `default` \| `small` | - | - | | status | Set Badge as a status dot | `success` \| `processing` \| `default` \| `error` \| `warning` | - | | -| styles | Semantic DOM style | Record | - | 5.7.0 | +| styles | Semantic DOM style | [Record](#semantic-dom) | - | 5.7.0 | | text | If `status` is set, `text` sets the display text of the status `dot` | ReactNode | - | | | title | Text to show when hovering over the badge | string | - | | @@ -63,12 +63,9 @@ Common props ref:[Common props](/docs/react/common-props) | placement | The placement of the Ribbon, `start` and `end` follow text direction (RTL or LTR) | `start` \| `end` | `end` | | | text | Content inside the Ribbon | ReactNode | - | | -### `styles` and `classNames` attribute +## Semantic DOM -| Property | Description | Version | -| --------- | ------------------- | ------- | -| root | set `root` element | 5.7.0 | -| indicator | set `badge` element | 5.7.0 | + ## Design Token diff --git a/components/badge/index.zh-CN.md b/components/badge/index.zh-CN.md index e6c84ba9e8..cc774abf85 100644 --- a/components/badge/index.zh-CN.md +++ b/components/badge/index.zh-CN.md @@ -45,14 +45,14 @@ group: 数据展示 | --- | --- | --- | --- | --- | | color | 自定义小圆点的颜色 | string | - | | | count | 展示的数字,大于 overflowCount 时显示为 `${overflowCount}+`,为 0 时隐藏 | ReactNode | - | | -| classNames | 语义化结构 class | Record | - | 5.7.0 | +| classNames | 语义化结构 class | [Record](#semantic-dom) | - | 5.7.0 | | dot | 不展示数字,只有一个小红点 | boolean | false | | | offset | 设置状态点的位置偏移 | \[number, number] | - | | | overflowCount | 展示封顶的数字值 | number | 99 | | | showZero | 当数值为 0 时,是否展示 Badge | boolean | false | | | size | 在设置了 `count` 的前提下有效,设置小圆点的大小 | `default` \| `small` | - | - | | status | 设置 Badge 为状态点 | `success` \| `processing` \| `default` \| `error` \| `warning` | - | | -| styles | 语义化结构 style | Record | - | 5.7.0 | +| styles | 语义化结构 style | [Record](#semantic-dom) | - | 5.7.0 | | text | 在设置了 `status` 的前提下有效,设置状态点的文本 | ReactNode | - | | | title | 设置鼠标放在状态点上时显示的文字 | string | - | | @@ -64,12 +64,9 @@ group: 数据展示 | placement | 缎带的位置,`start` 和 `end` 随文字方向(RTL 或 LTR)变动 | `start` \| `end` | `end` | | | text | 缎带中填入的内容 | ReactNode | - | | -### `styles` 和 `classNames` 属性 +## Semantic DOM -| 名称 | 说明 | 版本 | -| --------- | ------------ | ----- | -| root | 设置根元素 | 5.7.0 | -| indicator | 设置徽标元素 | 5.7.0 | + ## 主题变量(Design Token) diff --git a/components/button/demo/_semantic.tsx b/components/button/demo/_semantic.tsx new file mode 100644 index 0000000000..e3472b3d49 --- /dev/null +++ b/components/button/demo/_semantic.tsx @@ -0,0 +1,37 @@ +import React from 'react'; +import { AntDesignOutlined } from '@ant-design/icons'; +import { Button } from 'antd'; + +import SemanticPreview from '../../../.dumi/components/SemanticPreview'; +import useLocale from '../../../.dumi/hooks/useLocale'; + +const locales = { + cn: { + icon: '图标元素', + }, + en: { + icon: 'Icon element', + }, +}; + +const App: React.FC = () => { + const [locale] = useLocale(locales); + + return ( + + + + ); +}; + +export default App; diff --git a/components/button/index.en-US.md b/components/button/index.en-US.md index 9a99c459e3..2b098d7621 100644 --- a/components/button/index.en-US.md +++ b/components/button/index.en-US.md @@ -59,7 +59,7 @@ Different button styles can be generated by setting Button properties. The recom | Property | Description | Type | Default | Version | | --- | --- | --- | --- | --- | | block | Option to fit button width to its parent width | boolean | false | | -| classNames | Semantic DOM class | Record | - | 5.4.0 | +| classNames | Semantic DOM class | [Record](#semantic-dom) | - | 5.4.0 | | danger | Set the danger status of button | boolean | false | | | disabled | Disabled state of button | boolean | false | | | ghost | Make background transparent and invert text and border colors | boolean | false | | @@ -69,18 +69,16 @@ Different button styles can be generated by setting Button properties. The recom | loading | Set the loading status of button | boolean \| { delay: number } | false | | | shape | Can be set button shape | `default` \| `circle` \| `round` | `default` | | | size | Set the size of button | `large` \| `middle` \| `small` | `middle` | | -| styles | Semantic DOM style | Record | - | 5.4.0 | +| styles | Semantic DOM style | [Record](#semantic-dom) | - | 5.4.0 | | target | Same as target attribute of a, works when href is specified | string | - | | | type | Set button type | `primary` \| `dashed` \| `link` \| `text` \| `default` | `default` | | | onClick | Set the handler to handle `click` event | (event: MouseEvent) => void | - | | It accepts all props which native buttons support. -### `styles` and `classNames` attribute +## Semantic DOM -| Property | Description | Version | -| -------- | ----------------- | ------- | -| icon | set `icon`element | 5.5.0 | + ## Design Token diff --git a/components/button/index.zh-CN.md b/components/button/index.zh-CN.md index 72ad94df2f..0971b8788f 100644 --- a/components/button/index.zh-CN.md +++ b/components/button/index.zh-CN.md @@ -64,7 +64,7 @@ group: | 属性 | 说明 | 类型 | 默认值 | 版本 | | --- | --- | --- | --- | --- | | block | 将按钮宽度调整为其父宽度的选项 | boolean | false | | -| classNames | 语义化结构 class | Record | - | 5.4.0 | +| classNames | 语义化结构 class | [Record](#semantic-dom) | - | 5.4.0 | | danger | 设置危险按钮 | boolean | false | | | disabled | 设置按钮失效状态 | boolean | false | | | ghost | 幽灵属性,使按钮背景透明 | boolean | false | | @@ -74,18 +74,16 @@ group: | loading | 设置按钮载入状态 | boolean \| { delay: number } | false | | | shape | 设置按钮形状 | `default` \| `circle` \| `round` | `default` | | | size | 设置按钮大小 | `large` \| `middle` \| `small` | `middle` | | -| styles | 语义化结构 style | Record | - | 5.4.0 | +| styles | 语义化结构 style | [Record](#semantic-dom) | - | 5.4.0 | | target | 相当于 a 链接的 target 属性,href 存在时生效 | string | - | | | type | 设置按钮类型 | `primary` \| `dashed` \| `link` \| `text` \| `default` | `default` | | | onClick | 点击按钮时的回调 | (event: MouseEvent) => void | - | | 支持原生 button 的其他所有属性。 -### `styles` 和 `classNames` 属性 +## Semantic DOM -| 名称 | 说明 | 版本 | -| ---- | ------------ | ----- | -| icon | 设置图标元素 | 5.5.0 | + ## 主题变量(Design Token) diff --git a/components/drawer/demo/_semantic.tsx b/components/drawer/demo/_semantic.tsx new file mode 100644 index 0000000000..c89e0b414a --- /dev/null +++ b/components/drawer/demo/_semantic.tsx @@ -0,0 +1,72 @@ +import React from 'react'; +import { Drawer, Typography } from 'antd'; + +import SemanticPreview from '../../../.dumi/components/SemanticPreview'; +import useLocale from '../../../.dumi/hooks/useLocale'; + +const locales = { + cn: { + mask: '遮罩层元素', + content: 'Drawer 容器元素', + header: '头部元素', + body: '内容元素', + footer: '底部元素', + }, + en: { + mask: 'Mask element', + content: 'Drawer container element', + header: 'Header element', + body: 'Body element', + footer: 'Footer element', + }, +}; + +const App: React.FC = () => { + const [locale] = useLocale(locales); + + return ( + + Footer} + closable={false} + open + getContainer={false} + > +

Some contents...

+
+
+ ); +}; + +export default App; diff --git a/components/drawer/index.en-US.md b/components/drawer/index.en-US.md index c9d4b25870..86938a7d6b 100644 --- a/components/drawer/index.en-US.md +++ b/components/drawer/index.en-US.md @@ -51,8 +51,7 @@ v5 use `rootClassName` & `rootStyle` to config wrapper style instead of `classNa | autoFocus | Whether Drawer should get focused after open | boolean | true | 4.17.0 | | afterOpenChange | Callback after the animation ends when switching drawers | function(open) | - | | | className | Config Drawer Panel className. Use `rootClassName` if want to config top dom style | string | - | | -| classNames | Config Drawer build-in module's className | `header?: string; body?: string; footer?: string; mask?: string; content?: string; wrapper?: string;` | - | | -| styles | Config Drawer build-in module's style | `header?: CSSProperties; body?: CSSProperties; footer?: CSSProperties; mask?: CSSProperties; content?: CSSProperties; wrapper?: CSSProperties;` | - | 5.10.0 | +| classNames | Semantic structure className | [Record](#semantic-dom) | - | 5.10.0 | | closeIcon | Custom close icon. 5.7.0: close button will be hidden when setting to `null` or `false` | ReactNode | <CloseOutlined /> | | | destroyOnClose | Whether to unmount child components on closing drawer or not | boolean | false | | | extra | Extra actions area at corner | ReactNode | - | 4.17.0 | @@ -68,6 +67,7 @@ v5 use `rootClassName` & `rootStyle` to config wrapper style instead of `classNa | push | Nested drawers push behavior | boolean \| { distance: string \| number } | { distance: 180 } | 4.5.0+ | | rootStyle | Style of wrapper element which **contains mask** compare to `style` | CSSProperties | - | | | style | Style of Drawer panel. Use `bodyStyle` if want to config body only | CSSProperties | - | | +| styles | Semantic structure style | [Record](#semantic-dom) | - | 5.10.0 | | size | preset size of drawer, default `378px` and large `736px` | 'default' \| 'large' | 'default' | 4.17.0 | | title | The title for Drawer | ReactNode | - | | | open | Whether the Drawer dialog is visible or not | boolean | false | | @@ -75,6 +75,10 @@ v5 use `rootClassName` & `rootStyle` to config wrapper style instead of `classNa | zIndex | The `z-index` of the Drawer | number | 1000 | | | onClose | Specify a callback that will be called when a user clicks mask, close button or Cancel button | function(e) | - | | +## Semantic DOM + + + ## Design Token diff --git a/components/drawer/index.tsx b/components/drawer/index.tsx index 657f628158..d24f81008e 100644 --- a/components/drawer/index.tsx +++ b/components/drawer/index.tsx @@ -157,6 +157,9 @@ const Drawer: React.FC & { const [zIndex, contextZIndex] = useZIndex('Drawer', rest.zIndex); // =========================== Render =========================== + const { classNames: propClassNames = {}, styles: propStyles = {} } = rest; + const { classNames: contextClassNames = {}, styles: contextStyles = {} } = drawer || {}; + return wrapCSSVar( @@ -168,24 +171,24 @@ const Drawer: React.FC & { motion={panelMotion} {...rest} classNames={{ - mask: classNames(rest.classNames?.mask, drawer?.classNames?.mask), - content: classNames(rest.classNames?.content, drawer?.classNames?.content), + mask: classNames(propClassNames.mask, contextClassNames.mask), + content: classNames(propClassNames.content, contextClassNames.content), }} styles={{ mask: { - ...rest.styles?.mask, + ...propStyles.mask, ...maskStyle, - ...drawer?.styles?.mask, + ...contextStyles.mask, }, content: { - ...rest.styles?.content, + ...propStyles.content, ...drawerStyle, - ...drawer?.styles?.content, + ...contextStyles.content, }, wrapper: { - ...rest.styles?.wrapper, + ...propStyles.wrapper, ...contentWrapperStyle, - ...drawer?.styles?.wrapper, + ...contextStyles.wrapper, }, }} open={open ?? visible} diff --git a/components/drawer/index.zh-CN.md b/components/drawer/index.zh-CN.md index 0d0f1870c3..4cfb5be76c 100644 --- a/components/drawer/index.zh-CN.md +++ b/components/drawer/index.zh-CN.md @@ -50,8 +50,7 @@ v5 使用 `rootClassName` 与 `rootStyle` 来配置最外层元素样式。原 v | autoFocus | 抽屉展开后是否将焦点切换至其 Dom 节点 | boolean | true | 4.17.0 | | afterOpenChange | 切换抽屉时动画结束后的回调 | function(open) | - | | | className | Drawer 容器外层 className 设置,如果需要设置最外层,请使用 rootClassName | string | - | | -| classNames | 配置抽屉内置模块的 className | `header?: string; body?: string; footer?: string; mask?: string; content?: string; wrapper?: string;` | - | | -| styles | 配置抽屉内置模块的 style | `header?: CSSProperties; body?: CSSProperties; footer?: CSSProperties; mask?: CSSProperties; content?: CSSProperties; wrapper?: CSSProperties;` | - | 5.10.0 | +| classNames | 语义化结构 className | [Record](#semantic-dom) | - | 5.10.0 | | closeIcon | 自定义关闭图标。5.7.0:设置为 `null` 或 `false` 时隐藏关闭按钮 | ReactNode | <CloseOutlined /> | | | destroyOnClose | 关闭时销毁 Drawer 里的子元素 | boolean | false | | | extra | 抽屉右上角的操作区域 | ReactNode | - | 4.17.0 | @@ -67,12 +66,17 @@ v5 使用 `rootClassName` 与 `rootStyle` 来配置最外层元素样式。原 v | rootStyle | 可用于设置 Drawer 最外层容器的样式,和 `style` 的区别是作用节点包括 `mask` | CSSProperties | - | | | size | 预设抽屉宽度(或高度),default `378px` 和 large `736px` | 'default' \| 'large' | 'default' | 4.17.0 | | style | 设计 Drawer 容器样式,如果你只需要设置内容部分请使用 `bodyStyle` | CSSProperties | - | | +| styles | 语义化结构 style | [Record](#semantic-dom) | - | 5.10.0 | | title | 标题 | ReactNode | - | | | open | Drawer 是否可见 | boolean | - | | width | 宽度 | string \| number | 378 | | | zIndex | 设置 Drawer 的 `z-index` | number | 1000 | | | onClose | 点击遮罩层或左上角叉或取消按钮的回调 | function(e) | - | | +## Semantic DOM + + + ## 主题变量(Design Token) diff --git a/components/input/demo/_semantic_input.tsx b/components/input/demo/_semantic_input.tsx new file mode 100644 index 0000000000..79f7d71f89 --- /dev/null +++ b/components/input/demo/_semantic_input.tsx @@ -0,0 +1,61 @@ +import React from 'react'; +import { EditOutlined, UserOutlined } from '@ant-design/icons'; +import { Input } from 'antd'; + +import SemanticPreview from '../../../.dumi/components/SemanticPreview'; +import useLocale from '../../../.dumi/hooks/useLocale'; + +const locales = { + cn: { + input: '输入框元素', + prefix: '前缀的包裹元素', + suffix: '后缀的包裹元素', + count: '文字计数元素', + }, + en: { + input: 'input element', + prefix: 'prefix element', + suffix: 'suffix element', + count: 'count element', + }, +}; + +const App: React.FC = () => { + const [locale] = useLocale(locales); + + return ( + + } + suffix={} + showCount + defaultValue="Hello, Ant Design" + /> + + ); +}; + +export default App; diff --git a/components/input/demo/_semantic_textarea.tsx b/components/input/demo/_semantic_textarea.tsx new file mode 100644 index 0000000000..9c9fcf23fb --- /dev/null +++ b/components/input/demo/_semantic_textarea.tsx @@ -0,0 +1,48 @@ +import React from 'react'; +import { Input } from 'antd'; + +import SemanticPreview from '../../../.dumi/components/SemanticPreview'; +import useLocale from '../../../.dumi/hooks/useLocale'; + +const locales = { + cn: { + textarea: '输入框元素', + count: '文字计数元素', + }, + en: { + textarea: 'textarea element', + count: 'count element', + }, +}; + +const App: React.FC = () => { + const [locale] = useLocale(locales); + + return ( + + + + ); +}; + +export default App; diff --git a/components/input/index.en-US.md b/components/input/index.en-US.md index b6b0394fbd..ffc1d02d3c 100644 --- a/components/input/index.en-US.md +++ b/components/input/index.en-US.md @@ -138,21 +138,11 @@ Supports all props of `Input`. #### Input - -| Property | Description | Version | -| --- | --- | --- | -| input | `input` element | 5.4.0 | -| prefix | Wrapper of prefix | 5.4.0 | -| suffix | Wrapper of suffix | 5.4.0 | -| count | Text count element | 5.4.0 | + #### Input.TextArea - -| Property | Description | Version | -| --- | --- | --- | -| textarea | `textarea` element | 5.4.0 | -| count | Text count element | 5.4.0 | + ## Design Token diff --git a/components/input/index.zh-CN.md b/components/input/index.zh-CN.md index 139bbbdc3f..e8e7ef98af 100644 --- a/components/input/index.zh-CN.md +++ b/components/input/index.zh-CN.md @@ -139,19 +139,11 @@ interface CountConfig { #### Input -| 名称 | 说明 | 版本 | -| ------ | ------------------ | ----- | -| input | `input` 元素 | 5.4.0 | -| prefix | 所有前缀的包裹元素 | 5.4.0 | -| suffix | 所有后缀的包裹元素 | 5.4.0 | -| count | 文字计数元素 | 5.4.0 | + #### Input.TextArea -| 名称 | 说明 | 版本 | -| -------- | --------------- | ----- | -| textarea | `textarea` 元素 | 5.4.0 | -| count | 文字计数元素 | 5.4.0 | + ## 主题变量(Design Token) diff --git a/components/modal/demo/_semantic.tsx b/components/modal/demo/_semantic.tsx new file mode 100644 index 0000000000..1bc2483091 --- /dev/null +++ b/components/modal/demo/_semantic.tsx @@ -0,0 +1,90 @@ +import React from 'react'; +import { Modal, type ModalProps } from 'antd'; + +import SemanticPreview from '../../../.dumi/components/SemanticPreview'; +import useLocale from '../../../.dumi/hooks/useLocale'; + +const locales = { + cn: { + mask: '遮罩层元素', + wrapper: '包裹层元素,一般用于动画容器', + content: 'Drawer 容器元素', + header: '头部元素', + body: '内容元素', + footer: '底部元素', + }, + en: { + mask: 'Mask element', + wrapper: 'Wrapper element. Used for motion container', + content: 'Drawer container element', + header: 'Header element', + body: 'Body element', + footer: 'Footer element', + }, +}; + +const BlockModal = (props: ModalProps) => { + const divRef = React.useRef(null); + + return ( +
+ divRef.current!} + {...props} + styles={ + { + mask: { + position: 'absolute', + }, + wrapper: { + position: 'absolute', + }, + } as any + } + style={{ + top: '50%', + transform: 'translateY(-50%)', + marginBottom: 0, + paddingBottom: 0, + }} + /> +
+ ); +}; + +const App: React.FC = () => { + const [locale] = useLocale(locales); + + return ( + + +

Some contents...

+
+
+ ); +}; + +export default App; diff --git a/components/modal/index.en-US.md b/components/modal/index.en-US.md index a52f643ca6..d4954fef2a 100644 --- a/components/modal/index.en-US.md +++ b/components/modal/index.en-US.md @@ -190,6 +190,10 @@ const confirmed = await modal.confirm({ ... }); | originNode | default node | React.ReactNode | - | | extra | extended options | { OkBtn: FC; CancelBtn: FC } | - | +### `styles` and `classNames` attribute + + + ## Design Token diff --git a/components/modal/index.zh-CN.md b/components/modal/index.zh-CN.md index 7ff9aeb462..d8140d4700 100644 --- a/components/modal/index.zh-CN.md +++ b/components/modal/index.zh-CN.md @@ -191,6 +191,10 @@ const confirmed = await modal.confirm({ ... }); | originNode | 默认节点 | React.ReactNode | - | | extra | 扩展选项 | { OkBtn: FC; CancelBtn: FC } | - | +### `styles` and `classNames` 属性 + + + ## 主题变量(Design Token) diff --git a/components/slider/demo/_semantic.tsx b/components/slider/demo/_semantic.tsx new file mode 100644 index 0000000000..0ef46f7945 --- /dev/null +++ b/components/slider/demo/_semantic.tsx @@ -0,0 +1,55 @@ +import React from 'react'; +import { Slider } from 'antd'; + +import SemanticPreview from '../../../.dumi/components/SemanticPreview'; +import useLocale from '../../../.dumi/hooks/useLocale'; + +const locales = { + cn: { + track: '范围选择下,点和点之间单个选取条', + tracks: '范围选择下,整个范围选取条', + rail: '背景条元素', + handle: '抓取点元素', + }, + en: { + track: 'The selection bar between points and points under the range selection', + tracks: 'The entire range selection bar under the range selection', + rail: 'Background rail element', + handle: 'Grab handle element', + }, +}; + +const App: React.FC = () => { + const [locale] = useLocale(locales); + + return ( + + + + ); +}; + +export default App; diff --git a/components/slider/index.en-US.md b/components/slider/index.en-US.md index 845e6927af..bafce14b54 100644 --- a/components/slider/index.en-US.md +++ b/components/slider/index.en-US.md @@ -38,6 +38,7 @@ Common props ref:[Common props](/docs/react/common-props) | --- | --- | --- | --- | --- | | autoAdjustOverflow | Whether to automatically adjust the popup position | boolean | true | 5.8.0 | | autoFocus | Whether get focus when component mounted | boolean | false | | +| classNames | Semantic structure className | [Record](#semantic-dom) | - | 5.10.0 | | defaultValue | The default value of slider. When `range` is false, use number, otherwise, use \[number, number] | number \| \[number, number] | 0 \| \[0, 0] | | | disabled | If true, the slider will not be intractable | boolean | false | | | keyboard | Support using keyboard to move handlers | boolean | true | 5.2.0+ | @@ -49,21 +50,13 @@ Common props ref:[Common props](/docs/react/common-props) | range | Dual thumb mode | boolean | false | | | reverse | Reverse the component | boolean | false | | | step | The granularity the slider can step through values. Must greater than 0, and be divided by (max - min) . When `marks` no null, `step` can be null | number \| null | 1 | | +| styles | Semantic structure style | [Record](#semantic-dom) | - | 5.10.0 | | tooltip | The tooltip relate props | [tooltip](#tooltip) | - | 4.23.0 | | value | The value of slider. When `range` is false, use number, otherwise, use \[number, number] | number \| \[number, number] | - | | | vertical | If true, the slider will be vertical | boolean | false | | | onChangeComplete | Fire when `mouseup` or `keyup` is fired | (value) => void | - | | | onChange | Callback function that is fired when the user changes the slider's value | (value) => void | - | | -### `styles` 和 `classNames` 属性 - -| Property | Description | Version | -| -------- | ------------------------------------------- | ------- | -| track | The track between handle to handle in range | 5.10.0 | -| tracks | Who track in range | 5.10.0 | -| rail | Background rail | 5.10.0 | -| handle | The handle pointer | 5.10.0 | - ### range | Property | Description | Type | Default | Version | @@ -86,6 +79,10 @@ Common props ref:[Common props](/docs/react/common-props) | blur() | Remove focus | | | focus() | Get focus | | +## Semantic DOM + + + ## Design Token diff --git a/components/slider/index.zh-CN.md b/components/slider/index.zh-CN.md index 1bbf2813ac..5b953d1b54 100644 --- a/components/slider/index.zh-CN.md +++ b/components/slider/index.zh-CN.md @@ -38,7 +38,7 @@ demo: | 参数 | 说明 | 类型 | 默认值 | 版本 | | --- | --- | --- | --- | --- | | autoFocus | 自动获取焦点 | boolean | false | | -| classNames | 语义化结构 className | Record | - | 5.10.0 | +| classNames | 语义化结构 className | [Record](#semantic-dom) | - | 5.10.0 | | defaultValue | 设置初始取值。当 `range` 为 false 时,使用 number,否则用 \[number, number] | number \| \[number, number] | 0 \| \[0, 0] | | | disabled | 值为 true 时,滑块为禁用状态 | boolean | false | | | keyboard | 支持使用键盘操作 handler | boolean | true | 5.2.0+ | @@ -50,22 +50,13 @@ demo: | range | 双滑块模式 | boolean \| [range](#range) | false | | | reverse | 反向坐标轴 | boolean | false | | | step | 步长,取值必须大于 0,并且可被 (max - min) 整除。当 `marks` 不为空对象时,可以设置 `step` 为 null,此时 Slider 的可选值仅有 marks 标出来的部分 | number \| null | 1 | | -| styles | 语义化结构 className | Record | - | 5.10.0 | +| styles | 语义化结构 styles | [Record](#semantic-dom) | - | 5.10.0 | | tooltip | 设置 Tooltip 相关属性 | [tooltip](#tooltip) | - | 4.23.0 | | value | 设置当前取值。当 `range` 为 false 时,使用 number,否则用 \[number, number] | number \| \[number, number] | - | | | vertical | 值为 true 时,Slider 为垂直方向 | boolean | false | | | onChangeComplete | 与 `mouseup` 和 `keyup` 触发时机一致,把当前值作为参数传入 | (value) => void | - | | | onChange | 当 Slider 的值发生改变时,会触发 onChange 事件,并把改变后的值作为参数传入 | (value) => void | - | | -### `styles` 和 `classNames` 属性 - -| 名称 | 说明 | 版本 | -| ------ | -------------------------------- | ------ | -| track | 范围选择下,点和点之间单个选取条 | 5.10.0 | -| tracks | 范围选择下,整个范围选取条 | 5.10.0 | -| rail | 背景条 | 5.10.0 | -| handle | 抓取点 | 5.10.0 | - ### range | 参数 | 说明 | 类型 | 默认值 | 版本 | @@ -89,6 +80,10 @@ demo: | blur() | 移除焦点 | | | focus() | 获取焦点 | | +## Semantic DOM + + + ## 主题变量(Design Token) diff --git a/components/space/demo/_semantic.tsx b/components/space/demo/_semantic.tsx new file mode 100644 index 0000000000..041072e909 --- /dev/null +++ b/components/space/demo/_semantic.tsx @@ -0,0 +1,38 @@ +import React from 'react'; +import { Button, Space } from 'antd'; + +import SemanticPreview from '../../../.dumi/components/SemanticPreview'; +import useLocale from '../../../.dumi/hooks/useLocale'; + +const locales = { + cn: { + item: '包裹的子组件', + }, + en: { + item: 'Wrapped item element', + }, +}; + +const App: React.FC = () => { + const [locale] = useLocale(locales); + + return ( + + + + + + + + ); +}; + +export default App; diff --git a/components/space/index.en-US.md b/components/space/index.en-US.md index 5e106f09e2..25bf23e13b 100644 --- a/components/space/index.en-US.md +++ b/components/space/index.en-US.md @@ -42,9 +42,11 @@ Common props ref:[Common props](/docs/react/common-props) | Property | Description | Type | Default | Version | | --- | --- | --- | --- | --- | | align | Align items | `start` \| `end` \|`center` \|`baseline` | - | 4.2.0 | +| classNames | Semantic className | [Record](#semantic-dom) | - | | | direction | The space direction | `vertical` \| `horizontal` | `horizontal` | 4.1.0 | | size | The space size | [Size](#size) \| [Size\[\]](#size) | `small` | 4.1.0 \| Array: 4.9.0 | | split | Set split | ReactNode | - | 4.7.0 | +| styles | Semantic style | [Record](#semantic-dom) | - | | | wrap | Auto wrap line, when `horizontal` effective | boolean | false | 4.9.0 | ### Size @@ -71,12 +73,9 @@ Use Space.Compact when child form components are compactly connected and the bor | direction | Set direction of layout | `vertical` \| `horizontal` | `horizontal` | 4.24.0 | | size | Set child component size | `large` \| `middle` \| `small` | `middle` | 4.24.0 | -### `styles` and `classNames` attribute +## Semantic DOM - -| Property | Description | Version | -| -------- | ------------------------- | ------- | -| item | set `Space` child element | 5.6.0 | + ## Design Token diff --git a/components/space/index.zh-CN.md b/components/space/index.zh-CN.md index 23d34d2159..bea2e878c9 100644 --- a/components/space/index.zh-CN.md +++ b/components/space/index.zh-CN.md @@ -48,9 +48,11 @@ coverDark: https://mdn.alipayobjects.com/huamei_7uahnr/afts/img/A*37T2R6O9oi0AAA | 参数 | 说明 | 类型 | 默认值 | 版本 | | --- | --- | --- | --- | --- | | align | 对齐方式 | `start` \| `end` \|`center` \|`baseline` | - | 4.2.0 | +| classNames | 语义化 className | [Record](#semantic-dom) | - | | | direction | 间距方向 | `vertical` \| `horizontal` | `horizontal` | 4.1.0 | | size | 间距大小 | [Size](#size) \| [Size\[\]](#size) | `small` | 4.1.0 \| Array: 4.9.0 | | split | 设置拆分 | ReactNode | - | 4.7.0 | +| styles | 语义化 style | [Record](#semantic-dom) | - | | | wrap | 是否自动换行,仅在 `horizontal` 时有效 | boolean | false | 4.9.0 | ### Size @@ -79,12 +81,9 @@ coverDark: https://mdn.alipayobjects.com/huamei_7uahnr/afts/img/A*37T2R6O9oi0AAA | direction | 指定排列方向 | `vertical` \| `horizontal` | `horizontal` | 4.24.0 | | size | 子组件大小 | `large` \| `middle` \| `small` | `middle` | 4.24.0 | -### `styles` 和 `classNames` 属性 +## Semantic DOM - -| 名称 | 说明 | 版本 | -| ---- | --------------------- | ----- | -| item | 设置 `Space` 包裹的子组件 | 5.6.0 | + ## 主题变量(Design Token) diff --git a/package.json b/package.json index b4fe4f8169..3789988aee 100644 --- a/package.json +++ b/package.json @@ -50,7 +50,6 @@ "build": "npm run compile && NODE_OPTIONS='--max-old-space-size=4096' npm run dist", "changelog": "git fetch origin && tsx scripts/print-changelog.ts", "check-commit": "tsx scripts/check-commit.ts", - "check-ts-demo": "tsx scripts/check-ts-demo.ts", "clean": "antd-tools run clean && rm -rf es lib coverage dist report.html", "clean-lockfiles": "rm -rf package-lock.json yarn.lock", "collect-token-statistic": "tsx scripts/collect-token-statistic.ts", diff --git a/scripts/__snapshots__/check-site.ts.snap b/scripts/__snapshots__/check-site.ts.snap index 9498d40bc0..41d6502ca3 100644 --- a/scripts/__snapshots__/check-site.ts.snap +++ b/scripts/__snapshots__/check-site.ts.snap @@ -24,17 +24,17 @@ exports[`site test Component components/avatar en Page 1`] = `2`; exports[`site test Component components/avatar zh Page 1`] = `2`; -exports[`site test Component components/badge en Page 1`] = `3`; +exports[`site test Component components/badge en Page 1`] = `2`; -exports[`site test Component components/badge zh Page 1`] = `3`; +exports[`site test Component components/badge zh Page 1`] = `2`; exports[`site test Component components/breadcrumb en Page 1`] = `3`; exports[`site test Component components/breadcrumb zh Page 1`] = `3`; -exports[`site test Component components/button en Page 1`] = `2`; +exports[`site test Component components/button en Page 1`] = `1`; -exports[`site test Component components/button zh Page 1`] = `2`; +exports[`site test Component components/button zh Page 1`] = `1`; exports[`site test Component components/calendar en Page 1`] = `1`; @@ -116,9 +116,9 @@ exports[`site test Component components/image en Page 1`] = `4`; exports[`site test Component components/image zh Page 1`] = `4`; -exports[`site test Component components/input en Page 1`] = `8`; +exports[`site test Component components/input en Page 1`] = `6`; -exports[`site test Component components/input zh Page 1`] = `8`; +exports[`site test Component components/input zh Page 1`] = `6`; exports[`site test Component components/input-number en Page 1`] = `2`; @@ -196,13 +196,13 @@ exports[`site test Component components/skeleton en Page 1`] = `6`; exports[`site test Component components/skeleton zh Page 1`] = `6`; -exports[`site test Component components/slider en Page 1`] = `5`; +exports[`site test Component components/slider en Page 1`] = `4`; -exports[`site test Component components/slider zh Page 1`] = `5`; +exports[`site test Component components/slider zh Page 1`] = `4`; -exports[`site test Component components/space en Page 1`] = `3`; +exports[`site test Component components/space en Page 1`] = `2`; -exports[`site test Component components/space zh Page 1`] = `3`; +exports[`site test Component components/space zh Page 1`] = `2`; exports[`site test Component components/spin en Page 1`] = `1`; diff --git a/scripts/check-ts-demo.ts b/scripts/check-ts-demo.ts deleted file mode 100644 index efb4a17bfa..0000000000 --- a/scripts/check-ts-demo.ts +++ /dev/null @@ -1,86 +0,0 @@ -/* eslint-disable no-await-in-loop, no-console */ -import { spawn } from 'child_process'; -import path from 'path'; -import chalk from 'chalk'; -import fs from 'fs-extra'; -import { globSync } from 'glob'; - -(async () => { - console.time('Execution...'); - - const demoFiles = globSync(path.join(process.cwd(), 'components/**/demo/*.md')); - - const tmpFolder = path.resolve('components', '~tmp'); - await fs.remove(tmpFolder); - await fs.ensureDir(tmpFolder); - - function getTypescriptDemo(content: string, demoPath: string) { - const lines = content.split(/[\n\r]/); - - const tsxStartLine = lines.findIndex((line) => - line.replace(/\s/g, '').toLowerCase().includes('```tsx'), - ); - - if (tsxStartLine < 0) { - return null; - } - - const tsxEndLine = lines.findIndex( - (line, index) => index > tsxStartLine && line.trim() === '```', - ); - - let script = lines.slice(tsxStartLine + 1, tsxEndLine).join('\n'); - - // insert React - if (!script.includes('import React') && !script.includes('import * as React')) { - script = `import React from 'react';\n${script}`; - } - - // Replace mountNode - script = script.replace('mountNode', `document.getElementById('#root')`); - - // Replace antd - script = script.replace(`from 'antd'`, `from '..'`); - - // Add path - script = `/* eslint-disabled */\n// ${demoPath}\n${script}`; - - return script; - } - - for (let i = 0; i < demoFiles.length; i += 1) { - const demoPath = demoFiles[i]; - - const content = await fs.readFile(demoPath, 'utf8'); - const script = getTypescriptDemo(content, demoPath); - - const dirs = path.dirname(demoPath).split(path.sep); - - // Parse TSX - if (script) { - const tmpFile = path.join( - tmpFolder, - `${dirs[dirs.length - 2]}-${path.basename(demoPath).replace(/\..*/, '')}.tsx`, - ); - await fs.writeFile(tmpFile, script, 'utf8'); - } - } - - const child = spawn('npm', ['run', 'tsc']); - - child.stdout.pipe(process.stdout); - child.stderr.pipe(process.stderr); - - child.on('exit', async (code: number) => { - console.timeEnd('Execution...'); - - if (code) { - console.log(chalk.red('💥 OPS! Seems some tsx demo not pass tsc...')); - } else { - await fs.remove(tmpFolder); - console.log(chalk.green('🤪 All tsx demo passed. Congratulations!')); - } - - process.exit(code); - }); -})(); diff --git a/tests/shared/demoTest.tsx b/tests/shared/demoTest.tsx index 63bac11c88..e70929316e 100644 --- a/tests/shared/demoTest.tsx +++ b/tests/shared/demoTest.tsx @@ -2,6 +2,7 @@ import path from 'path'; import * as React from 'react'; import { createCache, StyleProvider } from '@ant-design/cssinjs'; +import { ConfigProvider } from 'antd'; import { globSync } from 'glob'; import kebabCase from 'lodash/kebabCase'; import { renderToString } from 'react-dom/server'; @@ -11,7 +12,6 @@ import { render } from '../utils'; import { TriggerMockContext } from './demoTestContext'; import { excludeWarning, isSafeWarning } from './excludeWarning'; import rootPropsTest from './rootPropsTest'; -import { ConfigProvider } from 'antd'; export { rootPropsTest }; @@ -28,7 +28,9 @@ export type Options = { }; function baseText(doInject: boolean, component: string, options: Options = {}) { - const files = globSync(`./components/${component}/demo/*.tsx`); + const files = globSync(`./components/${component}/demo/*.tsx`).filter( + (file) => !file.includes('_semantic'), + ); files.forEach((file) => { // to compatible windows path file = file.split(path.sep).join('/'); diff --git a/tests/shared/imageTest.tsx b/tests/shared/imageTest.tsx index 178d29bddb..4b91c09cee 100644 --- a/tests/shared/imageTest.tsx +++ b/tests/shared/imageTest.tsx @@ -251,7 +251,9 @@ type Options = { // eslint-disable-next-line jest/no-export export function imageDemoTest(component: string, options: Options = {}) { let describeMethod = options.skip === true ? describe.skip : describe; - const files = globSync(`./components/${component}/demo/*.tsx`); + const files = globSync(`./components/${component}/demo/*.tsx`).filter( + (file) => !file.includes('_semantic'), + ); files.forEach((file) => { if (Array.isArray(options.skip) && options.skip.some((c) => file.endsWith(c))) { diff --git a/tsconfig-old-react.json b/tsconfig-old-react.json index 4c549be80c..76983544d6 100644 --- a/tsconfig-old-react.json +++ b/tsconfig-old-react.json @@ -8,5 +8,6 @@ "antd/locale/*": ["locale/*"] } }, - "include": ["components/*/demo/*.tsx"] + "include": ["components/*/demo/*.tsx"], + "exclude": ["components/*/demo/_semantic*"] }