Skip to content

Commit c4d9e2f

Browse files
authored
feat: Trigger callbacks on tooltip enter and leave (#61)
1 parent 3a39cd3 commit c4d9e2f

File tree

7 files changed

+132
-54
lines changed

7 files changed

+132
-54
lines changed

README.md

Lines changed: 16 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -138,20 +138,22 @@ yarn add @untemps/svelte-use-tooltip
138138

139139
## API
140140

141-
| Props | Type | Default | Description |
142-
|---------------------------|---------|-------------------|-----------------------------------------------------------------------------------------------------------------|
143-
| `content` | string | null | Text content to display in the tooltip. |
144-
| `contentSelector` | string | null | Selector of the content to display in the tooltip. |
145-
| `contentActions` | object | null | Configuration of the tooltip actions (see [Content Actions](#content-actions)). |
146-
| `containerClassName` | string | '__tooltip' | Class name to apply to the tooltip container. |
147-
| `position` | string | 'top' | Position of the tooltip. Available values: 'top', 'bottom', 'left', 'right' |
148-
| `animated` | boolean | false | Flag to animate tooltip transitions. |
149-
| `animationEnterClassName` | string | '__tooltip-enter' | Class name to apply to the tooltip enter transition. |
150-
| `animationLeaveClassName` | string | '__tooltip-leave' | Class name to apply to the tooltip leave transition. |
151-
| `enterDelay` | number | 0 | Delay before showing the tooltip in milliseconds. |
152-
| `leaveDelay` | number | 0 | Delay before hiding the tooltip in milliseconds. |
153-
| `offset` | number | 10 | Distance between the tooltip and the target in pixels. |
154-
| `disabled` | boolean | false | Flag to disable the tooltip content. |
141+
| Props | Type | Default | Description |
142+
|---------------------------|---------|-------------------|---------------------------------------------------------------------------------|
143+
| `content` | string | null | Text content to display in the tooltip. |
144+
| `contentSelector` | string | null | Selector of the content to display in the tooltip. |
145+
| `contentActions` | object | null | Configuration of the tooltip actions (see [Content Actions](#content-actions)). |
146+
| `containerClassName` | string | '__tooltip' | Class name to apply to the tooltip container. |
147+
| `position` | string | 'top' | Position of the tooltip. Available values: 'top', 'bottom', 'left', 'right' |
148+
| `animated` | boolean | false | Flag to animate tooltip transitions. |
149+
| `animationEnterClassName` | string | '__tooltip-enter' | Class name to apply to the tooltip enter transition. |
150+
| `animationLeaveClassName` | string | '__tooltip-leave' | Class name to apply to the tooltip leave transition. |
151+
| `enterDelay` | number | 0 | Delay before showing the tooltip in milliseconds. |
152+
| `leaveDelay` | number | 0 | Delay before hiding the tooltip in milliseconds. |
153+
| `onEnter` | func | null | Callback triggered when the tooltip appears. |
154+
| `onLeave` | func | null | Callback triggered when the tooltip disappears. |
155+
| `offset` | number | 10 | Distance between the tooltip and the target in pixels. |
156+
| `disabled` | boolean | false | Flag to disable the tooltip content. |
155157

156158
### Content and Content Selector
157159

dev/src/App.svelte

Lines changed: 63 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -6,16 +6,30 @@
66
77
let showSettings = false
88
9-
let tooltipTextContent = null
10-
let useCustomTooltipClass = false
11-
let tooltipPosition = 'top'
12-
let isTooltipDisabled = false
13-
let animateTooltip = false
9+
let textContent = null
10+
let useCustomClass = false
11+
let position = 'top'
12+
let isDisabled = false
13+
let animate = false
1414
let useCustomAnimationEnterClass = false
1515
let useCustomAnimationLeaveClass = false
16-
let tooltipEnterDelay = 200
17-
let tooltipLeaveDelay = 200
18-
let tooltipOffset = 10
16+
let enterDelay = 200
17+
let leaveDelay = 200
18+
let triggerOnEnter = false
19+
let triggerOnLeave = false
20+
let offset = 10
21+
22+
const _onTooltipEnter = () => {
23+
if(triggerOnEnter) {
24+
alert('You\'ve entered the target')
25+
}
26+
}
27+
28+
const _onTooltipLeave = () => {
29+
if(triggerOnLeave) {
30+
alert('You\'ve left the target')
31+
}
32+
}
1933
2034
const _onTooltipClick = (arg, e) => {
2135
e.preventDefault()
@@ -204,20 +218,20 @@
204218
<form class="settings__form">
205219
<fieldset>
206220
<label>
207-
Tooltip Text Content:
208-
<input type="text" bind:value={tooltipTextContent}/>
221+
Text Content:
222+
<input type="text" bind:value={textContent}/>
209223
</label>
210224
</fieldset>
211225
<fieldset>
212226
<label>
213-
Use Custom Tooltip Class:
214-
<input type="checkbox" bind:checked={useCustomTooltipClass}/>
227+
Use Custom Class:
228+
<input type="checkbox" bind:checked={useCustomClass}/>
215229
</label>
216230
</fieldset>
217231
<fieldset>
218232
<label>
219-
Tooltip Position:
220-
<select bind:value={tooltipPosition}>
233+
Position:
234+
<select bind:value={position}>
221235
<option value="left">Left</option>
222236
<option value="right">Right</option>
223237
<option value="top">Top</option>
@@ -227,44 +241,56 @@
227241
</fieldset>
228242
<fieldset>
229243
<label>
230-
Animate tooltip:
231-
<input type="checkbox" bind:checked={animateTooltip}/>
244+
Animate:
245+
<input type="checkbox" bind:checked={animate}/>
232246
</label>
233247
</fieldset>
234248
<fieldset>
235249
<label>
236-
Use Custom Tooltip Animation Enter Class:
250+
Use Custom Animation Enter Class:
237251
<input type="checkbox" bind:checked={useCustomAnimationEnterClass}/>
238252
</label>
239253
</fieldset>
240254
<fieldset>
241255
<label>
242-
Use Custom Tooltip Animation Leave Class:
256+
Use Custom Animation Leave Class:
243257
<input type="checkbox" bind:checked={useCustomAnimationLeaveClass}/>
244258
</label>
245259
</fieldset>
246260
<fieldset>
247261
<label>
248-
Tooltip Enter Delay (ms):
249-
<input type="number" step={100} min={0} bind:value={tooltipEnterDelay}/>
262+
Enter Delay (ms):
263+
<input type="number" step={100} min={0} bind:value={enterDelay}/>
264+
</label>
265+
</fieldset>
266+
<fieldset>
267+
<label>
268+
Leave Delay (ms):
269+
<input type="number" step={100} min={0} bind:value={leaveDelay}/>
270+
</label>
271+
</fieldset>
272+
<fieldset>
273+
<label>
274+
Trigger callback on enter:
275+
<input type="checkbox" bind:checked={triggerOnEnter}/>
250276
</label>
251277
</fieldset>
252278
<fieldset>
253279
<label>
254-
Tooltip Leave Delay (ms):
255-
<input type="number" step={100} min={0} bind:value={tooltipLeaveDelay}/>
280+
Trigger callback on leave:
281+
<input type="checkbox" bind:checked={triggerOnLeave}/>
256282
</label>
257283
</fieldset>
258284
<fieldset>
259285
<label>
260-
Tooltip Offset (px):
261-
<input type="number" step={1} min={5} bind:value={tooltipOffset}/>
286+
Offset (px):
287+
<input type="number" step={1} min={5} bind:value={offset}/>
262288
</label>
263289
</fieldset>
264290
<fieldset>
265291
<label>
266-
Disable Tooltip:
267-
<input type="checkbox" bind:checked={isTooltipDisabled}/>
292+
Disable:
293+
<input type="checkbox" bind:checked={isDisabled}/>
268294
</label>
269295
</fieldset>
270296
</form>
@@ -276,9 +302,9 @@
276302
</button>
277303
<div
278304
use:useTooltip={{
279-
position: tooltipPosition,
280-
content: tooltipTextContent,
281-
contentSelector: !tooltipTextContent?.length ? '#tooltip__template' : null,
305+
position: position,
306+
content: textContent,
307+
contentSelector: !textContent?.length ? '#tooltip__template' : null,
282308
contentActions: {
283309
'*': {
284310
eventType: 'click',
@@ -287,14 +313,16 @@
287313
closeOnCallback: true,
288314
},
289315
},
290-
containerClassName: useCustomTooltipClass ? `tooltip tooltip-${tooltipPosition}` : null,
291-
animated: animateTooltip,
316+
containerClassName: useCustomClass ? `tooltip tooltip-${position}` : null,
317+
animated: animate,
292318
animationEnterClassName: useCustomAnimationEnterClass ? 'tooltip-enter' : null,
293319
animationLeaveClassName: useCustomAnimationLeaveClass ? 'tooltip-leave' : null,
294-
enterDelay: tooltipEnterDelay,
295-
leaveDelay: tooltipLeaveDelay,
296-
offset: tooltipOffset,
297-
disabled: isTooltipDisabled,
320+
enterDelay: enterDelay,
321+
leaveDelay: leaveDelay,
322+
onEnter: _onTooltipEnter,
323+
onLeave: _onTooltipLeave,
324+
offset: offset,
325+
disabled: isDisabled,
298326
}}
299327
class="target"
300328
>

package.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@
4040
"@testing-library/dom": "^8.11.3",
4141
"@testing-library/jest-dom": "^5.11.6",
4242
"@testing-library/svelte": "^3.0.3",
43-
"@untemps/utils": "^2.0.0-beta.1",
43+
"@untemps/utils": "^2.4.0",
4444
"babel-jest": "^28.0.0-alpha.11",
4545
"cross-env": "^7.0.3",
4646
"husky": "^7.0.4",
@@ -61,6 +61,7 @@
6161
},
6262
"dependencies": {
6363
"@untemps/dom-observer": "^2.0.0",
64+
"@untemps/utils": "^2.4.0",
6465
"svelte": "3.49.0"
6566
},
6667
"jest": {

src/Tooltip.js

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import { DOMObserver } from '@untemps/dom-observer'
2+
import { standby } from '@untemps/utils/async/standby'
23

34
class Tooltip {
45
static #instances = []
@@ -16,6 +17,8 @@ class Tooltip {
1617
#animationLeaveClassName = null
1718
#enterDelay = 0
1819
#leaveDelay = 0
20+
#onEnter = null
21+
#onLeave = null
1922
#offset = 10
2023

2124
#observer = null
@@ -46,6 +49,8 @@ class Tooltip {
4649
animationLeaveClassName,
4750
enterDelay,
4851
leaveDelay,
52+
onEnter,
53+
onLeave,
4954
offset,
5055
disabled
5156
) {
@@ -60,6 +65,8 @@ class Tooltip {
6065
this.#animationLeaveClassName = animationLeaveClassName || '__tooltip-leave'
6166
this.#enterDelay = enterDelay || 0
6267
this.#leaveDelay = leaveDelay || 0
68+
this.#onEnter = onEnter || null
69+
this.#onLeave = onLeave || null
6370
this.#offset = Math.max(offset || 10, 5)
6471

6572
this.#observer = new DOMObserver()
@@ -86,6 +93,8 @@ class Tooltip {
8693
animationLeaveClassName,
8794
enterDelay,
8895
leaveDelay,
96+
onEnter,
97+
onLeave,
8998
offset,
9099
disabled
91100
) {
@@ -109,6 +118,8 @@ class Tooltip {
109118
this.#animationLeaveClassName = animationLeaveClassName || '__tooltip-leave'
110119
this.#enterDelay = enterDelay || 0
111120
this.#leaveDelay = leaveDelay || 0
121+
this.#onEnter = onEnter || null
122+
this.#onLeave = onLeave || null
112123
this.#offset = Math.max(offset || 10, 5)
113124

114125
if (hasContentChanged || hasPositionChanged || hasOffsetChanged) {
@@ -366,13 +377,17 @@ class Tooltip {
366377
if (this.#target === e.target) {
367378
await this.#waitForDelay(this.#enterDelay)
368379
await this.#appendTooltipToTarget()
380+
await standby(0)
381+
this.#onEnter?.()
369382
}
370383
}
371384

372385
async #onTargetLeave(e) {
373386
if (this.#target === e.target || !this.#target.contains(e.target)) {
374387
await this.#waitForDelay(this.#leaveDelay)
375388
await this.#removeTooltipFromTarget()
389+
await standby(0)
390+
this.#onLeave?.()
376391
}
377392
}
378393

src/__tests__/useTooltip.test.js

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -421,6 +421,30 @@ describe('useTooltip', () => {
421421
})
422422
})
423423

424+
describe('useTooltip props: onEnter', () => {
425+
it('Triggers callback when entering tooltip', async () => {
426+
const onEnter = jest.fn()
427+
action = useTooltip(target, {
428+
...options,
429+
onEnter,
430+
})
431+
await _enter(target)
432+
await standby(0)
433+
expect(onEnter).toHaveBeenCalled()
434+
})
435+
436+
it('Triggers callback when entering tooltip after update', async () => {
437+
const onEnter = jest.fn()
438+
action = useTooltip(target, options)
439+
action.update({
440+
onEnter,
441+
})
442+
await _enter(target)
443+
await standby(0)
444+
expect(onEnter).toHaveBeenCalled()
445+
})
446+
})
447+
424448
describe('useTooltip props: leaveDelay', () => {
425449
it('Delays tooltip disappearance', async () => {
426450
action = useTooltip(target, {

src/useTooltip.js

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,8 @@ const useTooltip = (
1515
animationLeaveClassName,
1616
enterDelay,
1717
leaveDelay,
18+
onEnter,
19+
onLeave,
1820
offset,
1921
disabled,
2022
}
@@ -31,6 +33,8 @@ const useTooltip = (
3133
animationLeaveClassName,
3234
enterDelay,
3335
leaveDelay,
36+
onEnter,
37+
onLeave,
3438
offset,
3539
disabled
3640
)
@@ -47,6 +51,8 @@ const useTooltip = (
4751
animationLeaveClassName: newAnimationLeaveClassName,
4852
enterDelay: newEnterDelay,
4953
leaveDelay: newLeaveDelay,
54+
onEnter: newOnEnter,
55+
onLeave: newOnLeave,
5056
offset: newOffset,
5157
disabled: newDisabled,
5258
}) =>
@@ -61,6 +67,8 @@ const useTooltip = (
6167
newAnimationLeaveClassName,
6268
newEnterDelay,
6369
newLeaveDelay,
70+
newOnEnter,
71+
newOnLeave,
6472
newOffset,
6573
newDisabled
6674
),

yarn.lock

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2365,10 +2365,10 @@
23652365
resolved "https://registry.yarnpkg.com/@untemps/dom-observer/-/dom-observer-2.0.0.tgz#5b86b091f62905a6f18b0280bbf9067f2949716f"
23662366
integrity sha512-oZBj7j9hYSbuveNFvyPLamRfNh15KvGJC5pLGGpWVcO61EzISMESL0wudwP0UZETEu+0WfaJ16rGXtEZHsq+xw==
23672367

2368-
"@untemps/utils@^2.0.0-beta.1":
2369-
version "2.0.0-beta.1"
2370-
resolved "https://registry.yarnpkg.com/@untemps/utils/-/utils-2.0.0-beta.1.tgz#c7369b7efb06d0b7283e81e09d42207240dd9f8a"
2371-
integrity sha512-XwZzhIRcjDA2em+fGNLjbSqnq8UD5GtqqeENtuQAn73ztcGZCX/5eAhL9BK63wgnalwOO3wN+y9SfN+IXYjt4Q==
2368+
"@untemps/utils@^2.4.0":
2369+
version "2.4.0"
2370+
resolved "https://registry.yarnpkg.com/@untemps/utils/-/utils-2.4.0.tgz#56d47022f289d7c1578d587d33c922d494b921f1"
2371+
integrity sha512-SC8iU/J0HLXaDBd1NiXu4Tgal0WaeMIEDP4aaX/aotD8iwLi7vfihGwm970ATHZJphPheuPPZM9igsD4scr9og==
23722372
dependencies:
23732373
"@babel/runtime" "^7.17.8"
23742374

0 commit comments

Comments
 (0)