Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
31 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
48 changes: 48 additions & 0 deletions assets/index.less
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,54 @@
opacity: 0;
}
}

// =============== Float BG ===============
&-float-bg {
position: absolute;
z-index: 0;
box-sizing: border-box;
border: 1px solid red;
background: green;

&-hidden {
display: none;
}

&-visible {
transition: all 0.1s;
}
}

// Debug
&-unique-controlled {
border-color: rgba(0, 0, 0, 0.01) !important;
background: transparent !important;
z-index: 1;
}

// Motion Content
&-motion-content {
// Fade motion
&-fade-appear {
opacity: 0;
animation-duration: 0.3s;
animation-fill-mode: both;
animation-timing-function: cubic-bezier(0.55, 0, 0.55, 0.2);
}

&-fade-appear&-fade-appear-active {
animation-name: rcTriggerFadeIn;
}

@keyframes rcTriggerFadeIn {
0% {
opacity: 0;
}
100% {
opacity: 1;
}
}
}
}

@import './index/Mask';
Expand Down
8 changes: 8 additions & 0 deletions docs/demos/two-buttons.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
---
title: Moving Popup
nav:
title: Demo
path: /demo
---

<code src="../examples/two-buttons.tsx"></code>
140 changes: 140 additions & 0 deletions docs/examples/two-buttons.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,140 @@
import Trigger, { UniqueProvider } from '@rc-component/trigger';
import React, { useState } from 'react';
import '../../assets/index.less';

const LEAVE_DELAY = 0.2;

const builtinPlacements = {
left: {
points: ['cr', 'cl'],
offset: [-10, 0],
},
right: {
points: ['cl', 'cr'],
offset: [10, 0],
},
top: {
points: ['bc', 'tc'],
offset: [0, -10],
},
bottom: {
points: ['tc', 'bc'],
offset: [0, 10],
},
};

const MovingPopupDemo = () => {
const [useUniqueProvider, setUseUniqueProvider] = useState(true);
const [triggerControl, setTriggerControl] = useState('none'); // 'button1', 'button2', 'none'

const getVisible = (name: string) => {
if (triggerControl === 'none') {
return undefined;
}
if (triggerControl === name) {
return true;
}
return false;
};

const content = (
<div style={{ margin: 100 }}>
<div style={{ display: 'flex', gap: 20 }}>
<Trigger
mouseLeaveDelay={LEAVE_DELAY}
action={['hover']}
popupPlacement="top"
builtinPlacements={builtinPlacements}
popupVisible={getVisible('button1')}
popupMotion={{
motionName: 'rc-trigger-popup-zoom',
}}
popup={<div>这是左侧按钮的提示信息</div>}
popupStyle={{
border: '1px solid #ccc',
padding: 10,
background: 'white',
boxSizing: 'border-box',
}}
unique
>
<button type="button">左侧按钮</button>
</Trigger>

<Trigger
mouseLeaveDelay={LEAVE_DELAY}
action={['hover']}
popupPlacement="top"
builtinPlacements={builtinPlacements}
popupVisible={getVisible('button2')}
popupMotion={{
motionName: 'rc-trigger-popup-zoom',
}}
popup={<div>This is the tooltip for the right button</div>}
popupStyle={{
border: '1px solid #ccc',
padding: 10,
background: 'white',
boxSizing: 'border-box',
}}
unique
>
<button type="button">Right Button</button>
</Trigger>
</div>

<div style={{ marginBottom: 20 }}>
<label>
<input
type="checkbox"
checked={useUniqueProvider}
onChange={(e) => setUseUniqueProvider(e.target.checked)}
/>
使用 UniqueProvider
</label>
</div>

<div style={{ marginBottom: 20 }}>
<div>Trigger 控制:</div>
<label>
<input
type="radio"
name="triggerControl"
value="button1"
checked={triggerControl === 'button1'}
onChange={(e) => setTriggerControl(e.target.value)}
/>
Button 1 显示 Trigger
</label>
<label style={{ marginLeft: 20 }}>
<input
type="radio"
name="triggerControl"
value="button2"
checked={triggerControl === 'button2'}
onChange={(e) => setTriggerControl(e.target.value)}
/>
Button 2 显示 Trigger
</label>
<label style={{ marginLeft: 20 }}>
<input
type="radio"
name="triggerControl"
value="none"
checked={triggerControl === 'none'}
onChange={(e) => setTriggerControl(e.target.value)}
/>
都不受控 (Hover 控制)
</label>
</div>
</div>
);

return useUniqueProvider ? (
<UniqueProvider>{content}</UniqueProvider>
) : (
content
);
};

