Skip to content

Commit 59a589e

Browse files
authored
Merge pull request #526 from artdevgame/feature-listen-body
Feature: listen body events
2 parents c28d02b + f3e126a commit 59a589e

File tree

3 files changed

+143
-19
lines changed

3 files changed

+143
-19
lines changed

src/decorators/bodyMode.js

Lines changed: 104 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,104 @@
1+
/**
2+
* Util method to get effect
3+
*/
4+
import {checkStatus} from './customEvent'
5+
6+
const makeProxy = (e) => {
7+
const proxy = {}
8+
for (const key in e) {
9+
if (typeof e[key] === 'function') {
10+
proxy[key] = e[key].bind(e)
11+
} else {
12+
proxy[key] = e[key]
13+
}
14+
}
15+
return proxy
16+
}
17+
18+
const bodyListener = function (callback, options, e) {
19+
const {respectEffect = false, customEvent = false} = options
20+
const {id} = this.props
21+
22+
const tip = e.target.getAttribute('data-tip') || null
23+
const forId = e.target.getAttribute('data-for') || null
24+
25+
const target = e.target
26+
if (this.isCustomEvent(target) && !customEvent) {
27+
return
28+
}
29+
30+
const isTargetBelongsToTooltip =
31+
(id == null && forId == null) || forId === id
32+
33+
if (tip != null &&
34+
(!respectEffect || this.getEffect(target) === 'float') &&
35+
isTargetBelongsToTooltip
36+
) {
37+
const proxy = makeProxy(e)
38+
proxy.currentTarget = target
39+
callback(proxy)
40+
}
41+
}
42+
43+
const findCustomEvents = (targetArray, dataAttribute) => {
44+
const events = {}
45+
targetArray.forEach(target => {
46+
const event = target.getAttribute(dataAttribute)
47+
if (event) event.split(' ').forEach(event => events[event] = true)
48+
})
49+
50+
return events
51+
}
52+
53+
const getBody = () => document.getElementsByTagName('body')[0]
54+
55+
export default function (target) {
56+
target.prototype.isBodyMode = function () {
57+
return !!this.props.bodyMode
58+
}
59+
60+
target.prototype.bindBodyListener = function (targetArray) {
61+
const {event, eventOff, possibleCustomEvents,
62+
possibleCustomEventsOff} = this.state
63+
const body = getBody()
64+
65+
const customEvents = findCustomEvents(targetArray, 'data-event')
66+
const customEventsOff = findCustomEvents(targetArray, 'data-event-off')
67+
68+
if (event != null) customEvents[event] = true
69+
if (eventOff != null) customEventsOff[eventOff] = true
70+
possibleCustomEvents.split(' ').forEach(event => customEvents[event] = true)
71+
possibleCustomEventsOff.split(' ').forEach(event => customEventsOff[event] = true)
72+
73+
this.unbindBodyListener(body)
74+
75+
const listeners = this.bodyModeListeners = {}
76+
if (event == null) {
77+
listeners.mouseover = bodyListener.bind(this, this.showTooltip, {})
78+
listeners.mousemove = bodyListener.bind(this, this.updateTooltip, { respectEffect: true })
79+
listeners.mouseout = bodyListener.bind(this, this.hideTooltip, {})
80+
}
81+
82+
for (const event in customEvents) {
83+
listeners[event] = bodyListener.bind(this, (e) => {
84+
const targetEventOff = e.currentTarget.getAttribute('data-event-off') || eventOff
85+
checkStatus.call(this, targetEventOff, e)
86+
}, { customEvent: true })
87+
}
88+
for (const event in customEventsOff) {
89+
listeners[event] = bodyListener.bind(this, this.hideTooltip, { customEvent: true })
90+
}
91+
for (const event in listeners) {
92+
body.addEventListener(event, listeners[event])
93+
}
94+
}
95+
96+
target.prototype.unbindBodyListener = function (body) {
97+
body = body || getBody()
98+
99+
const listeners = this.bodyModeListeners
100+
for (const event in listeners) {
101+
body.removeEventListener(event, listeners[event])
102+
}
103+
}
104+
}

src/decorators/customEvent.js

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,11 +6,10 @@
66
* - `eventOff` {String}
77
*/
88

