Skip to content

Commit 495d444

Browse files
authored
Merge pull request #72 from MrPeak/master
feat: Add new trigger type --- 'contextmenu'
2 parents c2d58f9 + 8cb5194 commit 495d444

File tree

3 files changed

+105
-29
lines changed

3 files changed

+105
-29
lines changed

examples/simple.js

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -192,6 +192,15 @@ class Test extends React.Component {
192192
/>
193193
click
194194
</label>
195+
<label>
196+
<input
197+
value="contextMenu"
198+
checked={!!trigger.contextMenu}
199+
type="checkbox"
200+
onChange={this.onTriggerChange}
201+
/>
202+
contextMenu
203+
</label>
195204
&nbsp;&nbsp;&nbsp;&nbsp;
196205
<label>
197206
<input

src/index.js

Lines changed: 47 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ function returnDocument() {
2121
}
2222

2323
const ALL_HANDLERS = ['onClick', 'onMouseDown', 'onTouchStart', 'onMouseEnter',
24-
'onMouseLeave', 'onFocus', 'onBlur'];
24+
'onMouseLeave', 'onFocus', 'onBlur', 'onContextMenu'];
2525

2626
const IS_REACT_16 = !!createPortal;
2727

@@ -173,7 +173,7 @@ const Trigger = createReactClass({
173173
// https://github.com/react-component/trigger/issues/50
174174
if (state.popupVisible) {
175175
let currentDocument;
176-
if (!this.clickOutsideHandler && this.isClickToHide()) {
176+
if (!this.clickOutsideHandler && (this.isClickToHide() || this.isContextMenuToShow())) {
177177
currentDocument = props.getDocument();
178178
this.clickOutsideHandler = addEventListener(currentDocument,
179179
'mousedown', this.onDocumentClick);
@@ -187,6 +187,17 @@ const Trigger = createReactClass({
187187
this.touchOutsideHandler = addEventListener(currentDocument,
188188
'click', this.onDocumentClick);
189189
}
190+
// close popup when trigger type contains 'onContextMenu' and document is scrolling.
191+
if (!this.contextMenuOutsideHandler1 && this.isContextMenuToShow()) {
192+
currentDocument = currentDocument || props.getDocument();
193+
this.contextMenuOutsideHandler1 = addEventListener(currentDocument,
194+
'scroll', this.onContextMenuClose);
195+
}
196+
// close popup when trigger type contains 'onContextMenu' and window is blur.
197+
if (!this.contextMenuOutsideHandler2 && this.isContextMenuToShow()) {
198+
this.contextMenuOutsideHandler2 = addEventListener(window,
199+
'blur', this.onContextMenuClose);
200+
}
190201
return;
191202
}
192203

@@ -252,6 +263,18 @@ const Trigger = createReactClass({
252263
}
253264
},
254265

266+
onContextMenu(e) {
267+
e.preventDefault();
268+
this.fireEvents('onContextMenu', e);
269+
this.setPopupVisible(true);
270+
},
271+
272+
onContextMenuClose() {
273+
if (this.isContextMenuToShow()) {
274+
this.close();
275+
}
276+
},
277+
255278
onClick(event) {
256279
this.fireEvents('onClick', event);
257280
// focus will trigger click
@@ -412,6 +435,16 @@ const Trigger = createReactClass({
412435
this.clickOutsideHandler = null;
413436
}
414437

438+
if (this.contextMenuOutsideHandler1) {
439+
this.contextMenuOutsideHandler1.remove();
440+
this.contextMenuOutsideHandler1 = null;
441+
}
442+
443+
if (this.contextMenuOutsideHandler2) {
444+
this.contextMenuOutsideHandler2.remove();
445+
this.contextMenuOutsideHandler2 = null;
446+
}
447+
415448
if (this.touchOutsideHandler) {
416449
this.touchOutsideHandler.remove();
417450
this.touchOutsideHandler = null;
@@ -432,6 +465,11 @@ const Trigger = createReactClass({
432465
return action.indexOf('click') !== -1 || showAction.indexOf('click') !== -1;
433466
},
434467

468+
isContextMenuToShow() {
469+
const { action, showAction } = this.props;
470+
return action.indexOf('contextMenu') !== -1 || showAction.indexOf('contextMenu') !== -1;
471+
},
472+
435473
isClickToHide() {
436474
const { action, hideAction } = this.props;
437475
return action.indexOf('click') !== -1 || hideAction.indexOf('click') !== -1;
@@ -489,6 +527,13 @@ const Trigger = createReactClass({
489527
const children = props.children;
490528
const child = React.Children.only(children);
491529
const newChildProps = { key: 'trigger' };
530+
531+
if (this.isContextMenuToShow()) {
532+
newChildProps.onContextMenu = this.onContextMenu;
533+
} else {
534+
newChildProps.onContextMenu = this.createTwoChains('onContextMenu');
535+
}
536+
492537
if (this.isClickToHide() || this.isClickToShow()) {
493538
newChildProps.onClick = this.onClick;
494539
newChildProps.onMouseDown = this.onMouseDown;

tests/index.js

Lines changed: 49 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -215,6 +215,28 @@ describe('rc-trigger', function main() {
215215
}], done);
216216
});
217217

218+
it('contextMenu works', done => {
219+
const trigger = ReactDOM.render((
220+
<Trigger
221+
action={['contextMenu']}
222+
popupAlign={placementAlignMap.left}
223+
popup={<strong>trigger</strong>}
224+
>
225+
<div className="target">contextMenu</div>
226+
</Trigger>
227+
), div);
228+
229+
const target = scryRenderedDOMComponentsWithClass(trigger, 'target')[0];
230+
// can not simulate mouseenter
231+
Simulate.contextMenu(target);
232+
async.series([timeout(200), (next) => {
233+
const popupDomNode = trigger.getPopupDomNode();
234+
expect(popupDomNode).to.be.ok();
235+
expect($(popupDomNode).css('display')).to.be('block');
236+
next();
237+
}], done);
238+
});
239+
218240
it('afterPopupVisibleChange can be triggered', (done) => {
219241
let triggered = 0;
220242
const trigger = ReactDOM.render((
@@ -601,33 +623,33 @@ describe('rc-trigger', function main() {
601623
const domNode = ReactDOM.findDOMNode(trigger);
602624
Simulate.click(domNode);
603625
async.series([timeout(100),
604-
(next) => {
605-
const popupDomNode = trigger.getPopupDomNode();
606-
expect(popupDomNode).to.be.ok();
607-
expect($(popupDomNode).css('opacity')).not.to.be('1');
608-
next();
609-
},
610-
timeout(500),
611-
(next) => {
612-
const popupDomNode = trigger.getPopupDomNode();
613-
expect(popupDomNode).to.be.ok();
614-
expect($(popupDomNode).css('opacity')).to.be('1');
615-
Simulate.click(domNode);
616-
next();
617-
},
618-
timeout(100),
619-
(next) => {
620-
const popupDomNode = trigger.getPopupDomNode();
621-
expect(popupDomNode).to.be.ok();
622-
expect($(popupDomNode).css('opacity')).not.to.be('1');
623-
next();
624-
},
625-
timeout(500),
626-
(next) => {
627-
const popupDomNode = trigger.getPopupDomNode();
628-
expect($(popupDomNode).css('display')).to.be('none');
629-
next();
630-
}],
626+
(next) => {
627+
const popupDomNode = trigger.getPopupDomNode();
628+
expect(popupDomNode).to.be.ok();
629+
expect($(popupDomNode).css('opacity')).not.to.be('1');
630+
next();
631+
},
632+
timeout(500),
633+
(next) => {
634+
const popupDomNode = trigger.getPopupDomNode();
635+
expect(popupDomNode).to.be.ok();
636+
expect($(popupDomNode).css('opacity')).to.be('1');
637+
Simulate.click(domNode);
638+
next();
639+
},
640+
timeout(100),
641+
(next) => {
642+
const popupDomNode = trigger.getPopupDomNode();
643+
expect(popupDomNode).to.be.ok();
644+
expect($(popupDomNode).css('opacity')).not.to.be('1');
645+
next();
646+
},
647+
timeout(500),
648+
(next) => {
649+
const popupDomNode = trigger.getPopupDomNode();
650+
expect($(popupDomNode).css('display')).to.be('none');
651+
next();
652+
}],
631653
done);
632654
});
633655
});

0 commit comments

Comments
 (0)