From 80b0f9eb43fdc6d11e96a51e96bed562479643da Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E4=BA=8C=E8=B4=A7=E6=9C=BA=E5=99=A8=E4=BA=BA?= Date: Thu, 3 Apr 2025 14:55:53 +0800 Subject: [PATCH 1/5] chore: init docs --- docs/demos/mobile.md | 8 ++++++ docs/examples/mobile.tsx | 58 ++++++++++++++++++++++++++++++++++++++++ 2 files changed, 66 insertions(+) create mode 100644 docs/demos/mobile.md create mode 100644 docs/examples/mobile.tsx diff --git a/docs/demos/mobile.md b/docs/demos/mobile.md new file mode 100644 index 00000000..298eb128 --- /dev/null +++ b/docs/demos/mobile.md @@ -0,0 +1,8 @@ +--- +title: Mobile +nav: + title: Demo + path: /demo +--- + + diff --git a/docs/examples/mobile.tsx b/docs/examples/mobile.tsx new file mode 100644 index 00000000..24420bdb --- /dev/null +++ b/docs/examples/mobile.tsx @@ -0,0 +1,58 @@ +import Trigger from '@rc-component/trigger'; +import React from 'react'; +import '../../assets/index.less'; + +const builtinPlacements = { + left: { + points: ['cr', 'cl'], + }, + right: { + points: ['cl', 'cr'], + }, + top: { + points: ['bc', 'tc'], + }, + bottom: { + points: ['tc', 'bc'], + }, + topLeft: { + points: ['bl', 'tl'], + }, + topRight: { + points: ['br', 'tr'], + }, + bottomRight: { + points: ['tr', 'br'], + }, + bottomLeft: { + points: ['tl', 'bl'], + }, +}; + +const Test = () => { + const [open1, setOpen1] = React.useState(false); + + return ( +
+
+ +

Hello World

+
+ } + mobile + > + Click Me + +
+ + ); +}; + +export default Test; From a24e4f7fe5ff821a29df7e2eb271130a2c286d55 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E4=BA=8C=E8=B4=A7=E6=9C=BA=E5=99=A8=E4=BA=BA?= Date: Thu, 3 Apr 2025 16:24:19 +0800 Subject: [PATCH 2/5] chore: mobile support --- assets/index/Mobile.less | 42 ++++++++++++++++++++++++---- docs/examples/mobile.tsx | 8 ++++-- src/Popup/Mask.tsx | 10 ++++++- src/Popup/index.tsx | 59 ++++++++++++++++++++++++++++++++-------- src/hooks/useAlign.ts | 19 +++++++++++-- src/index.tsx | 28 +++++++++++-------- src/interface.ts | 8 ------ 7 files changed, 133 insertions(+), 41 deletions(-) diff --git a/assets/index/Mobile.less b/assets/index/Mobile.less index 3c302caf..81cdd3b9 100644 --- a/assets/index/Mobile.less +++ b/assets/index/Mobile.less @@ -1,25 +1,55 @@ .@{triggerPrefixCls} { &-mobile { - transition: all 0.3s; position: fixed; left: 0; right: 0; bottom: 0; top: auto; - &-fade { - &-appear, - &-enter { - &-start { - transform: translateY(100%); + // Motion + &.raise { + &-enter, + &-appear { + transform: translateY(100%); + + &-active { + transition: all 0.3s; + transform: translateY(0); } } &-leave { + transform: translateY(0); + &-active { + transition: all 0.3s; transform: translateY(100%); } } } + + // Mask + &-mask { + &.fade { + &-enter, + &-appear { + opacity: 0; + + &-active { + transition: all 0.3s; + opacity: 1; + } + } + + &-leave { + opacity: 1; + + &-active { + transition: all 0.3s; + opacity: 0; + } + } + } + } } } diff --git a/docs/examples/mobile.tsx b/docs/examples/mobile.tsx index 24420bdb..3da78b74 100644 --- a/docs/examples/mobile.tsx +++ b/docs/examples/mobile.tsx @@ -42,11 +42,15 @@ const Test = () => { popupVisible={open1} onOpenChange={setOpen1} popup={ -
+

Hello World

} - mobile + mobile={{ + mask: true, + motion: { motionName: 'raise' }, + maskMotion: { motionName: 'fade' }, + }} > Click Me diff --git a/src/Popup/Mask.tsx b/src/Popup/Mask.tsx index 125fe458..4c376da4 100644 --- a/src/Popup/Mask.tsx +++ b/src/Popup/Mask.tsx @@ -11,6 +11,8 @@ export interface MaskProps { // Motion motion?: CSSMotionProps; + + mobile?: boolean; } export default function Mask(props: MaskProps) { @@ -21,6 +23,8 @@ export default function Mask(props: MaskProps) { mask, motion, + + mobile, } = props; if (!mask) { @@ -32,7 +36,11 @@ export default function Mask(props: MaskProps) { {({ className }) => (
)} diff --git a/src/Popup/index.tsx b/src/Popup/index.tsx index 167a4aa5..b617bb8b 100644 --- a/src/Popup/index.tsx +++ b/src/Popup/index.tsx @@ -11,6 +11,14 @@ import Arrow from './Arrow'; import Mask from './Mask'; import PopupContent from './PopupContent'; +export interface MobileConfig { + mask?: boolean; + /** Set popup motion. You can ref `rc-motion` for more info. */ + motion?: CSSMotionProps; + /** Set mask motion. You can ref `rc-motion` for more info. */ + maskMotion?: CSSMotionProps; +} + export interface PopupProps { prefixCls: string; className?: string; @@ -63,6 +71,9 @@ export interface PopupProps { stretch?: string; targetWidth?: number; targetHeight?: number; + + // Mobile + mobile?: MobileConfig; } const Popup = React.forwardRef((props, ref) => { @@ -95,6 +106,9 @@ const Popup = React.forwardRef((props, ref) => { motion, maskMotion, + // Mobile + mobile, + // Portal forceRender, getPopupContainer, @@ -126,6 +140,24 @@ const Popup = React.forwardRef((props, ref) => { // We can not remove holder only when motion finished. const isNodeVisible = open || keepDom; + // ========================= Mobile ========================= + const isMobile = !!mobile; + + // ========================== Mask ========================== + const [mergedMask, mergedMaskMotion, mergedPopupMotion] = React.useMemo< + [ + mask: boolean, + maskMotion: CSSMotionProps | undefined, + popupMotion: CSSMotionProps | undefined, + ] + >(() => { + if (mobile) { + return [mobile.mask, mobile.maskMotion, mobile.motion]; + } + + return [mask, maskMotion, motion]; + }, [mobile]); + // ======================= Container ======================== const getPopupContainerNeedParams = getPopupContainer?.length > 0; @@ -148,15 +180,17 @@ const Popup = React.forwardRef((props, ref) => { // >>>>> Offset const AUTO = 'auto' as const; - const offsetStyle: React.CSSProperties = { - left: '-1000vw', - top: '-1000vh', - right: AUTO, - bottom: AUTO, - }; + const offsetStyle: React.CSSProperties = isMobile + ? {} + : { + left: '-1000vw', + top: '-1000vh', + right: AUTO, + bottom: AUTO, + }; // Set align style - if (ready || !open) { + if (!isMobile && (ready || !open)) { const { points } = align; const dynamicInset = align.dynamicInset || (align as any)._experimental?.dynamicInset; @@ -209,8 +243,9 @@ const Popup = React.forwardRef((props, ref) => { prefixCls={prefixCls} open={open} zIndex={zIndex} - mask={mask} - motion={maskMotion} + mask={mergedMask} + motion={mergedMaskMotion} + mobile={isMobile} /> {(resizeObserverRef) => { @@ -222,7 +257,7 @@ const Popup = React.forwardRef((props, ref) => { removeOnLeave={false} forceRender={forceRender} leavedClassName={`${prefixCls}-hidden`} - {...motion} + {...mergedPopupMotion} onAppearPrepare={onPrepare} onEnterPrepare={onPrepare} visible={open} @@ -235,7 +270,9 @@ const Popup = React.forwardRef((props, ref) => { { className: motionClassName, style: motionStyle }, motionRef, ) => { - const cls = classNames(prefixCls, motionClassName, className); + const cls = classNames(prefixCls, motionClassName, className, { + [`${prefixCls}-mobile`]: isMobile, + }); return (
({ ready: false, @@ -134,7 +145,7 @@ export default function useAlign( const alignCountRef = React.useRef(0); const scrollerList = React.useMemo(() => { - if (!popupEle) { + if (!popupEle || mobile) { return []; } @@ -161,7 +172,7 @@ export default function useAlign( // ========================= Align ========================= const onAlign = useEvent(() => { - if (popupEle && target && open) { + if (popupEle && target && open && !mobile) { const popupElement = popupEle; const doc = popupElement.ownerDocument; @@ -659,10 +670,14 @@ export default function useAlign( const targetTop = targetRect.y; const targetBottom = targetTop + targetHeight; + /** Max left of the popup and target element */ const maxLeft = Math.max(popupLeft, targetLeft); + /** Min right of the popup and target element */ const minRight = Math.min(popupRight, targetRight); + /** The center X of popup & target cross area */ const xCenter = (maxLeft + minRight) / 2; + /** Arrow X of popup offset */ const nextArrowX = xCenter - popupLeft; const maxTop = Math.max(popupTop, targetTop); diff --git a/src/index.tsx b/src/index.tsx index 2b5a696e..6c1a1bc0 100644 --- a/src/index.tsx +++ b/src/index.tsx @@ -7,9 +7,8 @@ import { getShadowRoot } from '@rc-component/util/lib/Dom/shadow'; import useEvent from '@rc-component/util/lib/hooks/useEvent'; import useId from '@rc-component/util/lib/hooks/useId'; import useLayoutEffect from '@rc-component/util/lib/hooks/useLayoutEffect'; -import isMobile from '@rc-component/util/lib/isMobile'; import * as React from 'react'; -import Popup from './Popup'; +import Popup, { MobileConfig } from './Popup'; import TriggerWrapper from './TriggerWrapper'; import type { TriggerContextProps } from './context'; import TriggerContext from './context'; @@ -121,9 +120,12 @@ export interface TriggerProps { getTriggerDOMNode?: (node: React.ReactInstance) => HTMLElement; // // ========================== Mobile ========================== - // /** @private Bump fixed position at bottom in mobile. - // * This is internal usage currently, do not use in your prod */ - // mobile?: MobileConfig; + /** + * @private Bump fixed position at bottom in mobile. + * Will replace the config of root props. + * This is internal usage currently, do not use in your prod. + */ + mobile?: MobileConfig; } export function generateTrigger( @@ -190,6 +192,7 @@ export function generateTrigger( // Private getTriggerDOMNode, + mobile, ...restProps } = props; @@ -197,10 +200,7 @@ export function generateTrigger( const mergedAutoDestroy = autoDestroy || false; // =========================== Mobile =========================== - const [mobile, setMobile] = React.useState(false); - useLayoutEffect(() => { - setMobile(isMobile()); - }, []); + const isMobile = !!mobile; // ========================== Context =========================== const subPopupElements = React.useRef>({}); @@ -377,10 +377,11 @@ export function generateTrigger( builtinPlacements, popupAlign, onPopupAlign, + isMobile, ); const [showActions, hideActions] = useAction( - mobile, + isMobile, action, showAction, hideAction, @@ -670,7 +671,10 @@ export function generateTrigger( ref={setPopupRef} prefixCls={prefixCls} popup={popup} - className={classNames(popupClassName, alignedClassName)} + className={classNames( + popupClassName, + !isMobile && alignedClassName, + )} style={popupStyle} target={targetEle} onMouseEnter={onPopupMouseEnter} @@ -711,6 +715,8 @@ export function generateTrigger( stretch={stretch} targetWidth={targetWidth / scaleX} targetHeight={targetHeight / scaleY} + // Mobile + mobile={mobile} /> diff --git a/src/interface.ts b/src/interface.ts index 93fe2832..976e03d1 100644 --- a/src/interface.ts +++ b/src/interface.ts @@ -118,11 +118,3 @@ export interface Point { export interface CommonEventHandler { remove: () => void; } - -export interface MobileConfig { - /** Set popup motion. You can ref `rc-motion` for more info. */ - popupMotion?: CSSMotionProps; - popupClassName?: string; - popupStyle?: React.CSSProperties; - popupRender?: (originNode: React.ReactNode) => React.ReactNode; -} From f9c0f30be00142b1fa1f96e0fb72d7b08c056eb6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E4=BA=8C=E8=B4=A7=E6=9C=BA=E5=99=A8=E4=BA=BA?= Date: Thu, 3 Apr 2025 17:45:27 +0800 Subject: [PATCH 3/5] test: add test case --- docs/examples/mobile.tsx | 22 +++-- src/hooks/useAction.ts | 23 ++--- src/hooks/useWinClick.ts | 4 + src/index.tsx | 92 +++++++++++++++--- tests/__snapshots__/mobile.test.tsx.snap | 14 --- tests/mobile.test.tsx | 117 ++++++++++------------- 6 files changed, 153 insertions(+), 119 deletions(-) delete mode 100644 tests/__snapshots__/mobile.test.tsx.snap diff --git a/docs/examples/mobile.tsx b/docs/examples/mobile.tsx index 3da78b74..fd3ff799 100644 --- a/docs/examples/mobile.tsx +++ b/docs/examples/mobile.tsx @@ -36,21 +36,27 @@ const Test = () => {
+

Hello World

} - mobile={{ - mask: true, - motion: { motionName: 'raise' }, - maskMotion: { motionName: 'fade' }, - }} + // mobile={{ + // mask: true, + // motion: { motionName: 'raise' }, + // maskMotion: { motionName: 'fade' }, + // }} > Click Me
diff --git a/src/hooks/useAction.ts b/src/hooks/useAction.ts index 99d78bb3..87c6abd0 100644 --- a/src/hooks/useAction.ts +++ b/src/hooks/useAction.ts @@ -1,18 +1,19 @@ import * as React from 'react'; import type { ActionType } from '../interface'; -type ActionTypes = ActionType | ActionType[]; +type InternalActionType = ActionType | 'touch'; + +type ActionTypes = InternalActionType | InternalActionType[]; function toArray(val?: T | T[]) { return val ? (Array.isArray(val) ? val : [val]) : []; } export default function useAction( - mobile: boolean, action: ActionTypes, showAction?: ActionTypes, hideAction?: ActionTypes, -): [showAction: Set, hideAction: Set] { +): [showAction: Set, hideAction: Set] { return React.useMemo(() => { const mergedShowAction = toArray(showAction ?? action); const mergedHideAction = toArray(hideAction ?? action); @@ -20,18 +21,14 @@ export default function useAction( const showActionSet = new Set(mergedShowAction); const hideActionSet = new Set(mergedHideAction); - if (mobile) { - if (showActionSet.has('hover')) { - showActionSet.delete('hover'); - showActionSet.add('click'); - } + if (showActionSet.has('hover') && !showActionSet.has('click')) { + showActionSet.add('touch'); + } - if (hideActionSet.has('hover')) { - hideActionSet.delete('hover'); - hideActionSet.add('click'); - } + if (hideActionSet.has('hover') && !hideActionSet.has('click')) { + hideActionSet.add('touch'); } return [showActionSet, hideActionSet]; - }, [mobile, action, showAction, hideAction]); + }, [action, showAction, hideAction]); } diff --git a/src/hooks/useWinClick.ts b/src/hooks/useWinClick.ts index e14bb482..ce2afe12 100644 --- a/src/hooks/useWinClick.ts +++ b/src/hooks/useWinClick.ts @@ -3,6 +3,10 @@ import { warning } from '@rc-component/util/lib/warning'; import * as React from 'react'; import { getWin } from '../util'; +/** + * Close if click on the window. + * Return the function that click on the Popup element. + */ export default function useWinClick( open: boolean, clickToHide: boolean, diff --git a/src/index.tsx b/src/index.tsx index 6c1a1bc0..0316c8ae 100644 --- a/src/index.tsx +++ b/src/index.tsx @@ -123,6 +123,7 @@ export interface TriggerProps { /** * @private Bump fixed position at bottom in mobile. * Will replace the config of root props. + * This will directly trade as mobile view which will not check what real is. * This is internal usage currently, do not use in your prod. */ mobile?: MobileConfig; @@ -250,7 +251,19 @@ export function generateTrigger( // ========================== Children ========================== const child = React.Children.only(children) as React.ReactElement; const originChildProps = child?.props || {}; - const cloneProps: typeof originChildProps = {}; + const cloneProps: Pick< + React.HTMLAttributes, + | 'onClick' + | 'onTouchStart' + | 'onMouseEnter' + | 'onMouseLeave' + | 'onMouseMove' + | 'onPointerEnter' + | 'onPointerLeave' + | 'onFocus' + | 'onBlur' + | 'onContextMenu' + > = {}; const inPopupOrChild = useEvent((ele: EventTarget) => { const childDOM = targetEle; @@ -381,7 +394,6 @@ export function generateTrigger( ); const [showActions, hideActions] = useAction( - isMobile, action, showAction, hideAction, @@ -483,22 +495,52 @@ export function generateTrigger( // =========================== Action =========================== /** * Util wrapper for trigger action + * @param eventName Listen event name + * @param nextOpen Next open state after trigger + * @param delay Delay to trigger open change + * @param callback Callback if current event need additional action + * @param ignoreCheck Ignore current event if check return true */ function wrapperAction( eventName: string, nextOpen: boolean, delay?: number, - preEvent?: (event: Event) => void, + callback?: (event: Event) => void, + ignoreCheck?: () => boolean, ) { cloneProps[eventName] = (event: any, ...args: any[]) => { - preEvent?.(event); - triggerOpen(nextOpen, delay); + if (!ignoreCheck || !ignoreCheck()) { + callback?.(event); + triggerOpen(nextOpen, delay); + } // Pass to origin originChildProps[eventName]?.(event, ...args); }; } + // ======================= Action: Touch ======================== + const touchToShow = showActions.has('touch'); + const touchToHide = hideActions.has('touch'); + + /** Used for prevent `hover` event conflict with mobile env */ + const touchedRef = React.useRef(false); + + if (touchToShow || touchToHide) { + cloneProps.onTouchStart = (...args: any[]) => { + touchedRef.current = true; + + if (openRef.current && touchToHide) { + triggerOpen(false); + } else if (!openRef.current && touchToShow) { + triggerOpen(true); + } + + // Pass to origin + originChildProps.onTouchStart?.(...args); + }; + } + // ======================= Action: Click ======================== if (clickToShow || clickToHide) { cloneProps.onClick = ( @@ -514,13 +556,14 @@ export function generateTrigger( // Pass to origin originChildProps.onClick?.(event, ...args); + touchedRef.current = false; }; } // Click to hide is special action since click popup element should not hide const onPopupPointerDown = useWinClick( mergedOpen, - clickToHide, + clickToHide || touchToHide, targetEle, popupEle, mask, @@ -536,24 +579,31 @@ export function generateTrigger( let onPopupMouseEnter: React.MouseEventHandler; let onPopupMouseLeave: VoidFunction; + const ignoreMouseTrigger = () => { + return touchedRef.current; + }; + if (hoverToShow) { + const onMouseEnterCallback = (event: React.MouseEvent) => { + setMousePosByEvent(event); + }; + // Compatible with old browser which not support pointer event wrapperAction( 'onMouseEnter', true, mouseEnterDelay, - (event) => { - setMousePosByEvent(event); - }, + onMouseEnterCallback, + ignoreMouseTrigger, ); wrapperAction( 'onPointerEnter', true, mouseEnterDelay, - (event) => { - setMousePosByEvent(event); - }, + onMouseEnterCallback, + ignoreMouseTrigger, ); + onPopupMouseEnter = (event) => { // Only trigger re-open when popup is visible if ( @@ -567,15 +617,27 @@ export function generateTrigger( // Align Point if (alignPoint) { cloneProps.onMouseMove = (event: React.MouseEvent) => { - // setMousePosByEvent(event); originChildProps.onMouseMove?.(event); }; } } if (hoverToHide) { - wrapperAction('onMouseLeave', false, mouseLeaveDelay); - wrapperAction('onPointerLeave', false, mouseLeaveDelay); + wrapperAction( + 'onMouseLeave', + false, + mouseLeaveDelay, + undefined, + ignoreMouseTrigger, + ); + wrapperAction( + 'onPointerLeave', + false, + mouseLeaveDelay, + undefined, + ignoreMouseTrigger, + ); + onPopupMouseLeave = () => { triggerOpen(false, mouseLeaveDelay); }; diff --git a/tests/__snapshots__/mobile.test.tsx.snap b/tests/__snapshots__/mobile.test.tsx.snap deleted file mode 100644 index 32c08942..00000000 --- a/tests/__snapshots__/mobile.test.tsx.snap +++ /dev/null @@ -1,14 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[`Trigger.Mobile popupRender 1`] = ` -
-
- Light -
- -
-`; diff --git a/tests/mobile.test.tsx b/tests/mobile.test.tsx index a3269c2b..5fecdafc 100644 --- a/tests/mobile.test.tsx +++ b/tests/mobile.test.tsx @@ -1,7 +1,7 @@ import { act, fireEvent, render } from '@testing-library/react'; import isMobile from '@rc-component/util/lib/isMobile'; import React from 'react'; -import Trigger from '../src'; +import Trigger, { type TriggerProps } from '../src'; import { placementAlignMap } from './util'; jest.mock('@rc-component/util/lib/isMobile'); @@ -36,24 +36,26 @@ describe('Trigger.Mobile', () => { , ); - flush(); - expect(document.querySelector('.rc-trigger-popup')).toBeFalsy(); + const target = document.querySelector('.target'); - // Hover not work - fireEvent.mouseEnter(document.querySelector('.target')); flush(); expect(document.querySelector('.rc-trigger-popup')).toBeFalsy(); - // Click work - fireEvent.click(document.querySelector('.target')); + // Touch work + fireEvent.touchStart(target); + fireEvent.mouseEnter(target); + fireEvent.mouseLeave(target); flush(); expect(document.querySelector('.rc-trigger-popup')).toBeTruthy(); + + // Touch again + fireEvent.touchStart(target); + flush(); + expect(document.querySelector('.rc-trigger-popup-hidden')).toBeTruthy(); }); // ==================================================================================== - // ZombieJ: back when we plan to support mobile - - function getTrigger(props?: any) { + function getTrigger(props?: Partial) { return ( { ); } - it.skip('mobile config', () => { - const { container } = render( - getTrigger({ - mobile: { - popupClassName: 'mobile-popup', - popupStyle: { background: 'red' }, - }, - }), - ); - - fireEvent.click(container.querySelector('.target')); + describe('mobile config', () => { + it('enabled', () => { + const { container } = render( + getTrigger({ + mobile: {}, + }), + ); - expect(document.querySelector('.rc-trigger-popup')).toHaveClass( - 'mobile-popup', - ); + fireEvent.click(container.querySelector('.target')); - expect(document.querySelector('.rc-trigger-popup')).toHaveStyle({ - background: 'red', + expect(document.querySelector('.rc-trigger-popup')).toHaveClass( + 'rc-trigger-popup-mobile', + ); }); - }); - it.skip('popupRender', () => { - const { container } = render( - getTrigger({ - mobile: { - popupRender: (node) => ( - <> -
Light
- {node} - - ), - }, - }), - ); - - fireEvent.click(container.querySelector('.target')); - expect(document.querySelector('.rc-trigger-popup')).toMatchSnapshot(); - }); - - it.skip('click inside not close', () => { - const triggerRef = React.createRef(); - const { container } = render(getTrigger({ ref: triggerRef })); - fireEvent.click(container.querySelector('.target')); - expect(triggerRef.current.state.popupVisible).toBeTruthy(); - fireEvent.click(document.querySelector('.x-content')); - expect(triggerRef.current.state.popupVisible).toBeTruthy(); - - // Document click - act(() => { - fireEvent.mouseDown(document); + it('replace motion', () => { + render( + getTrigger({ + mobile: { + motion: { + motionName: 'bamboo', + }, + mask: true, + maskMotion: { + motionName: 'little', + }, + }, + popupVisible: true, + }), + ); + + expect(document.querySelector('.rc-trigger-popup-mobile')).toBeTruthy(); + expect( + document.querySelector('.rc-trigger-popup-mobile-mask'), + ).toBeTruthy(); + + expect(document.querySelector('.rc-trigger-popup')).toHaveClass('bamboo'); + expect(document.querySelector('.rc-trigger-popup-mask')).toHaveClass( + 'little', + ); }); - expect(triggerRef.current.state.popupVisible).toBeFalsy(); - }); - - it.skip('legacy array children', () => { - const { container } = render( - getTrigger({ - popup: [
Light
,
Bamboo
], - }), - ); - fireEvent.click(container.querySelector('.target')); - expect(document.querySelectorAll('.rc-trigger-popup-content')).toHaveLength( - 1, - ); }); }); From a8af69c6dff8b2990f464553e7ce379688bce727 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E4=BA=8C=E8=B4=A7=E6=9C=BA=E5=99=A8=E4=BA=BA?= Date: Thu, 3 Apr 2025 18:03:15 +0800 Subject: [PATCH 4/5] chore: fix lint --- .eslintrc.js | 5 +++++ package.json | 2 +- src/index.tsx | 2 +- src/interface.ts | 2 -- 4 files changed, 7 insertions(+), 4 deletions(-) diff --git a/.eslintrc.js b/.eslintrc.js index 7e19ab39..15621baf 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -10,5 +10,10 @@ module.exports = { 'react/sort-comp': 0, 'jsx-a11y/label-has-for': 0, 'jsx-a11y/label-has-associated-control': 0, + '@typescript-eslint/consistent-indexed-object-style': 0, + '@typescript-eslint/no-parameter-properties': 0, + '@typescript-eslint/ban-types': 0, + '@typescript-eslint/type-annotation-spacing': 0, + '@typescript-eslint/no-throw-literal': 0, }, }; diff --git a/package.json b/package.json index 9920cf1c..1996c6d9 100644 --- a/package.json +++ b/package.json @@ -61,7 +61,7 @@ "react": "^18.0.0", "react-dom": "^18.0.0", "regenerator-runtime": "^0.14.0", - "typescript": "^5.1.6" + "typescript": "~5.1.6" }, "dependencies": { "@rc-component/motion": "^1.1.4", diff --git a/src/index.tsx b/src/index.tsx index 0316c8ae..a2e35c4b 100644 --- a/src/index.tsx +++ b/src/index.tsx @@ -8,7 +8,7 @@ import useEvent from '@rc-component/util/lib/hooks/useEvent'; import useId from '@rc-component/util/lib/hooks/useId'; import useLayoutEffect from '@rc-component/util/lib/hooks/useLayoutEffect'; import * as React from 'react'; -import Popup, { MobileConfig } from './Popup'; +import Popup, { type MobileConfig } from './Popup'; import TriggerWrapper from './TriggerWrapper'; import type { TriggerContextProps } from './context'; import TriggerContext from './context'; diff --git a/src/interface.ts b/src/interface.ts index 976e03d1..0ff8cfd4 100644 --- a/src/interface.ts +++ b/src/interface.ts @@ -1,5 +1,3 @@ -import type { CSSMotionProps } from '@rc-component/motion'; - export type Placement = | 'top' | 'left' From 902ac19bd18fc73dbde15d460eebb7b1069bb461 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E4=BA=8C=E8=B4=A7=E6=9C=BA=E5=99=A8=E4=BA=BA?= Date: Thu, 3 Apr 2025 18:04:15 +0800 Subject: [PATCH 5/5] docs: update demo --- docs/examples/mobile.tsx | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/docs/examples/mobile.tsx b/docs/examples/mobile.tsx index fd3ff799..37d50f3b 100644 --- a/docs/examples/mobile.tsx +++ b/docs/examples/mobile.tsx @@ -52,11 +52,11 @@ const Test = () => {

Hello World

} - // mobile={{ - // mask: true, - // motion: { motionName: 'raise' }, - // maskMotion: { motionName: 'fade' }, - // }} + mobile={{ + mask: true, + motion: { motionName: 'raise' }, + maskMotion: { motionName: 'fade' }, + }} > Click Me