9-
const checkStatus = function (dataEventOff, e) {
9+
export const checkStatus = function (dataEventOff, e) {
1010
const {show} = this.state
1111
const {id} = this.props
12-
const dataIsCapture = e.currentTarget.getAttribute('data-iscapture')
13-
const isCapture = dataIsCapture && dataIsCapture === 'true' || this.props.isCapture
12+
const isCapture = this.isCapture(e.currentTarget)
1413
const currentItem = e.currentTarget.getAttribute('currentItem')
1514

1615
if (!isCapture) e.stopPropagation()

src/index.js

Lines changed: 37 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import windowListener from './decorators/windowListener'
1010
import customEvent from './decorators/customEvent'
1111
import isCapture from './decorators/isCapture'
1212
import getEffect from './decorators/getEffect'
13+
import bodyMode from './decorators/bodyMode'
1314
import trackRemoval from './decorators/trackRemoval'
1415

1516
/* Utils */
@@ -26,6 +27,7 @@ import cssStyle from './style'
2627
@customEvent
2728
@isCapture
2829
@getEffect
30+
@bodyMode
2931
@trackRemoval
3032
class ReactTooltip extends React.Component {
3133

@@ -58,6 +60,9 @@ class ReactTooltip extends React.Component {
5860
scrollHide: PropTypes.bool,
5961
resizeHide: PropTypes.bool,
6062
wrapper: PropTypes.string,
63+
bodyMode: PropTypes.bool,
64+
possibleCustomEvents: PropTypes.string,
65+
possibleCustomEventsOff: PropTypes.string,
6166
clickable: PropTypes.bool
6267
};
6368

@@ -93,6 +98,8 @@ class ReactTooltip extends React.Component {
9398
ariaProps: parseAria(props), // aria- and role attributes
9499
isEmptyTip: false,
95100
disable: false,
101+
possibleCustomEvents: props.possibleCustomEvents || '',
102+
possibleCustomEventsOff: props.possibleCustomEventsOff || '',
96103
originTooltip: null,
97104
isMultiline: false
98105
}
@@ -131,6 +138,7 @@ class ReactTooltip extends React.Component {
131138
if (insecure) {
132139
this.setStyleHeader() // Set the style to the <link>
133140
}
141+
134142
this.bindListener() // Bind listener for tooltip
135143
this.bindWindowEvents(resizeHide) // Bind global event for static method
136144
}
@@ -206,25 +214,34 @@ class ReactTooltip extends React.Component {
206214
let targetArray = this.getTargetArray(id)
207215

208216
targetArray.forEach(target => {
209-
const isCaptureMode = this.isCapture(target)
210-
const effect = this.getEffect(target)
211217
if (target.getAttribute('currentItem') === null) {
212218
target.setAttribute('currentItem', 'false')
213219
}
214220
this.unbindBasicListener(target)
215-
216221
if (this.isCustomEvent(target)) {
217-
this.customBindListener(target)
218-
return
219-
}
220-
221-
target.addEventListener('mouseenter', this.showTooltip, isCaptureMode)
222-
if (effect === 'float') {
223-
target.addEventListener('mousemove', this.updateTooltip, isCaptureMode)
222+
this.customUnbindListener(target)
224223
}
225-
target.addEventListener('mouseleave', this.hideTooltip, isCaptureMode)
226224
})
227225

226+
if (this.isBodyMode()) {
227+
this.bindBodyListener(targetArray)
228+
} else {
229+
targetArray.forEach(target => {
230+
const isCaptureMode = this.isCapture(target)
231+
const effect = this.getEffect(target)
232+
if (this.isCustomEvent(target)) {
233+
this.customBindListener(target)
234+
return
235+
}
236+
237+
target.addEventListener('mouseenter', this.showTooltip, isCaptureMode)
238+
if (effect === 'float') {
239+
target.addEventListener('mousemove', this.updateTooltip, isCaptureMode)
240+
}
241+
target.addEventListener('mouseleave', this.hideTooltip, isCaptureMode)
242+
})
243+
}
244+
228245
// Global event to hide tooltip
229246
if (globalEventOff) {
230247
window.removeEventListener(globalEventOff, this.hideTooltip)
@@ -240,11 +257,15 @@ class ReactTooltip extends React.Component {
240257
*/
241258
unbindListener () {
242259
const {id, globalEventOff} = this.props
243-
const targetArray = this.getTargetArray(id)
244-
targetArray.forEach(target => {
245-
this.unbindBasicListener(target)
246-
if (this.isCustomEvent(target)) this.customUnbindListener(target)
247-
})
260+
if (this.isBodyMode()) {
261+
this.unbindBodyListener()
262+
} else {
263+
const targetArray = this.getTargetArray(id)
264+
targetArray.forEach(target => {
265+
this.unbindBasicListener(target)
266+
if (this.isCustomEvent(target)) this.customUnbindListener(target)
267+
})
268+
}
248269

249270
if (globalEventOff) window.removeEventListener(globalEventOff, this.hideTooltip)
250271
this.unbindRemovalTracker()

0 commit comments

Comments
 (0)