From 8b19102b0d2bbc506bdc946ede7d5a54fd8cd0f4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E4=BA=8C=E8=B4=A7=E7=88=B1=E5=90=83=E7=99=BD=E8=90=9D?= =?UTF-8?q?=E5=8D=9C?= Date: Tue, 25 Jul 2023 16:29:47 +0800 Subject: [PATCH] feat: wave effect config (#43784) * feat: support wave effect * docs: more info * test: add test case * docs: update demo * test: coverage --- components/_util/wave/WaveEffect.tsx | 7 +- components/_util/wave/index.ts | 7 +- components/_util/wave/useWave.ts | 34 ++++- components/button/button.tsx | 6 +- .../config-provider/__tests__/wave.test.tsx | 50 +++++++ components/config-provider/context.ts | 8 ++ components/config-provider/demo/wave.md | 7 + components/config-provider/demo/wave.tsx | 128 ++++++++++++++++++ components/config-provider/index.en-US.md | 2 + components/config-provider/index.tsx | 8 ++ components/config-provider/index.zh-CN.md | 2 + components/switch/index.tsx | 2 +- components/tag/index.tsx | 2 +- 13 files changed, 251 insertions(+), 12 deletions(-) create mode 100644 components/config-provider/__tests__/wave.test.tsx create mode 100644 components/config-provider/demo/wave.md create mode 100644 components/config-provider/demo/wave.tsx diff --git a/components/_util/wave/WaveEffect.tsx b/components/_util/wave/WaveEffect.tsx index 2a87aacb5f..353d978a25 100644 --- a/components/_util/wave/WaveEffect.tsx +++ b/components/_util/wave/WaveEffect.tsx @@ -4,6 +4,7 @@ import { render, unmount } from 'rc-util/lib/React/render'; import raf from 'rc-util/lib/raf'; import * as React from 'react'; import { getTargetWaveColor } from './util'; +import type { ShowWaveEffect } from './useWave'; function validateNum(value: number) { return Number.isNaN(value) ? 0 : value; @@ -125,7 +126,7 @@ const WaveEffect: React.FC = (props) => { ); }; -export default function showWaveEffect(node: HTMLElement, className: string) { +const showWaveEffect: ShowWaveEffect = (node, { className }) => { // Create holder const holder = document.createElement('div'); holder.style.position = 'absolute'; @@ -134,4 +135,6 @@ export default function showWaveEffect(node: HTMLElement, className: string) { node?.insertBefore(holder, node?.firstChild); render(, holder); -} +}; + +export default showWaveEffect; diff --git a/components/_util/wave/index.ts b/components/_util/wave/index.ts index 6ad40b2304..874686d47c 100644 --- a/components/_util/wave/index.ts +++ b/components/_util/wave/index.ts @@ -11,10 +11,11 @@ import useWave from './useWave'; export interface WaveProps { disabled?: boolean; children?: React.ReactNode; + component?: string; } const Wave: React.FC = (props) => { - const { children, disabled } = props; + const { children, disabled, component } = props; const { getPrefixCls } = useContext(ConfigContext); const containerRef = useRef(null); @@ -23,7 +24,7 @@ const Wave: React.FC = (props) => { const [, hashId] = useStyle(prefixCls); // =============================== Wave =============================== - const showWave = useWave(containerRef, classNames(prefixCls, hashId)); + const showWave = useWave(containerRef, classNames(prefixCls, hashId), component); // ============================== Effect ============================== React.useEffect(() => { @@ -48,7 +49,7 @@ const Wave: React.FC = (props) => { return; } - showWave(); + showWave(e); }; // Bind events diff --git a/components/_util/wave/useWave.ts b/components/_util/wave/useWave.ts index 162317b8fd..ba607c6706 100644 --- a/components/_util/wave/useWave.ts +++ b/components/_util/wave/useWave.ts @@ -1,14 +1,40 @@ +import * as React from 'react'; +import useEvent from 'rc-util/lib/hooks/useEvent'; import showWaveEffect from './WaveEffect'; +import { ConfigContext } from '../../config-provider'; +import useToken from '../../theme/useToken'; +import type { GlobalToken } from '../../theme'; + +export type ShowWaveEffect = ( + element: HTMLElement, + info: { + className: string; + token: GlobalToken; + component?: string; + event: MouseEvent; + }, +) => void; export default function useWave( nodeRef: React.RefObject, className: string, -): VoidFunction { - function showWave() { + component?: string, +) { + const { wave } = React.useContext(ConfigContext); + const [, token] = useToken(); + + const showWave = useEvent((event: MouseEvent) => { const node = nodeRef.current!; - showWaveEffect(node, className); - } + if (wave?.disabled || !node) { + return; + } + + const { showEffect } = wave || {}; + + // Customize wave effect + (showEffect || showWaveEffect)(node, { className, token, component, event }); + }); return showWave; } diff --git a/components/button/button.tsx b/components/button/button.tsx index fe0a072c25..8fbc7f8807 100644 --- a/components/button/button.tsx +++ b/components/button/button.tsx @@ -289,7 +289,11 @@ const InternalButton: React.ForwardRefRenderFunction< ); if (!isUnBorderedButtonType(type)) { - buttonNode = {buttonNode}; + buttonNode = ( + + {buttonNode} + + ); } return wrapSSR(buttonNode); diff --git a/components/config-provider/__tests__/wave.test.tsx b/components/config-provider/__tests__/wave.test.tsx new file mode 100644 index 0000000000..8958e38c19 --- /dev/null +++ b/components/config-provider/__tests__/wave.test.tsx @@ -0,0 +1,50 @@ +import React from 'react'; +import ConfigProvider from '..'; +import { fireEvent, render, waitFakeTimer } from '../../../tests/utils'; +import Button from '../../button'; + +jest.mock('rc-util/lib/Dom/isVisible', () => () => true); + +describe('ConfigProvider.Wave', () => { + beforeEach(() => { + jest.useFakeTimers(); + }); + + afterEach(() => { + jest.useRealTimers(); + }); + + it('disable', async () => { + const showEffect = jest.fn(); + const onClick = jest.fn(); + + const { container } = render( + + + +); + +const App = () => ( + + + + + + +); + +export default App; diff --git a/components/config-provider/index.en-US.md b/components/config-provider/index.en-US.md index 09e0f9dfc5..ca2662c228 100644 --- a/components/config-provider/index.en-US.md +++ b/components/config-provider/index.en-US.md @@ -43,6 +43,7 @@ Some components use dynamic style to support wave effect. You can config `csp` p Direction Component size Theme +Custom Wave prefixCls useConfig @@ -152,6 +153,7 @@ const { | tree | Set Tree common props | { className?: string, style?: React.CSSProperties } | - | 5.7.0 | | typography | Set Typography common props | { className?: string, style?: React.CSSProperties } | - | 5.7.0 | | upload | Set Upload common props | { className?: string, style?: React.CSSProperties } | - | 5.7.0 | +| wave | Config wave effect | { disabled?: boolean, showEffect?: (node: HTMLElement, info: { className, token, component }) => void } | - | 5.8.0 | ## FAQ diff --git a/components/config-provider/index.tsx b/components/config-provider/index.tsx index 623963f116..2f5d59964c 100644 --- a/components/config-provider/index.tsx +++ b/components/config-provider/index.tsx @@ -30,6 +30,7 @@ import type { PopupOverflow, Theme, ThemeConfig, + WaveConfig, } from './context'; import { ConfigConsumer, ConfigContext, defaultIconPrefixCls } from './context'; import { registerTheme } from './cssVariables'; @@ -186,6 +187,11 @@ export interface ConfigProviderProps { tree?: ComponentStyleConfig; colorPicker?: ComponentStyleConfig; datePicker?: ComponentStyleConfig; + + /** + * Wave is special component which only patch on the effect of component interaction. + */ + wave?: WaveConfig; } interface ProviderChildrenProps extends ConfigProviderProps { @@ -322,6 +328,7 @@ const ProviderChildren: React.FC = (props) => { tree, colorPicker, datePicker, + wave, } = props; // =================================== Warning =================================== @@ -420,6 +427,7 @@ const ProviderChildren: React.FC = (props) => { tree, colorPicker, datePicker, + wave, }; const config = { diff --git a/components/config-provider/index.zh-CN.md b/components/config-provider/index.zh-CN.md index 9bb937e72a..b981b022e4 100644 --- a/components/config-provider/index.zh-CN.md +++ b/components/config-provider/index.zh-CN.md @@ -44,6 +44,7 @@ export default Demo; 方向 组件尺寸 主题 +自定义波纹 前缀 useConfig @@ -154,6 +155,7 @@ const { | tree | 设置 Tree 组件的通用属性 | { className?: string, style?: React.CSSProperties } | - | 5.7.0 | | typography | 设置 Typography 组件的通用属性 | { className?: string, style?: React.CSSProperties } | - | 5.7.0 | | upload | 设置 Upload 组件的通用属性 | { className?: string, style?: React.CSSProperties } | - | 5.7.0 | +| wave | 设置水波纹特效 | { disabled?: boolean, showEffect?: (node: HTMLElement, info: { className, token, component }) => void } | - | 5.8.0 | ## FAQ diff --git a/components/switch/index.tsx b/components/switch/index.tsx index 7833b1efe1..7086be661f 100755 --- a/components/switch/index.tsx +++ b/components/switch/index.tsx @@ -97,7 +97,7 @@ const Switch = React.forwardRef((props, ref) => const mergedStyle: React.CSSProperties = { ...SWITCH?.style, ...style }; return wrapSSR( - + = ( ); - return wrapSSR(isNeedWave ? {tagNode} : tagNode); + return wrapSSR(isNeedWave ? {tagNode} : tagNode); }; const Tag = React.forwardRef(InternalTag) as TagType;