From f3cebe3dd2c68d1ed6301b639b6cf733be84ba5f Mon Sep 17 00:00:00 2001 From: thinkasany <480968828@qq.com> Date: Thu, 10 Apr 2025 15:08:32 +0800 Subject: [PATCH] feat: ConfigProvider support qrcode (#52172) * feat: ConfigProvider support ClassNames and styles for qrcode * update mask to cover * fix test * update token from QRCodeMaskBackgroundColor to QRCodeCoverBackgroundColor * rm * fix typo --- components/config-provider/context.ts | 4 ++ components/config-provider/index.en-US.md | 3 +- components/config-provider/index.tsx | 4 ++ components/config-provider/index.zh-CN.md | 3 +- .../__snapshots__/demo-extend.test.ts.snap | 12 ++--- .../__tests__/__snapshots__/demo.test.ts.snap | 12 ++--- .../__snapshots__/index.test.tsx.snap | 6 +-- components/qr-code/__tests__/index.test.tsx | 27 ++++++++++ components/qr-code/demo/_semantic.tsx | 34 +++++++++++++ components/qr-code/index.en-US.md | 4 ++ components/qr-code/index.tsx | 50 +++++++++++++++---- components/qr-code/index.zh-CN.md | 4 ++ components/qr-code/interface.ts | 3 ++ components/qr-code/style/index.ts | 10 ++-- 14 files changed, 143 insertions(+), 33 deletions(-) create mode 100644 components/qr-code/demo/_semantic.tsx diff --git a/components/config-provider/context.ts b/components/config-provider/context.ts index cc747d8dca..a94fd7566d 100644 --- a/components/config-provider/context.ts +++ b/components/config-provider/context.ts @@ -37,6 +37,7 @@ import type { ArgsProps as NotificationProps } from '../notification'; import type { PaginationProps } from '../pagination'; import type { PopconfirmProps } from '../popconfirm'; import type { PopoverProps } from '../popover'; +import type { QRCodeProps } from '../qr-code'; import type { RadioProps } from '../radio'; import type { ResultProps } from '../result'; import type { SegmentedProps } from '../segmented'; @@ -271,6 +272,8 @@ export type PopconfirmConfig = Pick< 'className' | 'style' | 'styles' | 'classNames' | 'arrow' >; +export type QRcodeConfig = ComponentStyleConfig & Pick; + export type SliderConfig = ComponentStyleConfig & Pick; export type SkeletonConfig = ComponentStyleConfig & Pick; @@ -406,6 +409,7 @@ export interface ConfigComponentProps { dropdown?: ComponentStyleConfig; flex?: FlexConfig; wave?: WaveConfig; + qrcode?: QRcodeConfig; watermark?: ComponentStyleConfig; } diff --git a/components/config-provider/index.en-US.md b/components/config-provider/index.en-US.md index c3317b5ace..0fbb1e1681 100644 --- a/components/config-provider/index.en-US.md +++ b/components/config-provider/index.en-US.md @@ -169,7 +169,8 @@ const { | tooltip | Set Tooltip common props | { className?: string, style?: React.CSSProperties, classNames?:[Tooltip\["classNames"\]](/components/tooltip#semantic-dom), styles?: [Tooltip\["styles"\]](/components/tooltip#semantic-dom), arrow: boolean \| { pointAtCenter: boolean } } | - | 5.23.0, `arrow`: 6.0.0 | | popover | Set Popover common props | { className?: string, style?: React.CSSProperties, classNames?:[Popover\["classNames"\]](/components/popover#semantic-dom), styles?: [Popover\["styles"\]](/components/popover#semantic-dom), arrow: boolean \| { pointAtCenter: boolean } } | - | 5.23.0, `arrow`: 6.0.0 | | popconfirm | Set Popconfirm common props | { className?: string, style?: React.CSSProperties, classNames?:[Popconfirm\["classNames"\]](/components/popconfirm#semantic-dom), styles?: [Popconfirm\["styles"\]](/components/popconfirm#semantic-dom), arrow: boolean \| { pointAtCenter: boolean } } | - | 5.23.0, `arrow`: 6.0.0 | -| transfer | Set Transfer common props | { className?: string, style?: React.CSSProperties, classNames?: [TransferConfig\["classNames"\]](/components/transfer#semantic-dom), styles?: [TransferConfig\["styles"\]](/components/transfer#semantic-dom), selectionsIcon?: React.ReactNode } | - | | +| qrcode | Set QRCode common props | { className?: string, style?: React.CSSProperties, classNames?:[QRCode\["classNames"\]](/components/qr-code#semantic-dom), styles?: [QRCode\["styles"\]](/components/qr-code#semantic-dom) } | - | | +| transfer | Set Transfer common props | { className?: string, style?: React.CSSProperties, classNames?: [TransferConfig\["classNames"\]](/components/transfer#semantic-dom), styles?: [TransferConfig\["styles"\]](/components/transfer#semantic-dom), selectionsIcon?: React.ReactNode } | - | | | tree | Set Tree common props | { className?: string, style?: React.CSSProperties, classNames?: [TreeConfig\["classNames"\]](/components/tree#semantic-dom), styles?: [TreeConfig\["styles"\]](/components/tree#semantic-dom) } | - | 5.7.0, `classNames` and `styles`: 6.0.0 | | treeSelect | Set TreeSelect common props | { className?: string, style?: React.CSSProperties, classNames?: [TreeSelectConfig\["classNames"\]](/components/tree-select#semantic-dom), styles?: [TreeSelectConfig\["styles"\]](/components/tree-select#semantic-dom) } | - | | | typography | Set Typography common props | { className?: string, style?: React.CSSProperties } | - | 5.7.0 | diff --git a/components/config-provider/index.tsx b/components/config-provider/index.tsx index 61bad86691..78d69cb97f 100644 --- a/components/config-provider/index.tsx +++ b/components/config-provider/index.tsx @@ -66,6 +66,7 @@ import type { TreeSelectConfig, Variant, WaveConfig, + QRcodeConfig, } from './context'; import { ConfigConsumer, @@ -252,6 +253,7 @@ export interface ConfigProviderProps { popover?: PopoverConfig; popconfirm?: PopconfirmConfig; watermark?: ComponentStyleConfig; + qrcode?: QRcodeConfig; } interface ProviderChildrenProps extends ConfigProviderProps { @@ -400,6 +402,7 @@ const ProviderChildren: React.FC = (props) => { tooltip, popover, popconfirm, + qrcode, floatButtonGroup, variant, inputNumber, @@ -508,6 +511,7 @@ const ProviderChildren: React.FC = (props) => { tooltip, popover, popconfirm, + qrcode, floatButtonGroup, variant, inputNumber, diff --git a/components/config-provider/index.zh-CN.md b/components/config-provider/index.zh-CN.md index fc186c3037..eab48b155a 100644 --- a/components/config-provider/index.zh-CN.md +++ b/components/config-provider/index.zh-CN.md @@ -171,7 +171,8 @@ const { | tooltip | 设置 Tooltip 组件的通用属性 | { className?: string, style?: React.CSSProperties, classNames?:[Tooltip\["classNames"\]](/components/tooltip-cn#semantic-dom), styles?: [Tooltip\["styles"\]](/components/tooltip-cn#semantic-dom), arrow: boolean \| { pointAtCenter: boolean } } | - | 5.23.0, `arrow`: 6.0.0 | | popover | 设置 Popover 组件的通用属性 | { className?: string, style?: React.CSSProperties, classNames?:[Popover\["classNames"\]](/components/popover-cn#semantic-dom), styles?: [Popover\["styles"\]](/components/popover-cn#semantic-dom), arrow: boolean \| { pointAtCenter: boolean } } | - | 5.23.0, `arrow`: 6.0.0 | | popconfirm | 设置 Popconfirm 组件的通用属性 | { className?: string, style?: React.CSSProperties, classNames?:[Popconfirm\["classNames"\]](/components/popconfirm-cn#semantic-dom), styles?: [Popconfirm\["styles"\]](/components/popconfirm-cn#semantic-dom), arrow: boolean \| { pointAtCenter: boolean } } | - | 5.23.0, `arrow`: 6.0.0 | -| transfer | 设置 Transfer 组件的通用属性 | { className?: string, style?: React.CSSProperties, classNames?: [TransferConfig\["classNames"\]](/components/transfer-cn#semantic-dom), styles?: [TransferConfig\["styles"\]](/components/transfer-cn#semantic-dom), selectionsIcon?: React.ReactNode } | - | | +| qrcode | 设置 QRCode 组件的通用属性 | { className?: string, style?: React.CSSProperties, classNames?:[QRCode\["classNames"\]](/components/qr-code-cn#semantic-dom), styles?: [QRCode\["styles"\]](/components/qr-code-cn#semantic-dom) } | - | | +| transfer | 设置 Transfer 组件的通用属性 | { className?: string, style?: React.CSSProperties, classNames?: [TransferConfig\["classNames"\]](/components/transfer-cn#semantic-dom), styles?: [TransferConfig\["styles"\]](/components/transfer-cn#semantic-dom), selectionsIcon?: React.ReactNode } | - | | | tree | 设置 Tree 组件的通用属性 | { className?: string, style?: React.CSSProperties, classNames?: [TreeConfig\["classNames"\]](/components/tree-cn#semantic-dom), styles?: [TreeConfig\["styles"\]](/components/tree-cn#semantic-dom) } | - | 5.7.0, `classNames` 和 `styles`: 6.0.0 | | treeSelect | 设置 TreeSelect 组件的通用属性 | { className?: string, style?: React.CSSProperties, classNames?: [TreeSelectConfig\["classNames"\]](/components/tree-select-cn#semantic-dom), styles?: [TreeSelectConfig\["styles"\]](/components/tree-select-cn#semantic-dom) } | - | | | typography | 设置 Typography 组件的通用属性 | { className?: string, style?: React.CSSProperties } | - | 5.7.0 | diff --git a/components/qr-code/__tests__/__snapshots__/demo-extend.test.ts.snap b/components/qr-code/__tests__/__snapshots__/demo-extend.test.ts.snap index ddf22fd3fc..708996357b 100644 --- a/components/qr-code/__tests__/__snapshots__/demo-extend.test.ts.snap +++ b/components/qr-code/__tests__/__snapshots__/demo-extend.test.ts.snap @@ -221,7 +221,7 @@ exports[`renders components/qr-code/demo/customStatusRender.tsx extend context c style="background-color: transparent; width: 160px; height: 160px;" >

{ ); expect(svgContainer.querySelector('svg')).toHaveAttribute('aria-label', ariaLabel); }); + + it('should apply custom styles to QRCode', () => { + const customClassNames = { + root: 'custom-root', + cover: 'custom-cover', + }; + + const customStyles = { + root: { color: 'red' }, + cover: { color: 'blue' }, + }; + + const { container } = render( + , + ); + + const QRCodeElement = container.querySelector('.ant-qrcode') as HTMLElement; + const QRCodeCoverElement = container.querySelector('.ant-qrcode-cover') as HTMLElement; + + // check classNames + expect(QRCodeElement.classList).toContain('custom-root'); + expect(QRCodeCoverElement.classList).toContain('custom-cover'); + + // check styles + expect(QRCodeElement.style.color).toBe('red'); + expect(QRCodeCoverElement.style.color).toBe('blue'); + }); }); diff --git a/components/qr-code/demo/_semantic.tsx b/components/qr-code/demo/_semantic.tsx new file mode 100644 index 0000000000..91005caed4 --- /dev/null +++ b/components/qr-code/demo/_semantic.tsx @@ -0,0 +1,34 @@ +import React from 'react'; +import { QRCode } from 'antd'; + +import SemanticPreview from '../../../.dumi/components/SemanticPreview'; +import useLocale from '../../../.dumi/hooks/useLocale'; + +const locales = { + cn: { + root: '根元素', + cover: '遮罩层元素', + }, + en: { + root: 'Root element', + cover: 'Cover element', + }, +}; + +const App: React.FC = () => { + const [locale] = useLocale(locales); + const value = 'https://ant.design'; + return ( + + + + ); +}; + +export default App; diff --git a/components/qr-code/index.en-US.md b/components/qr-code/index.en-US.md index fd01052da2..9521ccc2b3 100644 --- a/components/qr-code/index.en-US.md +++ b/components/qr-code/index.en-US.md @@ -61,6 +61,10 @@ type StatusRenderInfo = { }; ``` +## Semantic DOM + + + ## Design Token diff --git a/components/qr-code/index.tsx b/components/qr-code/index.tsx index 8ab94791ea..0756c67631 100644 --- a/components/qr-code/index.tsx +++ b/components/qr-code/index.tsx @@ -1,18 +1,20 @@ -import React, { useContext } from 'react'; +import React from 'react'; import { QRCodeCanvas, QRCodeSVG } from '@rc-component/qrcode'; import omit from '@rc-component/util/lib/omit'; import pickAttrs from '@rc-component/util/lib/pickAttrs'; -import classNames from 'classnames'; +import cls from 'classnames'; +import useMergeSemantic from '../_util/hooks/useMergeSemantic'; import { devUseWarning } from '../_util/warning'; -import type { ConfigConsumerProps } from '../config-provider'; -import { ConfigContext } from '../config-provider'; +import { useComponentConfig } from '../config-provider/context'; import { useLocale } from '../locale'; import { useToken } from '../theme/internal'; import type { QRCodeProps, QRProps } from './interface'; import QRcodeStatus from './QrcodeStatus'; import useStyle from './style/index'; +export type { QRCodeProps, QRProps }; + const QRCode: React.FC = (props) => { const [, token] = useToken(); const { @@ -32,9 +34,24 @@ const QRCode: React.FC = (props) => { prefixCls: customizePrefixCls, bgColor = 'transparent', statusRender, + classNames: qrcodeClassNames, + styles, ...rest } = props; - const { getPrefixCls } = useContext(ConfigContext); + + const { + getPrefixCls, + className: contextClassName, + style: contextStyle, + classNames: contextClassNames, + styles: contextStyles, + } = useComponentConfig('qrcode'); + + const [mergedClassNames, mergedStyles] = useMergeSemantic( + [contextClassNames, qrcodeClassNames], + [contextStyles, styles], + ); + const prefixCls = getPrefixCls('qrcode', customizePrefixCls); const [hashId, cssVarCls] = useStyle(prefixCls); @@ -85,21 +102,32 @@ const QRCode: React.FC = (props) => { return null; } - const mergedCls = classNames(prefixCls, className, rootClassName, hashId, cssVarCls, { - [`${prefixCls}-borderless`]: !bordered, - }); + const rootClassNames = cls( + prefixCls, + className, + rootClassName, + hashId, + cssVarCls, + contextClassName, + mergedClassNames.root, + { + [`${prefixCls}-borderless`]: !bordered, + }, + ); - const mergedStyle: React.CSSProperties = { + const rootStyle: React.CSSProperties = { backgroundColor: bgColor, + ...mergedStyles.root, + ...contextStyle, ...style, width: style?.width ?? size, height: style?.height ?? size, }; return ( -
+
{status !== 'active' && ( -
+
+ ## 主题变量(Design Token) diff --git a/components/qr-code/interface.ts b/components/qr-code/interface.ts index b79f8fad6e..5c3c30d79a 100644 --- a/components/qr-code/interface.ts +++ b/components/qr-code/interface.ts @@ -19,6 +19,7 @@ export type StatusRenderInfo = { onRefresh?: () => void; }; +type SemanticName = 'root' | 'cover'; export interface QRCodeProps extends QRProps, React.HTMLAttributes { type?: 'canvas' | 'svg'; className?: string; @@ -31,4 +32,6 @@ export interface QRCodeProps extends QRProps, React.HTMLAttributes void; statusRender?: (info: StatusRenderInfo) => ReactNode; + classNames?: Partial>; + styles?: Partial>; } diff --git a/components/qr-code/style/index.ts b/components/qr-code/style/index.ts index 5ce1ccae67..efd9ccffdb 100644 --- a/components/qr-code/style/index.ts +++ b/components/qr-code/style/index.ts @@ -20,9 +20,9 @@ interface QRCodeToken extends FullToken<'QRCode'> { QRCodeTextColor: string; /** * @desc QRCode 遮罩背景颜色 - * @descEN Mask background color of QRCode + * @descEN Cover background color of QRCode */ - QRCodeMaskBackgroundColor: string; + QRCodeCoverBackgroundColor: string; } const genQRCodeStyle: GenerateStyle = (token) => { @@ -40,7 +40,7 @@ const genQRCodeStyle: GenerateStyle = (token) => { position: 'relative', overflow: 'hidden', - [`& > ${componentCls}-mask`]: { + [`& > ${componentCls}-cover`]: { position: 'absolute', insetBlockStart: 0, insetInlineStart: 0, @@ -53,7 +53,7 @@ const genQRCodeStyle: GenerateStyle = (token) => { height: '100%', color: token.colorText, lineHeight: token.lineHeight, - background: token.QRCodeMaskBackgroundColor, + background: token.QRCodeCoverBackgroundColor, textAlign: 'center', [`& > ${componentCls}-expired, & > ${componentCls}-scanned`]: { color: token.QRCodeTextColor, @@ -80,7 +80,7 @@ const genQRCodeStyle: GenerateStyle = (token) => { }; export const prepareComponentToken: GetDefaultToken<'QRCode'> = (token) => ({ - QRCodeMaskBackgroundColor: new FastColor(token.colorBgContainer).setA(0.96).toRgbString(), + QRCodeCoverBackgroundColor: new FastColor(token.colorBgContainer).setA(0.96).toRgbString(), }); export default genStyleHooks<'QRCode'>(