export default MovingPopupDemo;
79 changes: 38 additions & 41 deletions src/Popup/index.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
import classNames from 'classnames';
import type { CSSMotionProps } from '@rc-component/motion';
import CSSMotion from '@rc-component/motion';
import ResizeObserver from '@rc-component/resize-observer';
import ResizeObserver, {
type ResizeObserverProps,
} from '@rc-component/resize-observer';
import useLayoutEffect from '@rc-component/util/lib/hooks/useLayoutEffect';
import { composeRef } from '@rc-component/util/lib/ref';
import * as React from 'react';
Expand All @@ -10,6 +12,8 @@ import type { AlignType, ArrowPos, ArrowTypeOuter } from '../interface';
import Arrow from './Arrow';
import Mask from './Mask';
import PopupContent from './PopupContent';
import useOffsetStyle from '../hooks/useOffsetStyle';
import { useEvent } from '@rc-component/util';

export interface MobileConfig {
mask?: boolean;
Expand Down Expand Up @@ -58,6 +62,8 @@ export interface PopupProps {
autoDestroy?: boolean;
portal: React.ComponentType<any>;

children?: React.ReactElement;

// Align
ready: boolean;
offsetX: number;
Expand All @@ -72,6 +78,9 @@ export interface PopupProps {
targetWidth?: number;
targetHeight?: number;

// Resize
onResize?: ResizeObserverProps['onResize'];

// Mobile
mobile?: MobileConfig;
}
Expand Down Expand Up @@ -114,6 +123,7 @@ const Popup = React.forwardRef<HTMLDivElement, PopupProps>((props, ref) => {
getPopupContainer,
autoDestroy,
portal: Portal,
children,

zIndex,

Expand All @@ -130,12 +140,15 @@ const Popup = React.forwardRef<HTMLDivElement, PopupProps>((props, ref) => {
onAlign,
onPrepare,

// Resize
onResize,

stretch,
targetWidth,
targetHeight,
} = props;

const childNode = typeof popup === 'function' ? popup() : popup;
const popupContent = typeof popup === 'function' ? popup() : popup;

// We can not remove holder only when motion finished.
const isNodeVisible = open || keepDom;
Expand Down Expand Up @@ -172,48 +185,31 @@ const Popup = React.forwardRef<HTMLDivElement, PopupProps>((props, ref) => {
}
}, [show, getPopupContainerNeedParams, target]);

// ========================= Resize =========================
const onInternalResize: ResizeObserverProps['onResize'] = useEvent(
(size, ele) => {
onResize?.(size, ele);
onAlign();
},
);

// ========================= Styles =========================
const offsetStyle = useOffsetStyle(
isMobile,
ready,
open,
align,
offsetR,
offsetB,
offsetX,
offsetY,
);

// ========================= Render =========================
if (!show) {
return null;
}

// >>>>> Offset
const AUTO = 'auto' as const;

const offsetStyle: React.CSSProperties = isMobile
? {}
: {
left: '-1000vw',
top: '-1000vh',
right: AUTO,
bottom: AUTO,
};

// Set align style
if (!isMobile && (ready || !open)) {
const { points } = align;
const dynamicInset =
align.dynamicInset || (align as any)._experimental?.dynamicInset;
const alignRight = dynamicInset && points[0][1] === 'r';
const alignBottom = dynamicInset && points[0][0] === 'b';

if (alignRight) {
offsetStyle.right = offsetR;
offsetStyle.left = AUTO;
} else {
offsetStyle.left = offsetX;
offsetStyle.right = AUTO;
}

if (alignBottom) {
offsetStyle.bottom = offsetB;
offsetStyle.top = AUTO;
} else {
offsetStyle.top = offsetY;
offsetStyle.bottom = AUTO;
}
}

// >>>>> Misc
const miscStyle: React.CSSProperties = {};
if (stretch) {
Expand Down Expand Up @@ -247,7 +243,7 @@ const Popup = React.forwardRef<HTMLDivElement, PopupProps>((props, ref) => {
motion={mergedMaskMotion}
mobile={isMobile}
/>
<ResizeObserver onResize={onAlign} disabled={!open}>
<ResizeObserver onResize={onInternalResize} disabled={!open}>
{(resizeObserverRef) => {
return (
<CSSMotion
Expand Down Expand Up @@ -305,7 +301,7 @@ const Popup = React.forwardRef<HTMLDivElement, PopupProps>((props, ref) => {
/>
)}
<PopupContent cache={!open && !fresh}>
{childNode}
{popupContent}
</PopupContent>
</div>
);
Expand All @@ -314,6 +310,7 @@ const Popup = React.forwardRef<HTMLDivElement, PopupProps>((props, ref) => {
);
}}
</ResizeObserver>
{children}
</Portal>
);
});
Expand Down
Loading
Loading