Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
30 changes: 17 additions & 13 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,9 @@ yarn add @untemps/svelte-use-tooltip
position: 'right',
animated: true,
animationEnterClassName: 'tooltip-enter',
animationLeaveClassName: 'tooltip-leave',
animationLeaveClassName: 'tooltip-leave',
enterDelay: 200,
leaveDelay: 400,
disabled: false
}
} class="tooltip__target">
Expand Down Expand Up @@ -102,18 +104,20 @@ yarn add @untemps/svelte-use-tooltip

## API

| Props | Type | Default | Description |
| -------------------------- | ------- | ------- | --------------------------------------------------------------------------------------------------------------- |
| `content` | string | null | Text content to display in the tooltip. |
| `contentSelector` | string | null | Selector of the content to display in the tooltip. |
| `contentClone` | boolean | null | Flag to clone the content to display in the tooltip. If false, the content is removed from its previous parent. |
| `contentActions` | object | null | Configuration of the tooltip actions (see [Content Actions](#content-actions)). |
| `containerClassName` | string | null | Class name to apply to the tooltip container. |
| `position` | string | 'top' | Position of the tooltip. Available values: 'top', 'bottom', 'left', 'right' |
| `animated` | boolean | false | Flag to animate tooltip transitions. |
| `animationEnterClassName` | string | null | Class name to apply to the tooltip enter transition. |
| `animationLeaveClassName` | string | null | Class name to apply to the tooltip leave transition. |
| `disabled` | boolean | false | Flag to disable the tooltip content. |
| Props | Type | Default | Description |
|---------------------------|---------|---------|-----------------------------------------------------------------------------------------------------------------|
| `content` | string | null | Text content to display in the tooltip. |
| `contentSelector` | string | null | Selector of the content to display in the tooltip. |
| `contentClone` | boolean | null | Flag to clone the content to display in the tooltip. If false, the content is removed from its previous parent. |
| `contentActions` | object | null | Configuration of the tooltip actions (see [Content Actions](#content-actions)). |
| `containerClassName` | string | null | Class name to apply to the tooltip container. |
| `position` | string | 'top' | Position of the tooltip. Available values: 'top', 'bottom', 'left', 'right' |
| `animated` | boolean | false | Flag to animate tooltip transitions. |
| `animationEnterClassName` | string | null | Class name to apply to the tooltip enter transition. |
| `animationLeaveClassName` | string | null | Class name to apply to the tooltip leave transition. |
| `enterDelay` | number | 0 | Delay before showing the tooltip. |
| `leaveDelay` | number | 0 | Delay before hiding the tooltip. |
| `disabled` | boolean | false | Flag to disable the tooltip content. |

### Content Actions

Expand Down
30 changes: 23 additions & 7 deletions dev/src/App.svelte
Original file line number Diff line number Diff line change
@@ -1,13 +1,15 @@
<script>
import { useTooltip } from '../../src'

let textContent = null
let tooltipTextContent = null
let useCustomTooltipClass = false
let tooltipPosition = 'top'
let isTooltipDisabled = false
let animateTooltip = false
let useCustomAnimationEnterClass = false
let useCustomAnimationLeaveClass = false
let tooltipEnterDelay = 200
let tooltipLeaveDelay = 200

const _onTooltipClick = (arg) => {
console.log(arg)
Expand All @@ -19,8 +21,8 @@
<div
use:useTooltip={{
position: tooltipPosition,
content: textContent,
contentSelector: !textContent?.length ? '.tooltip__content' : null,
content: tooltipTextContent,
contentSelector: !tooltipTextContent?.length ? '.tooltip__content' : null,
contentClone: true,
contentActions: {
'*': {
Expand All @@ -31,10 +33,12 @@
},
},
containerClassName: useCustomTooltipClass ? 'tooltip' : null,
disabled: isTooltipDisabled,
animated: animateTooltip,
animationEnterClassName: useCustomAnimationEnterClass ? 'tooltip-enter' : null,
animationLeaveClassName: useCustomAnimationLeaveClass ? 'tooltip-leave' : null,
enterDelay: tooltipEnterDelay,
leaveDelay: tooltipLeaveDelay,
disabled: isTooltipDisabled
}}
class="target"
>
Expand All @@ -51,7 +55,7 @@
<fieldset>
<label>
Tooltip Text Content:
<input type="text" bind:value={textContent} />
<input type="text" bind:value={tooltipTextContent} />
</label>
</fieldset>
<fieldset>
Expand Down Expand Up @@ -79,16 +83,28 @@
</fieldset>
<fieldset>
<label>
Use Custom Animation Enter Class:
Use Custom Tooltip Animation Enter Class:
<input type="checkbox" bind:checked={useCustomAnimationEnterClass} />
</label>
</fieldset>
<fieldset>
<label>
Use Custom Animation Leave Class:
Use Custom Tooltip Animation Leave Class:
<input type="checkbox" bind:checked={useCustomAnimationLeaveClass} />
</label>
</fieldset>
<fieldset>
<label>
Tooltip Enter Delay (ms):
<input type="number" step={100} min={0} bind:value={tooltipEnterDelay} />
</label>
</fieldset>
<fieldset>
<label>
Tooltip Leave Delay (ms):
<input type="number" step={100} min={0} bind:value={tooltipLeaveDelay} />
</label>
</fieldset>
<fieldset>
<label>
Disable Tooltip:
Expand Down
7 changes: 4 additions & 3 deletions jest/jest.setup.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,12 @@ import '@testing-library/jest-dom/extend-expect'

expect.extend({ toBeInTheDocument, toHaveAttribute, toHaveStyle })

global._createElement = (id = 'foo', className = 'bar', ariaLabel = 'gag') => {
global._createElement = (id = 'foo', attrs) => {
const el = document.createElement('div')
el.setAttribute('id', id)
el.setAttribute('class', className)
el.setAttribute('aria-label', ariaLabel)
for (let key in attrs) {
el.setAttribute(key, attrs[key])
}
document.body.appendChild(el)
return el
}
Expand Down
55 changes: 46 additions & 9 deletions src/Tooltip.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,9 @@ class Tooltip {

#observer = null
#events = []
#enterDelay = 0
#leaveDelay = 0
#delay = null

#tooltip = null

Expand Down Expand Up @@ -44,25 +47,30 @@ class Tooltip {
animated,
animationEnterClassName,
animationLeaveClassName,
enterDelay,
leaveDelay,
disabled
) {
this.#target = target
this.#content = content
this.#contentSelector = contentSelector
this.#contentClone = contentClone || false
this.#contentActions = contentActions
this.#contentClone = contentClone
this.#containerClassName = containerClassName
this.#position = position
this.#animated = animated
this.#position = position || 'top'
this.#animated = animated || false
this.#animationEnterClassName = animationEnterClassName || '__tooltip-enter'
this.#animationLeaveClassName = animationLeaveClassName || '__tooltip-leave'
this.#enterDelay = enterDelay || 0
this.#leaveDelay = leaveDelay || 0

this.#observer = new DOMObserver()

this.#target.title = ''
this.#target.setAttribute('style', 'position: relative')

this.#createTooltip()
this.#tooltip.classList.add(this.#containerClassName || '__tooltip', `__tooltip-${this.#position}`)

disabled ? this.#disableTarget() : this.#enableTarget()

Expand All @@ -79,31 +87,41 @@ class Tooltip {
animated,
animationEnterClassName,
animationLeaveClassName,
enterDelay,
leaveDelay,
disabled
) {
const hasContentChanged = contentSelector !== this.#contentSelector || content !== this.#content
const hasContainerClassNameChanged = containerClassName !== this.#containerClassName
const oldPosition = this.#position
const hasPositionChanged = position !== this.#position
const hasToDisableTarget = disabled && this.#boundEnterHandler
const hasToEnableTarget = !disabled && !this.#boundEnterHandler

this.#content = content
this.#contentSelector = contentSelector
this.#contentClone = contentClone
this.#contentClone = contentClone || false
this.#contentActions = contentActions
this.#containerClassName = containerClassName
this.#position = position
this.#animated = animated
this.#position = position || 'top'
this.#animated = animated || false
this.#animationEnterClassName = animationEnterClassName || '__tooltip-enter'
this.#animationLeaveClassName = animationLeaveClassName || '__tooltip-leave'
this.#enterDelay = enterDelay || 0
this.#leaveDelay = leaveDelay || 0

if (hasContentChanged) {
this.#removeTooltipFromTarget()

this.#createTooltip()
}

if (hasContainerClassNameChanged) {
this.#tooltip.className = this.#containerClassName || `__tooltip __tooltip-${this.#position}`
this.#tooltip.classList.add(this.#containerClassName || '__tooltip')
}

if (hasPositionChanged) {
this.#tooltip.classList.remove(`__tooltip-${oldPosition}`)
this.#tooltip.classList.add(`__tooltip-${this.#position}`)
}

if (hasToDisableTarget) {
Expand All @@ -118,6 +136,8 @@ class Tooltip {

this.#disableTarget()

this.#clearDelay()

this.#observer?.clear()
this.#observer = null
}
Expand All @@ -140,7 +160,6 @@ class Tooltip {

#createTooltip() {
this.#tooltip = document.createElement('div')
this.#tooltip.className = this.#containerClassName || `__tooltip __tooltip-${this.#position}`

if (this.#contentSelector) {
this.#observer
Expand Down Expand Up @@ -229,6 +248,22 @@ class Tooltip {
this.#events = []
}

#waitForDelay(delay) {
this.#clearDelay()
return new Promise(
(resolve) =>
(this.#delay = setTimeout(() => {
this.#clearDelay()
resolve()
}, delay))
)
}

#clearDelay() {
clearTimeout(this.#delay)
this.#delay = null
}

#transitionTooltip(direction) {
return new Promise((resolve) => {
let classToAdd, classToRemove
Expand Down Expand Up @@ -260,10 +295,12 @@ class Tooltip {
}

async #onTargetEnter() {
await this.#waitForDelay(this.#enterDelay)
await this.#appendTooltipToTarget()
}

async #onTargetLeave() {
await this.#waitForDelay(this.#leaveDelay)
await this.#removeTooltipFromTarget()
}
}
Expand Down
Loading