Skip to content

Commit 9913515

Browse files
authored
fix: Popup element with stopPropagation will make popup close (#160)
* fix: Popup with stop mouseDown popup should not close popup * add test case * add engines
1 parent 4295808 commit 9913515

File tree

5 files changed

+245
-29
lines changed

5 files changed

+245
-29
lines changed

.prettierrc

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
{
2+
"endOfLine": "lf",
3+
"semi": true,
4+
"singleQuote": true,
5+
"tabWidth": 2,
6+
"trailingComma": "all"
7+
}

examples/click-nested.js

Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,90 @@
1+
/* eslint no-console:0 */
2+
3+
import React from 'react';
4+
import Trigger from '../src';
5+
import '../assets/index.less';
6+
7+
const builtinPlacements = {
8+
left: {
9+
points: ['cr', 'cl'],
10+
},
11+
right: {
12+
points: ['cl', 'cr'],
13+
},
14+
top: {
15+
points: ['bc', 'tc'],
16+
},
17+
bottom: {
18+
points: ['tc', 'bc'],
19+
},
20+
topLeft: {
21+
points: ['bl', 'tl'],
22+
},
23+
topRight: {
24+
points: ['br', 'tr'],
25+
},
26+
bottomRight: {
27+
points: ['tr', 'br'],
28+
},
29+
bottomLeft: {
30+
points: ['tl', 'bl'],
31+
},
32+
};
33+
34+
const popupBorderStyle = {
35+
border: '1px solid red',
36+
padding: 10,
37+
background: 'rgba(255, 0, 0, 0.1)',
38+
};
39+
40+
function saveRef(name, component) {
41+
this[name] = component;
42+
}
43+
44+
class Test extends React.Component {
45+
constructor(props) {
46+
super(props);
47+
48+
this.saveContainerRef = saveRef.bind(this, 'containerInstance');
49+
}
50+
51+
render() {
52+
return (
53+
<div style={{ margin: 200 }}>
54+
<div>
55+
<Trigger
56+
popupPlacement="right"
57+
action={['click']}
58+
builtinPlacements={builtinPlacements}
59+
popup={
60+
// Level 2
61+
<Trigger
62+
popupPlacement="right"
63+
action={['click']}
64+
builtinPlacements={builtinPlacements}
65+
popup={<div style={popupBorderStyle}>i am a click popup</div>}
66+
>
67+
<div style={popupBorderStyle}>
68+
i am a click popup{' '}
69+
<button
70+
type="button"
71+
onMouseDown={e => {
72+
e.stopPropagation();
73+
e.preventDefault();
74+
}}
75+
>
76+
I am preventPop
77+
</button>
78+
</div>
79+
</Trigger>
80+
}
81+
>
82+
<span>Click Me</span>
83+
</Trigger>
84+
</div>
85+
</div>
86+
);
87+
}
88+
}
89+
90+
export default Test;

package.json

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,9 @@
22
"name": "rc-trigger",
33
"version": "4.0.0-rc.1",
44
"description": "base abstract trigger component for react",
5+
"engines": {
6+
"node": "12.x"
7+
},
58
"keywords": [
69
"react",
710
"react-component",

src/index.tsx

Lines changed: 80 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,10 @@ import addEventListener from 'rc-util/lib/Dom/addEventListener';
77
import Portal from 'rc-util/lib/Portal';
88
import classNames from 'classnames';
99

10-
import { getAlignFromPlacement, getAlignPopupClassName } from './utils/alignUtil';
10+
import {
11+
getAlignFromPlacement,
12+
getAlignPopupClassName,
13+
} from './utils/alignUtil';
1114
import Popup from './Popup';
1215
import TriggerContext from './context';
1316
import {
@@ -106,7 +109,9 @@ interface TriggerState {
106109
/**
107110
* Internal usage. Do not use in your code since this will be removed.
108111
*/
109-
export function generateTrigger(PortalComponent: any): React.ComponentClass<TriggerProps> {
112+
export function generateTrigger(
113+
PortalComponent: any,
114+
): React.ComponentClass<TriggerProps> {
110115
class Trigger extends React.Component<TriggerProps, TriggerState> {
111116
static contextType = TriggerContext;
112117

@@ -193,7 +198,10 @@ export function generateTrigger(PortalComponent: any): React.ComponentClass<Trig
193198
// https://github.com/react-component/trigger/issues/50
194199
if (state.popupVisible) {
195200
let currentDocument;
196-
if (!this.clickOutsideHandler && (this.isClickToHide() || this.isContextMenuToShow())) {
201+
if (
202+
!this.clickOutsideHandler &&
203+
(this.isClickToHide() || this.isContextMenuToShow())
204+
) {
197205
currentDocument = props.getDocument();
198206
this.clickOutsideHandler = addEventListener(
199207
currentDocument,
@@ -242,7 +250,11 @@ export function generateTrigger(PortalComponent: any): React.ComponentClass<Trig
242250
onMouseEnter = e => {
243251
const { mouseEnterDelay } = this.props;
244252
this.fireEvents('onMouseEnter', e);
245-
this.delaySetPopupVisible(true, mouseEnterDelay, mouseEnterDelay ? null : e);
253+
this.delaySetPopupVisible(
254+
true,
255+
mouseEnterDelay,
256+
mouseEnterDelay ? null : e,
257+
);
246258
};
247259

248260
onMouseMove = e => {
@@ -346,7 +358,10 @@ export function generateTrigger(PortalComponent: any): React.ComponentClass<Trig
346358
event.preventDefault();
347359
}
348360
const nextVisible = !this.state.popupVisible;
349-
if ((this.isClickToHide() && !nextVisible) || (nextVisible && this.isClickToShow())) {
361+
if (
362+
(this.isClickToHide() && !nextVisible) ||
363+
(nextVisible && this.isClickToShow())
364+
) {
350365
this.setPopupVisible(!this.state.popupVisible, event);
351366
}
352367
};
@@ -371,15 +386,26 @@ export function generateTrigger(PortalComponent: any): React.ComponentClass<Trig
371386

372387
const { target } = event;
373388
const root = this.getRootDomNode();
374-
if (!contains(root, target) && !this.hasPopupMouseDown) {
389+
const popupNode = this.getPopupDomNode();
390+
if (
391+
!contains(root, target) &&
392+
!contains(popupNode, target) &&
393+
!this.hasPopupMouseDown
394+
) {
375395
this.close();
376396
}
377397
};
378398

379-
static getDerivedStateFromProps({ popupVisible }: TriggerProps, prevState: TriggerState) {
399+
static getDerivedStateFromProps(
400+
{ popupVisible }: TriggerProps,
401+
prevState: TriggerState,
402+
) {
380403
const newState: Partial<TriggerState> = {};
381404

382-
if (popupVisible !== undefined && prevState.popupVisible !== popupVisible) {
405+
if (
406+
popupVisible !== undefined &&
407+
prevState.popupVisible !== popupVisible
408+
) {
383409
newState.popupVisible = popupVisible;
384410
newState.prevPopupVisible = prevState.popupVisible;
385411
}
@@ -423,7 +449,14 @@ export function generateTrigger(PortalComponent: any): React.ComponentClass<Trig
423449
getPopupClassNameFromAlign,
424450
} = this.props;
425451
if (popupPlacement && builtinPlacements) {
426-
className.push(getAlignPopupClassName(builtinPlacements, prefixCls, align, alignPoint));
452+
className.push(
453+
getAlignPopupClassName(
454+
builtinPlacements,
455+
prefixCls,
456+
align,
457+
alignPoint,
458+
),
459+
);
427460
}
428461
if (getPopupClassNameFromAlign) {
429462
className.push(getPopupClassNameFromAlign(align));
@@ -435,7 +468,11 @@ export function generateTrigger(PortalComponent: any): React.ComponentClass<Trig
435468
const { props } = this;
436469
const { popupPlacement, popupAlign, builtinPlacements } = props;
437470
if (popupPlacement && builtinPlacements) {
438-
return getAlignFromPlacement(builtinPlacements, popupPlacement, popupAlign);
471+
return getAlignFromPlacement(
472+
builtinPlacements,
473+
popupPlacement,
474+
popupAlign,
475+
);
439476
}
440477
return popupAlign;
441478
}
@@ -614,37 +651,54 @@ export function generateTrigger(PortalComponent: any): React.ComponentClass<Trig
614651

615652
isClickToShow() {
616653
const { action, showAction } = this.props;
617-
return action.indexOf('click') !== -1 || showAction.indexOf('click') !== -1;
654+
return (
655+
action.indexOf('click') !== -1 || showAction.indexOf('click') !== -1
656+
);
618657
}
619658

620659
isContextMenuToShow() {
621660
const { action, showAction } = this.props;
622-
return action.indexOf('contextMenu') !== -1 || showAction.indexOf('contextMenu') !== -1;
661+
return (
662+
action.indexOf('contextMenu') !== -1 ||
663+
showAction.indexOf('contextMenu') !== -1
664+
);
623665
}
624666

625667
isClickToHide() {
626668
const { action, hideAction } = this.props;
627-
return action.indexOf('click') !== -1 || hideAction.indexOf('click') !== -1;
669+
return (
670+
action.indexOf('click') !== -1 || hideAction.indexOf('click') !== -1
671+
);
628672
}
629673

630674
isMouseEnterToShow() {
631675
const { action, showAction } = this.props;
632-
return action.indexOf('hover') !== -1 || showAction.indexOf('mouseEnter') !== -1;
676+
return (
677+
action.indexOf('hover') !== -1 ||
678+
showAction.indexOf('mouseEnter') !== -1
679+
);
633680
}
634681

635682
isMouseLeaveToHide() {
636683
const { action, hideAction } = this.props;
637-
return action.indexOf('hover') !== -1 || hideAction.indexOf('mouseLeave') !== -1;
684+
return (
685+
action.indexOf('hover') !== -1 ||
686+
hideAction.indexOf('mouseLeave') !== -1
687+
);
638688
}
639689

640690
isFocusToShow() {
641691
const { action, showAction } = this.props;
642-
return action.indexOf('focus') !== -1 || showAction.indexOf('focus') !== -1;
692+
return (
693+
action.indexOf('focus') !== -1 || showAction.indexOf('focus') !== -1
694+
);
643695
}
644696

645697
isBlurToHide() {
646698
const { action, hideAction } = this.props;
647-
return action.indexOf('focus') !== -1 || hideAction.indexOf('blur') !== -1;
699+
return (
700+
action.indexOf('focus') !== -1 || hideAction.indexOf('blur') !== -1
701+
);
648702
}
649703

650704
forcePopupAlign() {
@@ -658,7 +712,9 @@ export function generateTrigger(PortalComponent: any): React.ComponentClass<Trig
658712
}
659713

660714
fireEvents(type: string, e: Event) {
661-
const childCallback = (this.props.children as React.ReactElement).props[type];
715+
const childCallback = (this.props.children as React.ReactElement).props[
716+
type
717+
];
662718
if (childCallback) {
663719
childCallback(e);
664720
}
@@ -676,7 +732,9 @@ export function generateTrigger(PortalComponent: any): React.ComponentClass<Trig
676732
const { popupVisible } = this.state;
677733
const { children, forceRender, alignPoint, className } = this.props;
678734
const child = React.Children.only(children) as React.ReactElement;
679-
const newChildProps: HTMLAttributes<HTMLElement> & { key: string } = { key: 'trigger' };
735+
const newChildProps: HTMLAttributes<HTMLElement> & { key: string } = {
736+
key: 'trigger',
737+
};
680738

681739
if (this.isContextMenuToShow()) {
682740
newChildProps.onContextMenu = this.onContextMenu;
@@ -741,7 +799,9 @@ export function generateTrigger(PortalComponent: any): React.ComponentClass<Trig
741799
}
742800

743801
return (
744-
<TriggerContext.Provider value={{ onPopupMouseDown: this.onPopupMouseDown }}>
802+
<TriggerContext.Provider
803+
value={{ onPopupMouseDown: this.onPopupMouseDown }}
804+
>
745805
{trigger}
746806
{portal}
747807
</TriggerContext.Provider>

0 commit comments

Comments
 (0)