react-tooltip 5.1.2 → 5.1.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/react-tooltip.d.ts +15 -3
- package/package.json +1 -1
- package/src/App.tsx +0 -119
- package/src/components/Tooltip/Tooltip.tsx +0 -226
- package/src/components/Tooltip/TooltipTypes.d.ts +0 -47
- package/src/components/Tooltip/index.ts +0 -1
- package/src/components/Tooltip/styles.module.css +0 -62
- package/src/components/TooltipContent/TooltipContent.tsx +0 -8
- package/src/components/TooltipContent/TooltipContentTypes.d.ts +0 -3
- package/src/components/TooltipContent/index.ts +0 -1
- package/src/components/TooltipController/TooltipController.tsx +0 -187
- package/src/components/TooltipController/TooltipControllerTypes.d.ts +0 -46
- package/src/components/TooltipController/index.ts +0 -1
- package/src/components/TooltipProvider/TooltipProvider.tsx +0 -110
- package/src/components/TooltipProvider/TooltipProviderTypes.d.ts +0 -33
- package/src/components/TooltipProvider/TooltipWrapper.tsx +0 -48
- package/src/components/TooltipProvider/index.ts +0 -2
- package/src/index-dev.tsx +0 -16
- package/src/index.tsx +0 -4
- package/src/styles.module.css +0 -5
- package/src/test/__snapshots__/index.spec.js.snap +0 -102
- package/src/test/index.spec.js +0 -143
- package/src/tokens.css +0 -8
- package/src/utils/compute-positions-types.d.ts +0 -8
- package/src/utils/compute-positions.ts +0 -65
- package/src/utils/debounce.ts +0 -27
package/dist/react-tooltip.d.ts
CHANGED
|
@@ -12,6 +12,18 @@ type EventsType = 'hover' | 'click'
|
|
|
12
12
|
|
|
13
13
|
type PositionStrategy = 'absolute' | 'fixed'
|
|
14
14
|
|
|
15
|
+
type DataAttribute =
|
|
16
|
+
| 'place'
|
|
17
|
+
| 'content'
|
|
18
|
+
| 'html'
|
|
19
|
+
| 'variant'
|
|
20
|
+
| 'offset'
|
|
21
|
+
| 'wrapper'
|
|
22
|
+
| 'events'
|
|
23
|
+
| 'position-strategy'
|
|
24
|
+
| 'delay-show'
|
|
25
|
+
| 'delay-hide'
|
|
26
|
+
|
|
15
27
|
interface ITooltipController {
|
|
16
28
|
className?: string
|
|
17
29
|
classNameArrow?: string
|
|
@@ -48,8 +60,6 @@ declare module 'react' {
|
|
|
48
60
|
}
|
|
49
61
|
}
|
|
50
62
|
|
|
51
|
-
declare const TooltipController: ({ id, anchorId, content, html, className, classNameArrow, variant, place, offset, wrapper, children, events, positionStrategy, delayShow, delayHide, style, isOpen, setIsOpen, }: ITooltipController) => JSX.Element;
|
|
52
|
-
|
|
53
63
|
interface ITooltipWrapper {
|
|
54
64
|
tooltipId?: string
|
|
55
65
|
children: ReactNode
|
|
@@ -66,8 +76,10 @@ interface ITooltipWrapper {
|
|
|
66
76
|
delayHide?: ITooltipController['delayHide']
|
|
67
77
|
}
|
|
68
78
|
|
|
79
|
+
declare const TooltipController: ({ id, anchorId, content, html, className, classNameArrow, variant, place, offset, wrapper, children, events, positionStrategy, delayShow, delayHide, style, isOpen, setIsOpen, }: ITooltipController) => JSX.Element;
|
|
80
|
+
|
|
69
81
|
declare const TooltipProvider: React.FC<PropsWithChildren>;
|
|
70
82
|
|
|
71
83
|
declare const TooltipWrapper: ({ tooltipId, children, place, content, html, variant, offset, wrapper, events, positionStrategy, delayShow, delayHide, }: ITooltipWrapper) => JSX.Element;
|
|
72
84
|
|
|
73
|
-
export { TooltipController as Tooltip, TooltipProvider, TooltipWrapper };
|
|
85
|
+
export { ChildrenType, DataAttribute, EventsType, ITooltipController as ITooltip, ITooltipWrapper, PlacesType, PositionStrategy, TooltipController as Tooltip, TooltipProvider, TooltipWrapper, VariantType, WrapperType };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "react-tooltip",
|
|
3
|
-
"version": "5.1.
|
|
3
|
+
"version": "5.1.3",
|
|
4
4
|
"description": "react tooltip component",
|
|
5
5
|
"scripts": {
|
|
6
6
|
"dev": "node ./cli.js --env=development && node --max_old_space_size=2048 ./node_modules/rollup/dist/bin/rollup -c rollup.config.dev.js --watch",
|
package/src/App.tsx
DELETED
|
@@ -1,119 +0,0 @@
|
|
|
1
|
-
import { TooltipController as Tooltip } from 'components/TooltipController'
|
|
2
|
-
import { TooltipProvider, TooltipWrapper } from 'components/TooltipProvider'
|
|
3
|
-
import { useState } from 'react'
|
|
4
|
-
import styles from './styles.module.css'
|
|
5
|
-
|
|
6
|
-
function WithProviderMinimal() {
|
|
7
|
-
return (
|
|
8
|
-
<section style={{ marginTop: '100px' }}>
|
|
9
|
-
<p>
|
|
10
|
-
<TooltipWrapper place="bottom" content="Shared Global Tooltip">
|
|
11
|
-
<button>Minimal 1</button>
|
|
12
|
-
</TooltipWrapper>
|
|
13
|
-
<TooltipWrapper place="right" content="Shared Global Tooltip">
|
|
14
|
-
<button>Minimal 2</button>
|
|
15
|
-
</TooltipWrapper>
|
|
16
|
-
</p>
|
|
17
|
-
<Tooltip />
|
|
18
|
-
</section>
|
|
19
|
-
)
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
function WithProviderMultiple() {
|
|
23
|
-
return (
|
|
24
|
-
<section style={{ marginTop: '100px' }}>
|
|
25
|
-
<p>
|
|
26
|
-
<TooltipWrapper tooltipId="tooltip-1" place="bottom">
|
|
27
|
-
<button>Multiple 1</button>
|
|
28
|
-
</TooltipWrapper>
|
|
29
|
-
<TooltipWrapper tooltipId="tooltip-2" place="right">
|
|
30
|
-
<button>Multiple 2</button>
|
|
31
|
-
</TooltipWrapper>
|
|
32
|
-
</p>
|
|
33
|
-
<Tooltip id="tooltip-1" content="Tooltip 1" />
|
|
34
|
-
<Tooltip id="tooltip-2" content="Tooltip 2" />
|
|
35
|
-
</section>
|
|
36
|
-
)
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
function App() {
|
|
40
|
-
const [anchorId, setAnchorId] = useState('button')
|
|
41
|
-
const [isDarkOpen, setIsDarkOpen] = useState(false)
|
|
42
|
-
|
|
43
|
-
return (
|
|
44
|
-
<main className={styles['main']}>
|
|
45
|
-
<button
|
|
46
|
-
id="button"
|
|
47
|
-
aria-describedby="tooltip"
|
|
48
|
-
data-tooltip-content="My big tooltip content 1"
|
|
49
|
-
onClick={() => {
|
|
50
|
-
setAnchorId('button')
|
|
51
|
-
}}
|
|
52
|
-
>
|
|
53
|
-
My button
|
|
54
|
-
</button>
|
|
55
|
-
<Tooltip
|
|
56
|
-
place="bottom"
|
|
57
|
-
anchorId={anchorId}
|
|
58
|
-
// only shown if `data-tooltip-content` is unset
|
|
59
|
-
content={`Showing tooltip on ${anchorId}`}
|
|
60
|
-
isOpen={isDarkOpen}
|
|
61
|
-
setIsOpen={setIsDarkOpen}
|
|
62
|
-
/>
|
|
63
|
-
<Tooltip
|
|
64
|
-
place="top"
|
|
65
|
-
variant="success"
|
|
66
|
-
anchorId="button2"
|
|
67
|
-
isOpen={isDarkOpen}
|
|
68
|
-
setIsOpen={setIsDarkOpen}
|
|
69
|
-
/>
|
|
70
|
-
<Tooltip
|
|
71
|
-
place="top"
|
|
72
|
-
variant="info"
|
|
73
|
-
anchorId="button3"
|
|
74
|
-
isOpen={isDarkOpen}
|
|
75
|
-
setIsOpen={setIsDarkOpen}
|
|
76
|
-
/>
|
|
77
|
-
<Tooltip
|
|
78
|
-
place="right"
|
|
79
|
-
variant="info"
|
|
80
|
-
anchorId="button3"
|
|
81
|
-
content="My big tooltip content"
|
|
82
|
-
isOpen={isDarkOpen}
|
|
83
|
-
setIsOpen={setIsDarkOpen}
|
|
84
|
-
style={{ backgroundColor: '#ff00ff' }}
|
|
85
|
-
/>
|
|
86
|
-
|
|
87
|
-
<section style={{ marginTop: '100px' }}>
|
|
88
|
-
<p>
|
|
89
|
-
<button
|
|
90
|
-
id="button2"
|
|
91
|
-
data-tooltip-content="Hello World from a Tooltip 2"
|
|
92
|
-
onClick={() => {
|
|
93
|
-
setAnchorId('button2')
|
|
94
|
-
}}
|
|
95
|
-
>
|
|
96
|
-
Hover or focus me
|
|
97
|
-
</button>
|
|
98
|
-
<button
|
|
99
|
-
id="button3"
|
|
100
|
-
data-tooltip-content="Hello World from a Tooltip 3"
|
|
101
|
-
onClick={() => {
|
|
102
|
-
setAnchorId('button3')
|
|
103
|
-
}}
|
|
104
|
-
>
|
|
105
|
-
Hover or focus me 2
|
|
106
|
-
</button>
|
|
107
|
-
</p>
|
|
108
|
-
</section>
|
|
109
|
-
<TooltipProvider>
|
|
110
|
-
<WithProviderMinimal />
|
|
111
|
-
</TooltipProvider>
|
|
112
|
-
<TooltipProvider>
|
|
113
|
-
<WithProviderMultiple />
|
|
114
|
-
</TooltipProvider>
|
|
115
|
-
</main>
|
|
116
|
-
)
|
|
117
|
-
}
|
|
118
|
-
|
|
119
|
-
export default App
|
|
@@ -1,226 +0,0 @@
|
|
|
1
|
-
import { useEffect, useState, useRef } from 'react'
|
|
2
|
-
import classNames from 'classnames'
|
|
3
|
-
import debounce from 'utils/debounce'
|
|
4
|
-
import { TooltipContent } from 'components/TooltipContent'
|
|
5
|
-
import { useTooltip } from 'components/TooltipProvider'
|
|
6
|
-
import { computeTooltipPosition } from '../../utils/compute-positions'
|
|
7
|
-
import styles from './styles.module.css'
|
|
8
|
-
import type { ITooltip } from './TooltipTypes'
|
|
9
|
-
|
|
10
|
-
const Tooltip = ({
|
|
11
|
-
// props
|
|
12
|
-
id,
|
|
13
|
-
className,
|
|
14
|
-
classNameArrow,
|
|
15
|
-
variant = 'dark',
|
|
16
|
-
anchorId,
|
|
17
|
-
place = 'top',
|
|
18
|
-
offset = 10,
|
|
19
|
-
events = ['hover'],
|
|
20
|
-
positionStrategy = 'absolute',
|
|
21
|
-
wrapper: WrapperElement = 'div',
|
|
22
|
-
children = null,
|
|
23
|
-
delayShow = 0,
|
|
24
|
-
delayHide = 0,
|
|
25
|
-
style: externalStyles,
|
|
26
|
-
// props handled by controller
|
|
27
|
-
isHtmlContent = false,
|
|
28
|
-
content,
|
|
29
|
-
isOpen,
|
|
30
|
-
setIsOpen,
|
|
31
|
-
}: ITooltip) => {
|
|
32
|
-
const tooltipRef = useRef<HTMLElement>(null)
|
|
33
|
-
const tooltipArrowRef = useRef<HTMLDivElement>(null)
|
|
34
|
-
const tooltipShowDelayTimerRef = useRef<NodeJS.Timeout | null>(null)
|
|
35
|
-
const tooltipHideDelayTimerRef = useRef<NodeJS.Timeout | null>(null)
|
|
36
|
-
const [inlineStyles, setInlineStyles] = useState({})
|
|
37
|
-
const [inlineArrowStyles, setInlineArrowStyles] = useState({})
|
|
38
|
-
const [show, setShow] = useState<boolean>(false)
|
|
39
|
-
const [calculatingPosition, setCalculatingPosition] = useState(false)
|
|
40
|
-
const { anchorRefs, setActiveAnchor: setProviderActiveAnchor } = useTooltip()(id)
|
|
41
|
-
const [activeAnchor, setActiveAnchor] = useState<React.RefObject<HTMLElement>>({ current: null })
|
|
42
|
-
|
|
43
|
-
const handleShow = (value: boolean) => {
|
|
44
|
-
if (setIsOpen) {
|
|
45
|
-
setIsOpen(value)
|
|
46
|
-
} else if (isOpen === undefined) {
|
|
47
|
-
setShow(value)
|
|
48
|
-
}
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
const handleShowTooltipDelayed = () => {
|
|
52
|
-
if (tooltipShowDelayTimerRef.current) {
|
|
53
|
-
clearTimeout(tooltipShowDelayTimerRef.current)
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
tooltipShowDelayTimerRef.current = setTimeout(() => {
|
|
57
|
-
handleShow(true)
|
|
58
|
-
}, delayShow)
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
const handleHideTooltipDelayed = () => {
|
|
62
|
-
if (tooltipHideDelayTimerRef.current) {
|
|
63
|
-
clearTimeout(tooltipHideDelayTimerRef.current)
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
tooltipHideDelayTimerRef.current = setTimeout(() => {
|
|
67
|
-
handleShow(false)
|
|
68
|
-
}, delayHide)
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
const handleShowTooltip = (event?: Event) => {
|
|
72
|
-
if (!event) {
|
|
73
|
-
return
|
|
74
|
-
}
|
|
75
|
-
if (delayShow) {
|
|
76
|
-
handleShowTooltipDelayed()
|
|
77
|
-
} else {
|
|
78
|
-
handleShow(true)
|
|
79
|
-
}
|
|
80
|
-
setActiveAnchor((anchor) =>
|
|
81
|
-
anchor.current === event.target ? anchor : { current: event.target as HTMLElement },
|
|
82
|
-
)
|
|
83
|
-
setProviderActiveAnchor({ current: event.target as HTMLElement })
|
|
84
|
-
|
|
85
|
-
if (tooltipHideDelayTimerRef.current) {
|
|
86
|
-
clearTimeout(tooltipHideDelayTimerRef.current)
|
|
87
|
-
}
|
|
88
|
-
}
|
|
89
|
-
|
|
90
|
-
const handleHideTooltip = () => {
|
|
91
|
-
if (delayHide) {
|
|
92
|
-
handleHideTooltipDelayed()
|
|
93
|
-
} else {
|
|
94
|
-
handleShow(false)
|
|
95
|
-
}
|
|
96
|
-
|
|
97
|
-
if (tooltipShowDelayTimerRef.current) {
|
|
98
|
-
clearTimeout(tooltipShowDelayTimerRef.current)
|
|
99
|
-
}
|
|
100
|
-
}
|
|
101
|
-
|
|
102
|
-
const handleClickTooltipAnchor = () => {
|
|
103
|
-
if (setIsOpen) {
|
|
104
|
-
setIsOpen(!isOpen)
|
|
105
|
-
} else if (isOpen === undefined) {
|
|
106
|
-
setShow((currentValue) => !currentValue)
|
|
107
|
-
}
|
|
108
|
-
}
|
|
109
|
-
|
|
110
|
-
// debounce handler to prevent call twice when
|
|
111
|
-
// mouse enter and focus events being triggered toggether
|
|
112
|
-
const debouncedHandleShowTooltip = debounce(handleShowTooltip, 50)
|
|
113
|
-
const debouncedHandleHideTooltip = debounce(handleHideTooltip, 50)
|
|
114
|
-
|
|
115
|
-
useEffect(() => {
|
|
116
|
-
const elementRefs = new Set(anchorRefs)
|
|
117
|
-
|
|
118
|
-
const anchorById = document.querySelector(`[id='${anchorId}']`) as HTMLElement
|
|
119
|
-
if (anchorById) {
|
|
120
|
-
setActiveAnchor((anchor) =>
|
|
121
|
-
anchor.current === anchorById ? anchor : { current: anchorById },
|
|
122
|
-
)
|
|
123
|
-
elementRefs.add({ current: anchorById })
|
|
124
|
-
}
|
|
125
|
-
|
|
126
|
-
if (!elementRefs.size) {
|
|
127
|
-
// eslint-disable-next-line @typescript-eslint/no-empty-function
|
|
128
|
-
return () => {}
|
|
129
|
-
}
|
|
130
|
-
|
|
131
|
-
const enabledEvents: { event: string; listener: (event?: Event) => void }[] = []
|
|
132
|
-
|
|
133
|
-
if (events.find((event: string) => event === 'click')) {
|
|
134
|
-
enabledEvents.push({ event: 'click', listener: handleClickTooltipAnchor })
|
|
135
|
-
}
|
|
136
|
-
|
|
137
|
-
if (events.find((event: string) => event === 'hover')) {
|
|
138
|
-
enabledEvents.push(
|
|
139
|
-
{ event: 'mouseenter', listener: debouncedHandleShowTooltip },
|
|
140
|
-
{ event: 'mouseleave', listener: debouncedHandleHideTooltip },
|
|
141
|
-
{ event: 'focus', listener: debouncedHandleShowTooltip },
|
|
142
|
-
{ event: 'blur', listener: debouncedHandleHideTooltip },
|
|
143
|
-
)
|
|
144
|
-
}
|
|
145
|
-
|
|
146
|
-
enabledEvents.forEach(({ event, listener }) => {
|
|
147
|
-
elementRefs.forEach((ref) => {
|
|
148
|
-
ref.current?.addEventListener(event, listener)
|
|
149
|
-
})
|
|
150
|
-
})
|
|
151
|
-
|
|
152
|
-
return () => {
|
|
153
|
-
enabledEvents.forEach(({ event, listener }) => {
|
|
154
|
-
elementRefs.forEach((ref) => {
|
|
155
|
-
ref.current?.removeEventListener(event, listener)
|
|
156
|
-
})
|
|
157
|
-
})
|
|
158
|
-
}
|
|
159
|
-
}, [anchorRefs, anchorId, events, delayHide, delayShow])
|
|
160
|
-
|
|
161
|
-
useEffect(() => {
|
|
162
|
-
let elementReference = activeAnchor.current
|
|
163
|
-
if (anchorId) {
|
|
164
|
-
// `anchorId` element takes precedence
|
|
165
|
-
elementReference = document.querySelector(`[id='${anchorId}']`) as HTMLElement
|
|
166
|
-
}
|
|
167
|
-
setCalculatingPosition(true)
|
|
168
|
-
let mounted = true
|
|
169
|
-
computeTooltipPosition({
|
|
170
|
-
place,
|
|
171
|
-
offset,
|
|
172
|
-
elementReference,
|
|
173
|
-
tooltipReference: tooltipRef.current,
|
|
174
|
-
tooltipArrowReference: tooltipArrowRef.current,
|
|
175
|
-
strategy: positionStrategy,
|
|
176
|
-
}).then((computedStylesData) => {
|
|
177
|
-
if (!mounted) {
|
|
178
|
-
// invalidate computed positions after remount
|
|
179
|
-
return
|
|
180
|
-
}
|
|
181
|
-
setCalculatingPosition(false)
|
|
182
|
-
if (Object.keys(computedStylesData.tooltipStyles).length) {
|
|
183
|
-
setInlineStyles(computedStylesData.tooltipStyles)
|
|
184
|
-
}
|
|
185
|
-
if (Object.keys(computedStylesData.tooltipArrowStyles).length) {
|
|
186
|
-
setInlineArrowStyles(computedStylesData.tooltipArrowStyles)
|
|
187
|
-
}
|
|
188
|
-
})
|
|
189
|
-
return () => {
|
|
190
|
-
mounted = false
|
|
191
|
-
}
|
|
192
|
-
}, [show, isOpen, anchorId, activeAnchor, content, place, offset, positionStrategy])
|
|
193
|
-
|
|
194
|
-
useEffect(() => {
|
|
195
|
-
return () => {
|
|
196
|
-
if (tooltipShowDelayTimerRef.current) {
|
|
197
|
-
clearTimeout(tooltipShowDelayTimerRef.current)
|
|
198
|
-
}
|
|
199
|
-
if (tooltipHideDelayTimerRef.current) {
|
|
200
|
-
clearTimeout(tooltipHideDelayTimerRef.current)
|
|
201
|
-
}
|
|
202
|
-
}
|
|
203
|
-
}, [])
|
|
204
|
-
|
|
205
|
-
return (
|
|
206
|
-
<WrapperElement
|
|
207
|
-
id={id}
|
|
208
|
-
role="tooltip"
|
|
209
|
-
className={classNames(styles['tooltip'], styles[variant], className, {
|
|
210
|
-
[styles['show']]: !calculatingPosition && (isOpen || show),
|
|
211
|
-
[styles['fixed']]: positionStrategy === 'fixed',
|
|
212
|
-
})}
|
|
213
|
-
style={{ ...externalStyles, ...inlineStyles }}
|
|
214
|
-
ref={tooltipRef}
|
|
215
|
-
>
|
|
216
|
-
{children || (isHtmlContent ? <TooltipContent content={content as string} /> : content)}
|
|
217
|
-
<div
|
|
218
|
-
className={classNames(styles['arrow'], classNameArrow)}
|
|
219
|
-
style={inlineArrowStyles}
|
|
220
|
-
ref={tooltipArrowRef}
|
|
221
|
-
/>
|
|
222
|
-
</WrapperElement>
|
|
223
|
-
)
|
|
224
|
-
}
|
|
225
|
-
|
|
226
|
-
export default Tooltip
|
|
@@ -1,47 +0,0 @@
|
|
|
1
|
-
import type { ElementType, ReactNode, Element, CSSProperties } from 'react'
|
|
2
|
-
|
|
3
|
-
export type PlacesType = 'top' | 'right' | 'bottom' | 'left'
|
|
4
|
-
|
|
5
|
-
export type VariantType = 'dark' | 'light' | 'success' | 'warning' | 'error' | 'info'
|
|
6
|
-
|
|
7
|
-
export type WrapperType = ElementType | 'div' | 'span'
|
|
8
|
-
|
|
9
|
-
export type ChildrenType = Element | ElementType | ReactNode
|
|
10
|
-
|
|
11
|
-
export type EventsType = 'hover' | 'click'
|
|
12
|
-
|
|
13
|
-
export type PositionStrategy = 'absolute' | 'fixed'
|
|
14
|
-
|
|
15
|
-
export type DataAttribute =
|
|
16
|
-
| 'place'
|
|
17
|
-
| 'content'
|
|
18
|
-
| 'html'
|
|
19
|
-
| 'variant'
|
|
20
|
-
| 'offset'
|
|
21
|
-
| 'wrapper'
|
|
22
|
-
| 'events'
|
|
23
|
-
| 'position-strategy'
|
|
24
|
-
| 'delay-show'
|
|
25
|
-
| 'delay-hide'
|
|
26
|
-
|
|
27
|
-
export interface ITooltip {
|
|
28
|
-
className?: string
|
|
29
|
-
classNameArrow?: string
|
|
30
|
-
content?: string
|
|
31
|
-
html?: string
|
|
32
|
-
place?: PlacesType
|
|
33
|
-
offset?: number
|
|
34
|
-
id?: string
|
|
35
|
-
variant?: VariantType
|
|
36
|
-
anchorId?: string
|
|
37
|
-
isHtmlContent?: boolean
|
|
38
|
-
wrapper?: WrapperType
|
|
39
|
-
children?: ChildrenType
|
|
40
|
-
events?: EventsType[]
|
|
41
|
-
positionStrategy?: PositionStrategy
|
|
42
|
-
delayShow?: number
|
|
43
|
-
delayHide?: number
|
|
44
|
-
style?: CSSProperties
|
|
45
|
-
isOpen?: boolean
|
|
46
|
-
setIsOpen?: (value: boolean) => void
|
|
47
|
-
}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export { default as Tooltip } from './Tooltip'
|
|
@@ -1,62 +0,0 @@
|
|
|
1
|
-
.tooltip {
|
|
2
|
-
visibility: hidden;
|
|
3
|
-
width: max-content;
|
|
4
|
-
position: absolute;
|
|
5
|
-
top: 0;
|
|
6
|
-
left: 0;
|
|
7
|
-
padding: 8px 16px;
|
|
8
|
-
border-radius: 3px;
|
|
9
|
-
font-size: 90%;
|
|
10
|
-
pointer-events: none;
|
|
11
|
-
opacity: 0;
|
|
12
|
-
transition: opacity 0.3s ease-out;
|
|
13
|
-
will-change: opacity, visibility;
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
.fixed {
|
|
17
|
-
position: fixed;
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
.arrow {
|
|
21
|
-
position: absolute;
|
|
22
|
-
background: inherit;
|
|
23
|
-
width: 8px;
|
|
24
|
-
height: 8px;
|
|
25
|
-
transform: rotate(45deg);
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
.show {
|
|
29
|
-
visibility: visible;
|
|
30
|
-
opacity: 0.9;
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
/** Types variant **/
|
|
34
|
-
.dark {
|
|
35
|
-
background: var(--rt-color-dark);
|
|
36
|
-
color: var(--rt-color-white);
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
.light {
|
|
40
|
-
background-color: var(--rt-color-white);
|
|
41
|
-
color: var(--rt-color-dark);
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
.success {
|
|
45
|
-
background-color: var(--rt-color-success);
|
|
46
|
-
color: var(--rt-color-white);
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
.warning {
|
|
50
|
-
background-color: var(--rt-color-warning);
|
|
51
|
-
color: var(--rt-color-white);
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
.error {
|
|
55
|
-
background-color: var(--rt-color-error);
|
|
56
|
-
color: var(--rt-color-white);
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
.info {
|
|
60
|
-
background-color: var(--rt-color-info);
|
|
61
|
-
color: var(--rt-color-white);
|
|
62
|
-
}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export { default as TooltipContent } from './TooltipContent'
|
|
@@ -1,187 +0,0 @@
|
|
|
1
|
-
import { useEffect, useState } from 'react'
|
|
2
|
-
import { Tooltip } from 'components/Tooltip'
|
|
3
|
-
import type {
|
|
4
|
-
EventsType,
|
|
5
|
-
PositionStrategy,
|
|
6
|
-
PlacesType,
|
|
7
|
-
VariantType,
|
|
8
|
-
WrapperType,
|
|
9
|
-
DataAttribute,
|
|
10
|
-
ITooltip,
|
|
11
|
-
} from 'components/Tooltip/TooltipTypes'
|
|
12
|
-
import { useTooltip } from 'components/TooltipProvider'
|
|
13
|
-
import type { ITooltipController } from './TooltipControllerTypes'
|
|
14
|
-
|
|
15
|
-
const TooltipController = ({
|
|
16
|
-
id,
|
|
17
|
-
anchorId,
|
|
18
|
-
content,
|
|
19
|
-
html,
|
|
20
|
-
className,
|
|
21
|
-
classNameArrow,
|
|
22
|
-
variant = 'dark',
|
|
23
|
-
place = 'top',
|
|
24
|
-
offset = 10,
|
|
25
|
-
wrapper = 'div',
|
|
26
|
-
children = null,
|
|
27
|
-
events = ['hover'],
|
|
28
|
-
positionStrategy = 'absolute',
|
|
29
|
-
delayShow = 0,
|
|
30
|
-
delayHide = 0,
|
|
31
|
-
style,
|
|
32
|
-
isOpen,
|
|
33
|
-
setIsOpen,
|
|
34
|
-
}: ITooltipController) => {
|
|
35
|
-
const [tooltipContent, setTooltipContent] = useState(content || html)
|
|
36
|
-
const [tooltipPlace, setTooltipPlace] = useState(place)
|
|
37
|
-
const [tooltipVariant, setTooltipVariant] = useState(variant)
|
|
38
|
-
const [tooltipOffset, setTooltipOffset] = useState(offset)
|
|
39
|
-
const [tooltipDelayShow, setTooltipDelayShow] = useState(delayShow)
|
|
40
|
-
const [tooltipDelayHide, setTooltipDelayHide] = useState(delayHide)
|
|
41
|
-
const [tooltipWrapper, setTooltipWrapper] = useState<WrapperType>(wrapper)
|
|
42
|
-
const [tooltipEvents, setTooltipEvents] = useState(events)
|
|
43
|
-
const [tooltipPositionStrategy, setTooltipPositionStrategy] = useState(positionStrategy)
|
|
44
|
-
const [isHtmlContent, setIsHtmlContent] = useState(Boolean(html))
|
|
45
|
-
const { anchorRefs, activeAnchor } = useTooltip()(id)
|
|
46
|
-
|
|
47
|
-
const getDataAttributesFromAnchorElement = (elementReference: HTMLElement) => {
|
|
48
|
-
const dataAttributes = elementReference?.getAttributeNames().reduce((acc, name) => {
|
|
49
|
-
if (name.startsWith('data-tooltip-')) {
|
|
50
|
-
const parsedAttribute = name.replace(/^data-tooltip-/, '') as DataAttribute
|
|
51
|
-
acc[parsedAttribute] = elementReference?.getAttribute(name) ?? null
|
|
52
|
-
}
|
|
53
|
-
return acc
|
|
54
|
-
}, {} as Record<DataAttribute, string | null>)
|
|
55
|
-
|
|
56
|
-
return dataAttributes
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
const applyAllDataAttributesFromAnchorElement = (
|
|
60
|
-
dataAttributes: Record<string, string | null>,
|
|
61
|
-
) => {
|
|
62
|
-
const handleDataAttributes: Record<DataAttribute, (value: string | null) => void> = {
|
|
63
|
-
place: (value) => {
|
|
64
|
-
setTooltipPlace((value as PlacesType) ?? place)
|
|
65
|
-
},
|
|
66
|
-
content: (value) => {
|
|
67
|
-
setIsHtmlContent(false)
|
|
68
|
-
setTooltipContent(value ?? content)
|
|
69
|
-
},
|
|
70
|
-
html: (value) => {
|
|
71
|
-
setIsHtmlContent(!!value)
|
|
72
|
-
setTooltipContent(value ?? html ?? content)
|
|
73
|
-
},
|
|
74
|
-
variant: (value) => {
|
|
75
|
-
setTooltipVariant((value as VariantType) ?? variant)
|
|
76
|
-
},
|
|
77
|
-
offset: (value) => {
|
|
78
|
-
setTooltipOffset(value === null ? offset : Number(value))
|
|
79
|
-
},
|
|
80
|
-
wrapper: (value) => {
|
|
81
|
-
setTooltipWrapper((value as WrapperType) ?? 'div')
|
|
82
|
-
},
|
|
83
|
-
events: (value) => {
|
|
84
|
-
const parsed = value?.split(' ') as EventsType[]
|
|
85
|
-
setTooltipEvents(parsed ?? events)
|
|
86
|
-
},
|
|
87
|
-
'position-strategy': (value) => {
|
|
88
|
-
setTooltipPositionStrategy((value as PositionStrategy) ?? positionStrategy)
|
|
89
|
-
},
|
|
90
|
-
'delay-show': (value) => {
|
|
91
|
-
setTooltipDelayShow(value === null ? delayShow : Number(value))
|
|
92
|
-
},
|
|
93
|
-
'delay-hide': (value) => {
|
|
94
|
-
setTooltipDelayHide(value === null ? delayHide : Number(value))
|
|
95
|
-
},
|
|
96
|
-
}
|
|
97
|
-
// reset unset data attributes to default values
|
|
98
|
-
// without this, data attributes from the last active anchor will still be used
|
|
99
|
-
Object.values(handleDataAttributes).forEach((handler) => handler(null))
|
|
100
|
-
Object.entries(dataAttributes).forEach(([key, value]) => {
|
|
101
|
-
handleDataAttributes[key as DataAttribute]?.(value)
|
|
102
|
-
})
|
|
103
|
-
}
|
|
104
|
-
|
|
105
|
-
useEffect(() => {
|
|
106
|
-
if (content) {
|
|
107
|
-
setTooltipContent(content)
|
|
108
|
-
}
|
|
109
|
-
if (html) {
|
|
110
|
-
setTooltipContent(html)
|
|
111
|
-
}
|
|
112
|
-
}, [content, html])
|
|
113
|
-
|
|
114
|
-
useEffect(() => {
|
|
115
|
-
const elementRefs = new Set(anchorRefs)
|
|
116
|
-
|
|
117
|
-
const anchorById = document.querySelector(`[id='${anchorId}']`) as HTMLElement
|
|
118
|
-
if (anchorById) {
|
|
119
|
-
elementRefs.add({ current: anchorById })
|
|
120
|
-
}
|
|
121
|
-
|
|
122
|
-
if (!elementRefs.size) {
|
|
123
|
-
// eslint-disable-next-line @typescript-eslint/no-empty-function
|
|
124
|
-
return () => {}
|
|
125
|
-
}
|
|
126
|
-
|
|
127
|
-
const observerCallback: MutationCallback = (mutationList) => {
|
|
128
|
-
mutationList.forEach((mutation) => {
|
|
129
|
-
if (
|
|
130
|
-
!activeAnchor.current ||
|
|
131
|
-
mutation.type !== 'attributes' ||
|
|
132
|
-
!mutation.attributeName?.startsWith('data-tooltip-')
|
|
133
|
-
) {
|
|
134
|
-
return
|
|
135
|
-
}
|
|
136
|
-
// make sure to get all set attributes, since all unset attributes are reset
|
|
137
|
-
const dataAttributes = getDataAttributesFromAnchorElement(activeAnchor.current)
|
|
138
|
-
applyAllDataAttributesFromAnchorElement(dataAttributes)
|
|
139
|
-
})
|
|
140
|
-
}
|
|
141
|
-
|
|
142
|
-
// Create an observer instance linked to the callback function
|
|
143
|
-
const observer = new MutationObserver(observerCallback)
|
|
144
|
-
|
|
145
|
-
// do not check for subtree and childrens, we only want to know attribute changes
|
|
146
|
-
// to stay watching `data-attributes-*` from anchor element
|
|
147
|
-
const observerConfig = { attributes: true, childList: false, subtree: false }
|
|
148
|
-
|
|
149
|
-
const element = activeAnchor.current ?? anchorById
|
|
150
|
-
|
|
151
|
-
if (element) {
|
|
152
|
-
const dataAttributes = getDataAttributesFromAnchorElement(element)
|
|
153
|
-
applyAllDataAttributesFromAnchorElement(dataAttributes)
|
|
154
|
-
// Start observing the target node for configured mutations
|
|
155
|
-
observer.observe(element, observerConfig)
|
|
156
|
-
}
|
|
157
|
-
|
|
158
|
-
return () => {
|
|
159
|
-
// Remove the observer when the tooltip is destroyed
|
|
160
|
-
observer.disconnect()
|
|
161
|
-
}
|
|
162
|
-
}, [anchorRefs, activeAnchor, anchorId])
|
|
163
|
-
|
|
164
|
-
const props: ITooltip = {
|
|
165
|
-
id,
|
|
166
|
-
anchorId,
|
|
167
|
-
className,
|
|
168
|
-
classNameArrow,
|
|
169
|
-
content: tooltipContent,
|
|
170
|
-
isHtmlContent,
|
|
171
|
-
place: tooltipPlace,
|
|
172
|
-
variant: tooltipVariant,
|
|
173
|
-
offset: tooltipOffset,
|
|
174
|
-
wrapper: tooltipWrapper,
|
|
175
|
-
events: tooltipEvents,
|
|
176
|
-
positionStrategy: tooltipPositionStrategy,
|
|
177
|
-
delayShow: tooltipDelayShow,
|
|
178
|
-
delayHide: tooltipDelayHide,
|
|
179
|
-
style,
|
|
180
|
-
isOpen,
|
|
181
|
-
setIsOpen,
|
|
182
|
-
}
|
|
183
|
-
|
|
184
|
-
return children ? <Tooltip {...props}>{children}</Tooltip> : <Tooltip {...props} />
|
|
185
|
-
}
|
|
186
|
-
|
|
187
|
-
export default TooltipController
|
|
@@ -1,46 +0,0 @@
|
|
|
1
|
-
import type { CSSProperties } from 'react'
|
|
2
|
-
|
|
3
|
-
import type {
|
|
4
|
-
PlacesType,
|
|
5
|
-
VariantType,
|
|
6
|
-
WrapperType,
|
|
7
|
-
ChildrenType,
|
|
8
|
-
EventsType,
|
|
9
|
-
PositionStrategy,
|
|
10
|
-
} from 'components/Tooltip/TooltipTypes'
|
|
11
|
-
|
|
12
|
-
export interface ITooltipController {
|
|
13
|
-
className?: string
|
|
14
|
-
classNameArrow?: string
|
|
15
|
-
content?: string
|
|
16
|
-
html?: string
|
|
17
|
-
place?: PlacesType
|
|
18
|
-
offset?: number
|
|
19
|
-
id?: string
|
|
20
|
-
variant?: VariantType
|
|
21
|
-
anchorId?: string
|
|
22
|
-
wrapper?: WrapperType
|
|
23
|
-
children?: ChildrenType
|
|
24
|
-
events?: EventsType[]
|
|
25
|
-
positionStrategy?: PositionStrategy
|
|
26
|
-
delayShow?: number
|
|
27
|
-
delayHide?: number
|
|
28
|
-
style?: CSSProperties
|
|
29
|
-
isOpen?: boolean
|
|
30
|
-
setIsOpen?: (value: boolean) => void
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
declare module 'react' {
|
|
34
|
-
interface HTMLAttributes<T> extends AriaAttributes, DOMAttributes<T> {
|
|
35
|
-
'data-tooltip-place'?: PlacesType
|
|
36
|
-
'data-tooltip-content'?: string
|
|
37
|
-
'data-tooltip-html'?: string
|
|
38
|
-
'data-tooltip-variant'?: VariantType
|
|
39
|
-
'data-tooltip-offset'?: number
|
|
40
|
-
'data-tooltip-wrapper'?: WrapperType
|
|
41
|
-
'data-tooltip-events'?: EventsType[]
|
|
42
|
-
'data-tooltip-position-strategy'?: PositionStrategy
|
|
43
|
-
'data-tooltip-delay-show'?: number
|
|
44
|
-
'data-tooltip-delay-hide'?: number
|
|
45
|
-
}
|
|
46
|
-
}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export { default as TooltipController } from './TooltipController'
|
|
@@ -1,110 +0,0 @@
|
|
|
1
|
-
import React, {
|
|
2
|
-
createContext,
|
|
3
|
-
PropsWithChildren,
|
|
4
|
-
useCallback,
|
|
5
|
-
useContext,
|
|
6
|
-
useId,
|
|
7
|
-
useMemo,
|
|
8
|
-
useState,
|
|
9
|
-
} from 'react'
|
|
10
|
-
|
|
11
|
-
import type {
|
|
12
|
-
AnchorRef,
|
|
13
|
-
TooltipContextData,
|
|
14
|
-
TooltipContextDataWrapper,
|
|
15
|
-
} from './TooltipProviderTypes'
|
|
16
|
-
|
|
17
|
-
const defaultContextData: TooltipContextData = {
|
|
18
|
-
anchorRefs: new Set(),
|
|
19
|
-
activeAnchor: { current: null },
|
|
20
|
-
attach: () => {
|
|
21
|
-
/* attach anchor element */
|
|
22
|
-
},
|
|
23
|
-
detach: () => {
|
|
24
|
-
/* detach anchor element */
|
|
25
|
-
},
|
|
26
|
-
setActiveAnchor: () => {
|
|
27
|
-
/* set active anchor */
|
|
28
|
-
},
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
const defaultContextWrapper = Object.assign(() => defaultContextData, defaultContextData)
|
|
32
|
-
const TooltipContext = createContext<TooltipContextDataWrapper>(defaultContextWrapper)
|
|
33
|
-
|
|
34
|
-
const TooltipProvider: React.FC<PropsWithChildren> = ({ children }) => {
|
|
35
|
-
const defaultTooltipId = useId()
|
|
36
|
-
const [anchorRefMap, setAnchorRefMap] = useState<Record<string, Set<AnchorRef>>>({
|
|
37
|
-
[defaultTooltipId]: new Set(),
|
|
38
|
-
})
|
|
39
|
-
const [activeAnchorMap, setActiveAnchorMap] = useState<Record<string, AnchorRef>>({
|
|
40
|
-
[defaultTooltipId]: { current: null },
|
|
41
|
-
})
|
|
42
|
-
|
|
43
|
-
const attach = (tooltipId: string, ...refs: AnchorRef[]) => {
|
|
44
|
-
setAnchorRefMap((oldMap) => {
|
|
45
|
-
const tooltipRefs = oldMap[tooltipId] ?? new Set()
|
|
46
|
-
refs.forEach((ref) => tooltipRefs.add(ref))
|
|
47
|
-
// create new object to trigger re-render
|
|
48
|
-
return { ...oldMap, [tooltipId]: new Set(tooltipRefs) }
|
|
49
|
-
})
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
const detach = (tooltipId: string, ...refs: AnchorRef[]) => {
|
|
53
|
-
setAnchorRefMap((oldMap) => {
|
|
54
|
-
const tooltipRefs = oldMap[tooltipId]
|
|
55
|
-
if (!tooltipRefs) {
|
|
56
|
-
// tooltip not found
|
|
57
|
-
// maybe thow error?
|
|
58
|
-
return oldMap
|
|
59
|
-
}
|
|
60
|
-
refs.forEach((ref) => tooltipRefs.delete(ref))
|
|
61
|
-
// create new object to trigger re-render
|
|
62
|
-
return { ...oldMap }
|
|
63
|
-
})
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
const setActiveAnchor = (tooltipId: string, ref: React.RefObject<HTMLElement>) => {
|
|
67
|
-
setActiveAnchorMap((oldMap) => {
|
|
68
|
-
if (oldMap[tooltipId]?.current === ref.current) {
|
|
69
|
-
return oldMap
|
|
70
|
-
}
|
|
71
|
-
// create new object to trigger re-render
|
|
72
|
-
return { ...oldMap, [tooltipId]: ref }
|
|
73
|
-
})
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
const getTooltipData = useCallback(
|
|
77
|
-
(tooltipId?: string) => ({
|
|
78
|
-
anchorRefs: anchorRefMap[tooltipId ?? defaultTooltipId] ?? new Set(),
|
|
79
|
-
activeAnchor: activeAnchorMap[tooltipId ?? defaultTooltipId] ?? { current: null },
|
|
80
|
-
attach: (...refs: AnchorRef[]) => attach(tooltipId ?? defaultTooltipId, ...refs),
|
|
81
|
-
detach: (...refs: AnchorRef[]) => detach(tooltipId ?? defaultTooltipId, ...refs),
|
|
82
|
-
setActiveAnchor: (ref: AnchorRef) => setActiveAnchor(tooltipId ?? defaultTooltipId, ref),
|
|
83
|
-
}),
|
|
84
|
-
[defaultTooltipId, anchorRefMap, activeAnchorMap, attach, detach],
|
|
85
|
-
)
|
|
86
|
-
|
|
87
|
-
const context = useMemo(() => {
|
|
88
|
-
const contextData: TooltipContextData = getTooltipData(defaultTooltipId)
|
|
89
|
-
const contextWrapper = Object.assign(
|
|
90
|
-
(tooltipId?: string) => getTooltipData(tooltipId),
|
|
91
|
-
contextData,
|
|
92
|
-
)
|
|
93
|
-
return contextWrapper
|
|
94
|
-
}, [getTooltipData])
|
|
95
|
-
|
|
96
|
-
return <TooltipContext.Provider value={context}>{children}</TooltipContext.Provider>
|
|
97
|
-
}
|
|
98
|
-
|
|
99
|
-
/*
|
|
100
|
-
// this will use the "global" tooltip (same as `useTooltip()()`)
|
|
101
|
-
const { anchorRefs, attach, detach } = useTooltip()
|
|
102
|
-
|
|
103
|
-
// this will use the tooltip with id `tooltip-id`
|
|
104
|
-
const { anchorRefs, attach, detach } = useTooltip()('tooltip-id')
|
|
105
|
-
*/
|
|
106
|
-
export function useTooltip() {
|
|
107
|
-
return useContext(TooltipContext)
|
|
108
|
-
}
|
|
109
|
-
|
|
110
|
-
export default TooltipProvider
|
|
@@ -1,33 +0,0 @@
|
|
|
1
|
-
import type { ReactNode, RefObject } from 'react'
|
|
2
|
-
import type { ITooltipController } from 'components/TooltipController/TooltipControllerTypes'
|
|
3
|
-
|
|
4
|
-
export type AnchorRef = RefObject<HTMLElement>
|
|
5
|
-
|
|
6
|
-
export interface TooltipContextData {
|
|
7
|
-
anchorRefs: Set<AnchorRef>
|
|
8
|
-
activeAnchor: AnchorRef
|
|
9
|
-
attach: (...refs: AnchorRef[]) => void
|
|
10
|
-
detach: (...refs: AnchorRef[]) => void
|
|
11
|
-
setActiveAnchor: (ref: AnchorRef) => void
|
|
12
|
-
}
|
|
13
|
-
|
|
14
|
-
export type TooltipContextDataWrapper = TooltipContextData & {
|
|
15
|
-
// This means the context is a callable object
|
|
16
|
-
(tooltipId?: string): TooltipContextData
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
export interface ITooltipWrapper {
|
|
20
|
-
tooltipId?: string
|
|
21
|
-
children: ReactNode
|
|
22
|
-
|
|
23
|
-
place?: ITooltipController['place']
|
|
24
|
-
content?: ITooltipController['content']
|
|
25
|
-
html?: ITooltipController['html']
|
|
26
|
-
variant?: ITooltipController['variant']
|
|
27
|
-
offset?: ITooltipController['offset']
|
|
28
|
-
wrapper?: ITooltipController['wrapper']
|
|
29
|
-
events?: ITooltipController['events']
|
|
30
|
-
positionStrategy?: ITooltipController['positionStrategy']
|
|
31
|
-
delayShow?: ITooltipController['delayShow']
|
|
32
|
-
delayHide?: ITooltipController['delayHide']
|
|
33
|
-
}
|
|
@@ -1,48 +0,0 @@
|
|
|
1
|
-
import { useEffect, useRef } from 'react'
|
|
2
|
-
import { useTooltip } from './TooltipProvider'
|
|
3
|
-
import type { ITooltipWrapper } from './TooltipProviderTypes'
|
|
4
|
-
|
|
5
|
-
const TooltipWrapper = ({
|
|
6
|
-
tooltipId,
|
|
7
|
-
children,
|
|
8
|
-
place,
|
|
9
|
-
content,
|
|
10
|
-
html,
|
|
11
|
-
variant,
|
|
12
|
-
offset,
|
|
13
|
-
wrapper,
|
|
14
|
-
events,
|
|
15
|
-
positionStrategy,
|
|
16
|
-
delayShow,
|
|
17
|
-
delayHide,
|
|
18
|
-
}: ITooltipWrapper) => {
|
|
19
|
-
const { attach, detach } = useTooltip()(tooltipId)
|
|
20
|
-
const anchorRef = useRef<HTMLElement | null>(null)
|
|
21
|
-
|
|
22
|
-
useEffect(() => {
|
|
23
|
-
attach(anchorRef)
|
|
24
|
-
return () => {
|
|
25
|
-
detach(anchorRef)
|
|
26
|
-
}
|
|
27
|
-
}, [])
|
|
28
|
-
|
|
29
|
-
return (
|
|
30
|
-
<span
|
|
31
|
-
ref={anchorRef}
|
|
32
|
-
data-tooltip-place={place}
|
|
33
|
-
data-tooltip-content={content}
|
|
34
|
-
data-tooltip-html={html}
|
|
35
|
-
data-tooltip-variant={variant}
|
|
36
|
-
data-tooltip-offset={offset}
|
|
37
|
-
data-tooltip-wrapper={wrapper}
|
|
38
|
-
data-tooltip-events={events}
|
|
39
|
-
data-tooltip-position-strategy={positionStrategy}
|
|
40
|
-
data-tooltip-delay-show={delayShow}
|
|
41
|
-
data-tooltip-delay-hide={delayHide}
|
|
42
|
-
>
|
|
43
|
-
{children}
|
|
44
|
-
</span>
|
|
45
|
-
)
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
export default TooltipWrapper
|
package/src/index-dev.tsx
DELETED
|
@@ -1,16 +0,0 @@
|
|
|
1
|
-
import { StrictMode, version } from 'react'
|
|
2
|
-
import { createRoot } from 'react-dom/client'
|
|
3
|
-
import './tokens.css'
|
|
4
|
-
import App from './App'
|
|
5
|
-
|
|
6
|
-
// eslint-disable-next-line no-console
|
|
7
|
-
console.log('Parent folder loaded react version: ', version)
|
|
8
|
-
|
|
9
|
-
const container = document.getElementById('app')
|
|
10
|
-
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
|
11
|
-
const root = createRoot(container!)
|
|
12
|
-
root.render(
|
|
13
|
-
<StrictMode>
|
|
14
|
-
<App />
|
|
15
|
-
</StrictMode>,
|
|
16
|
-
)
|
package/src/index.tsx
DELETED
package/src/styles.module.css
DELETED
|
@@ -1,102 +0,0 @@
|
|
|
1
|
-
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
|
2
|
-
|
|
3
|
-
exports[`tooltip props basic tooltip component 1`] = `
|
|
4
|
-
[
|
|
5
|
-
<span
|
|
6
|
-
id="basic-example"
|
|
7
|
-
>
|
|
8
|
-
Lorem Ipsum
|
|
9
|
-
</span>,
|
|
10
|
-
<div
|
|
11
|
-
className=""
|
|
12
|
-
role="tooltip"
|
|
13
|
-
style={{}}
|
|
14
|
-
>
|
|
15
|
-
Hello World!
|
|
16
|
-
<div
|
|
17
|
-
className=""
|
|
18
|
-
style={{}}
|
|
19
|
-
/>
|
|
20
|
-
</div>,
|
|
21
|
-
]
|
|
22
|
-
`;
|
|
23
|
-
|
|
24
|
-
exports[`tooltip props tooltip component - getContent 1`] = `
|
|
25
|
-
[
|
|
26
|
-
<span
|
|
27
|
-
id="basic-example-get-content"
|
|
28
|
-
>
|
|
29
|
-
Lorem Ipsum
|
|
30
|
-
</span>,
|
|
31
|
-
<div
|
|
32
|
-
className=""
|
|
33
|
-
role="tooltip"
|
|
34
|
-
style={{}}
|
|
35
|
-
>
|
|
36
|
-
Hello World!
|
|
37
|
-
<div
|
|
38
|
-
className=""
|
|
39
|
-
style={{}}
|
|
40
|
-
/>
|
|
41
|
-
</div>,
|
|
42
|
-
]
|
|
43
|
-
`;
|
|
44
|
-
|
|
45
|
-
exports[`tooltip props tooltip component - html 1`] = `
|
|
46
|
-
[
|
|
47
|
-
<span
|
|
48
|
-
id="basic-example-html"
|
|
49
|
-
>
|
|
50
|
-
Lorem Ipsum
|
|
51
|
-
</span>,
|
|
52
|
-
<div
|
|
53
|
-
className=""
|
|
54
|
-
role="tooltip"
|
|
55
|
-
style={{}}
|
|
56
|
-
>
|
|
57
|
-
<span
|
|
58
|
-
dangerouslySetInnerHTML={
|
|
59
|
-
{
|
|
60
|
-
"__html": "Hello World!",
|
|
61
|
-
}
|
|
62
|
-
}
|
|
63
|
-
/>
|
|
64
|
-
<div
|
|
65
|
-
className=""
|
|
66
|
-
style={{}}
|
|
67
|
-
/>
|
|
68
|
-
</div>,
|
|
69
|
-
]
|
|
70
|
-
`;
|
|
71
|
-
|
|
72
|
-
exports[`tooltip props tooltip component - without anchorId 1`] = `
|
|
73
|
-
[
|
|
74
|
-
<span>
|
|
75
|
-
Lorem Ipsum
|
|
76
|
-
</span>,
|
|
77
|
-
<div
|
|
78
|
-
className=""
|
|
79
|
-
role="tooltip"
|
|
80
|
-
style={{}}
|
|
81
|
-
>
|
|
82
|
-
Hello World!
|
|
83
|
-
<div
|
|
84
|
-
className=""
|
|
85
|
-
style={{}}
|
|
86
|
-
/>
|
|
87
|
-
</div>,
|
|
88
|
-
]
|
|
89
|
-
`;
|
|
90
|
-
|
|
91
|
-
exports[`tooltip props tooltip component - without element reference 1`] = `
|
|
92
|
-
<div
|
|
93
|
-
className=""
|
|
94
|
-
role="tooltip"
|
|
95
|
-
style={{}}
|
|
96
|
-
>
|
|
97
|
-
<div
|
|
98
|
-
className=""
|
|
99
|
-
style={{}}
|
|
100
|
-
/>
|
|
101
|
-
</div>
|
|
102
|
-
`;
|
package/src/test/index.spec.js
DELETED
|
@@ -1,143 +0,0 @@
|
|
|
1
|
-
import renderer from 'react-test-renderer'
|
|
2
|
-
import debounce from 'utils/debounce'
|
|
3
|
-
import { computeTooltipPosition } from 'utils/compute-positions'
|
|
4
|
-
import { TooltipController as Tooltip } from '../components/TooltipController'
|
|
5
|
-
|
|
6
|
-
// Tell Jest to mock all timeout functions
|
|
7
|
-
jest.useFakeTimers()
|
|
8
|
-
|
|
9
|
-
// eslint-disable-next-line react/prop-types
|
|
10
|
-
const TooltipProps = ({ id, ...tooltipParams }) => (
|
|
11
|
-
<>
|
|
12
|
-
<span id={id}>Lorem Ipsum</span>
|
|
13
|
-
<Tooltip anchorId={id} {...tooltipParams} />
|
|
14
|
-
</>
|
|
15
|
-
)
|
|
16
|
-
|
|
17
|
-
describe('tooltip props', () => {
|
|
18
|
-
test('tooltip component - without anchorId', () => {
|
|
19
|
-
const component = renderer.create(<TooltipProps content="Hello World!" />)
|
|
20
|
-
const tree = component.toJSON()
|
|
21
|
-
expect(tree).toMatchSnapshot()
|
|
22
|
-
})
|
|
23
|
-
|
|
24
|
-
test('tooltip component - without element reference', () => {
|
|
25
|
-
const component = renderer.create(<Tooltip />)
|
|
26
|
-
const tree = component.toJSON()
|
|
27
|
-
expect(tree).toMatchSnapshot()
|
|
28
|
-
})
|
|
29
|
-
|
|
30
|
-
test('basic tooltip component', () => {
|
|
31
|
-
const component = renderer.create(<TooltipProps id="basic-example" content="Hello World!" />)
|
|
32
|
-
const tree = component.toJSON()
|
|
33
|
-
expect(tree).toMatchSnapshot()
|
|
34
|
-
})
|
|
35
|
-
|
|
36
|
-
test('tooltip component - html', () => {
|
|
37
|
-
const component = renderer.create(
|
|
38
|
-
<TooltipProps id="basic-example-html" html="Hello World!" variant="info" place="top" />,
|
|
39
|
-
)
|
|
40
|
-
const tree = component.toJSON()
|
|
41
|
-
expect(tree).toMatchSnapshot()
|
|
42
|
-
})
|
|
43
|
-
|
|
44
|
-
test('tooltip component - getContent', () => {
|
|
45
|
-
const component = renderer.create(
|
|
46
|
-
<TooltipProps
|
|
47
|
-
id="basic-example-get-content"
|
|
48
|
-
content="Hello World!"
|
|
49
|
-
getContent={(value) => `${value} Manipuled!`}
|
|
50
|
-
variant="info"
|
|
51
|
-
place="top"
|
|
52
|
-
/>,
|
|
53
|
-
)
|
|
54
|
-
const tree = component.toJSON()
|
|
55
|
-
expect(tree).toMatchSnapshot()
|
|
56
|
-
})
|
|
57
|
-
})
|
|
58
|
-
|
|
59
|
-
describe('compute positions', () => {
|
|
60
|
-
test('empty reference elements', async () => {
|
|
61
|
-
const value = await computeTooltipPosition({
|
|
62
|
-
elementReference: null,
|
|
63
|
-
tooltipReference: null,
|
|
64
|
-
tooltipArrowReference: null,
|
|
65
|
-
})
|
|
66
|
-
|
|
67
|
-
expect(value).toEqual({ tooltipStyles: {}, tooltipArrowStyles: {} })
|
|
68
|
-
})
|
|
69
|
-
|
|
70
|
-
test('empty tooltip reference element', async () => {
|
|
71
|
-
const element = document.createElement('div')
|
|
72
|
-
const value = await computeTooltipPosition({
|
|
73
|
-
elementReference: element,
|
|
74
|
-
tooltipReference: null,
|
|
75
|
-
tooltipArrowReference: null,
|
|
76
|
-
})
|
|
77
|
-
|
|
78
|
-
expect(value).toEqual({ tooltipStyles: {}, tooltipArrowStyles: {} })
|
|
79
|
-
})
|
|
80
|
-
|
|
81
|
-
test('empty tooltip arrow reference element', async () => {
|
|
82
|
-
const element = document.createElement('div')
|
|
83
|
-
const elementTooltip = document.createElement('div')
|
|
84
|
-
const value = await computeTooltipPosition({
|
|
85
|
-
elementReference: element,
|
|
86
|
-
tooltipReference: elementTooltip,
|
|
87
|
-
tooltipArrowReference: null,
|
|
88
|
-
})
|
|
89
|
-
|
|
90
|
-
expect(value).toEqual({
|
|
91
|
-
tooltipArrowStyles: {},
|
|
92
|
-
tooltipStyles: {
|
|
93
|
-
left: '5px',
|
|
94
|
-
top: '10px',
|
|
95
|
-
},
|
|
96
|
-
})
|
|
97
|
-
})
|
|
98
|
-
|
|
99
|
-
test('all reference elements', async () => {
|
|
100
|
-
const element = document.createElement('div')
|
|
101
|
-
const elementTooltip = document.createElement('div')
|
|
102
|
-
const elementTooltipArrow = document.createElement('div')
|
|
103
|
-
const value = await computeTooltipPosition({
|
|
104
|
-
elementReference: element,
|
|
105
|
-
tooltipReference: elementTooltip,
|
|
106
|
-
tooltipArrowReference: elementTooltipArrow,
|
|
107
|
-
})
|
|
108
|
-
|
|
109
|
-
expect(value).toEqual({
|
|
110
|
-
tooltipArrowStyles: {
|
|
111
|
-
bottom: '-4px',
|
|
112
|
-
left: '0px',
|
|
113
|
-
right: '',
|
|
114
|
-
top: '',
|
|
115
|
-
},
|
|
116
|
-
tooltipStyles: {
|
|
117
|
-
left: '5px',
|
|
118
|
-
top: '-10px',
|
|
119
|
-
},
|
|
120
|
-
})
|
|
121
|
-
})
|
|
122
|
-
})
|
|
123
|
-
|
|
124
|
-
describe('debounce', () => {
|
|
125
|
-
let func
|
|
126
|
-
let debouncedFunc
|
|
127
|
-
|
|
128
|
-
beforeEach((timeout = 1000) => {
|
|
129
|
-
func = jest.fn()
|
|
130
|
-
debouncedFunc = debounce(func, timeout)
|
|
131
|
-
})
|
|
132
|
-
|
|
133
|
-
test('execute just once', () => {
|
|
134
|
-
for (let i = 0; i < 100; i += 1) {
|
|
135
|
-
debouncedFunc()
|
|
136
|
-
}
|
|
137
|
-
|
|
138
|
-
// Fast-forward time
|
|
139
|
-
jest.runAllTimers()
|
|
140
|
-
|
|
141
|
-
expect(func).toBeCalledTimes(1)
|
|
142
|
-
})
|
|
143
|
-
})
|
package/src/tokens.css
DELETED
|
@@ -1,8 +0,0 @@
|
|
|
1
|
-
export interface IComputePositions {
|
|
2
|
-
elementReference?: Element | HTMLElement | null
|
|
3
|
-
tooltipReference?: Element | HTMLElement | null
|
|
4
|
-
tooltipArrowReference?: Element | HTMLElement | null
|
|
5
|
-
place?: 'top' | 'right' | 'bottom' | 'left'
|
|
6
|
-
offset?: number
|
|
7
|
-
strategy?: 'absolute' | 'fixed'
|
|
8
|
-
}
|
|
@@ -1,65 +0,0 @@
|
|
|
1
|
-
import { computePosition, offset, flip, shift, arrow } from '@floating-ui/dom'
|
|
2
|
-
import type { IComputePositions } from './compute-positions-types'
|
|
3
|
-
|
|
4
|
-
export const computeTooltipPosition = async ({
|
|
5
|
-
elementReference = null,
|
|
6
|
-
tooltipReference = null,
|
|
7
|
-
tooltipArrowReference = null,
|
|
8
|
-
place = 'top',
|
|
9
|
-
offset: offsetValue = 10,
|
|
10
|
-
strategy = 'absolute',
|
|
11
|
-
}: IComputePositions) => {
|
|
12
|
-
if (!elementReference) {
|
|
13
|
-
// elementReference can be null or undefined and we will not compute the position
|
|
14
|
-
// eslint-disable-next-line no-console
|
|
15
|
-
// console.error('The reference element for tooltip was not defined: ', elementReference)
|
|
16
|
-
return { tooltipStyles: {}, tooltipArrowStyles: {} }
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
if (tooltipReference === null) {
|
|
20
|
-
return { tooltipStyles: {}, tooltipArrowStyles: {} }
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
const middleware = [offset(Number(offsetValue)), flip(), shift({ padding: 5 })]
|
|
24
|
-
|
|
25
|
-
if (tooltipArrowReference) {
|
|
26
|
-
middleware.push(arrow({ element: tooltipArrowReference as HTMLElement }))
|
|
27
|
-
return computePosition(elementReference as HTMLElement, tooltipReference as HTMLElement, {
|
|
28
|
-
placement: place,
|
|
29
|
-
strategy,
|
|
30
|
-
middleware,
|
|
31
|
-
}).then(({ x, y, placement, middlewareData }) => {
|
|
32
|
-
const styles = { left: `${x}px`, top: `${y}px` }
|
|
33
|
-
|
|
34
|
-
const { x: arrowX, y: arrowY } = middlewareData.arrow ?? { x: 0, y: 0 }
|
|
35
|
-
|
|
36
|
-
const staticSide =
|
|
37
|
-
{
|
|
38
|
-
top: 'bottom',
|
|
39
|
-
right: 'left',
|
|
40
|
-
bottom: 'top',
|
|
41
|
-
left: 'right',
|
|
42
|
-
}[placement.split('-')[0]] ?? 'bottom'
|
|
43
|
-
|
|
44
|
-
const arrowStyle = {
|
|
45
|
-
left: arrowX != null ? `${arrowX}px` : '',
|
|
46
|
-
top: arrowY != null ? `${arrowY}px` : '',
|
|
47
|
-
right: '',
|
|
48
|
-
bottom: '',
|
|
49
|
-
[staticSide]: '-4px',
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
return { tooltipStyles: styles, tooltipArrowStyles: arrowStyle }
|
|
53
|
-
})
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
return computePosition(elementReference as HTMLElement, tooltipReference as HTMLElement, {
|
|
57
|
-
placement: 'bottom',
|
|
58
|
-
strategy,
|
|
59
|
-
middleware,
|
|
60
|
-
}).then(({ x, y }) => {
|
|
61
|
-
const styles = { left: `${x}px`, top: `${y}px` }
|
|
62
|
-
|
|
63
|
-
return { tooltipStyles: styles, tooltipArrowStyles: {} }
|
|
64
|
-
})
|
|
65
|
-
}
|
package/src/utils/debounce.ts
DELETED
|
@@ -1,27 +0,0 @@
|
|
|
1
|
-
/* eslint-disable @typescript-eslint/no-explicit-any */
|
|
2
|
-
/**
|
|
3
|
-
* This function debounce the received function
|
|
4
|
-
* @param { function } func Function to be debounced
|
|
5
|
-
* @param { number } wait Time to wait before execut the function
|
|
6
|
-
* @param { boolean } immediate Param to define if the function will be executed immediately
|
|
7
|
-
*/
|
|
8
|
-
const debounce = (func: (...args: any[]) => void, wait?: number, immediate?: true) => {
|
|
9
|
-
let timeout: NodeJS.Timeout | null = null
|
|
10
|
-
|
|
11
|
-
return function debounced(this: typeof func, ...args: any[]) {
|
|
12
|
-
const later = () => {
|
|
13
|
-
timeout = null
|
|
14
|
-
if (!immediate) {
|
|
15
|
-
func.apply(this, args)
|
|
16
|
-
}
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
if (timeout) {
|
|
20
|
-
clearTimeout(timeout)
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
timeout = setTimeout(later, wait)
|
|
24
|
-
}
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
export default debounce
|