create-template-html-css 2.1.0 → 2.2.0
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/CHANGELOG.md +131 -0
- package/CODE-SPLITTING-GUIDE.md +274 -0
- package/COMPONENTS-GALLERY.html +143 -8
- package/README.md +71 -3
- package/bin/cli.js +2 -0
- package/bin/commands/create.js +16 -0
- package/package.json +1 -1
- package/src/react-component-choices.js +53 -1
- package/src/react-component-templates.js +182 -0
- package/src/react-generator.js +15 -4
- package/src/react-templates.js +192 -124
- package/src/templates/basic-components-templates.js +157 -0
- package/src/templates/form-components-templates.js +194 -0
- package/src/templates/interactive-components-templates.js +139 -0
- package/templates-react/alert/Alert.css +158 -0
- package/templates-react/alert/Alert.example.jsx +106 -0
- package/templates-react/alert/Alert.jsx +61 -0
- package/templates-react/badge/Badge.css +196 -0
- package/templates-react/badge/Badge.example.jsx +182 -0
- package/templates-react/badge/Badge.jsx +44 -0
- package/templates-react/button/Button.example.jsx +1 -1
- package/templates-react/button/Button.jsx +1 -1
- package/templates-react/card/Card.example.jsx +1 -1
- package/templates-react/card/Card.jsx +1 -1
- package/templates-react/checkbox/Checkbox.css +217 -0
- package/templates-react/checkbox/Checkbox.example.jsx +141 -0
- package/templates-react/checkbox/Checkbox.jsx +82 -0
- package/templates-react/counter/Counter.example.jsx +1 -1
- package/templates-react/counter/Counter.jsx +1 -1
- package/templates-react/dropdown/Dropdown.css +237 -0
- package/templates-react/dropdown/Dropdown.example.jsx +98 -0
- package/templates-react/dropdown/Dropdown.jsx +154 -0
- package/templates-react/form/Form.example.jsx +0 -1
- package/templates-react/form/Form.jsx +1 -1
- package/templates-react/input/Input.css +113 -0
- package/templates-react/input/Input.example.jsx +82 -0
- package/templates-react/input/Input.jsx +87 -0
- package/templates-react/modal/Modal.example.jsx +1 -1
- package/templates-react/modal/Modal.jsx +1 -1
- package/templates-react/navbar/Navbar.css +139 -0
- package/templates-react/navbar/Navbar.example.jsx +37 -0
- package/templates-react/navbar/Navbar.jsx +62 -0
- package/templates-react/progress/Progress.css +247 -0
- package/templates-react/progress/Progress.example.jsx +244 -0
- package/templates-react/progress/Progress.jsx +79 -0
- package/templates-react/switch/Switch.css +244 -0
- package/templates-react/switch/Switch.example.jsx +221 -0
- package/templates-react/switch/Switch.jsx +98 -0
- package/templates-react/todo-list/TodoList.example.jsx +1 -1
- package/templates-react/todo-list/TodoList.jsx +1 -1
- package/templates-react/tooltip/Tooltip.css +165 -0
- package/templates-react/tooltip/Tooltip.example.jsx +166 -0
- package/templates-react/tooltip/Tooltip.jsx +176 -0
|
@@ -0,0 +1,165 @@
|
|
|
1
|
+
.tooltip-trigger {
|
|
2
|
+
display: inline-flex;
|
|
3
|
+
position: relative;
|
|
4
|
+
cursor: help;
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
.tooltip {
|
|
8
|
+
position: fixed;
|
|
9
|
+
z-index: 9999;
|
|
10
|
+
padding: 0.5rem 0.75rem;
|
|
11
|
+
background-color: #1f2937;
|
|
12
|
+
color: white;
|
|
13
|
+
font-size: 0.875rem;
|
|
14
|
+
line-height: 1.25rem;
|
|
15
|
+
border-radius: 0.375rem;
|
|
16
|
+
box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1), 0 2px 4px -1px rgba(0, 0, 0, 0.06);
|
|
17
|
+
pointer-events: none;
|
|
18
|
+
animation: tooltipFadeIn 0.15s ease-out;
|
|
19
|
+
word-wrap: break-word;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
@keyframes tooltipFadeIn {
|
|
23
|
+
from {
|
|
24
|
+
opacity: 0;
|
|
25
|
+
transform: scale(0.95);
|
|
26
|
+
}
|
|
27
|
+
to {
|
|
28
|
+
opacity: 1;
|
|
29
|
+
transform: scale(1);
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
/* Arrow Styles */
|
|
34
|
+
.tooltip-arrow::before {
|
|
35
|
+
content: '';
|
|
36
|
+
position: absolute;
|
|
37
|
+
width: 0;
|
|
38
|
+
height: 0;
|
|
39
|
+
border-style: solid;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
.tooltip-top.tooltip-arrow::before {
|
|
43
|
+
bottom: -6px;
|
|
44
|
+
left: 50%;
|
|
45
|
+
transform: translateX(-50%);
|
|
46
|
+
border-width: 6px 6px 0 6px;
|
|
47
|
+
border-color: #1f2937 transparent transparent transparent;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
.tooltip-bottom.tooltip-arrow::before {
|
|
51
|
+
top: -6px;
|
|
52
|
+
left: 50%;
|
|
53
|
+
transform: translateX(-50%);
|
|
54
|
+
border-width: 0 6px 6px 6px;
|
|
55
|
+
border-color: transparent transparent #1f2937 transparent;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
.tooltip-left.tooltip-arrow::before {
|
|
59
|
+
right: -6px;
|
|
60
|
+
top: 50%;
|
|
61
|
+
transform: translateY(-50%);
|
|
62
|
+
border-width: 6px 0 6px 6px;
|
|
63
|
+
border-color: transparent transparent transparent #1f2937;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
.tooltip-right.tooltip-arrow::before {
|
|
67
|
+
left: -6px;
|
|
68
|
+
top: 50%;
|
|
69
|
+
transform: translateY(-50%);
|
|
70
|
+
border-width: 6px 6px 6px 0;
|
|
71
|
+
border-color: transparent #1f2937 transparent transparent;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
/* Position Animations */
|
|
75
|
+
.tooltip-top {
|
|
76
|
+
animation: tooltipSlideDown 0.2s ease-out;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
.tooltip-bottom {
|
|
80
|
+
animation: tooltipSlideUp 0.2s ease-out;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
.tooltip-left {
|
|
84
|
+
animation: tooltipSlideRight 0.2s ease-out;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
.tooltip-right {
|
|
88
|
+
animation: tooltipSlideLeft 0.2s ease-out;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
@keyframes tooltipSlideDown {
|
|
92
|
+
from {
|
|
93
|
+
opacity: 0;
|
|
94
|
+
transform: translateY(-10px);
|
|
95
|
+
}
|
|
96
|
+
to {
|
|
97
|
+
opacity: 1;
|
|
98
|
+
transform: translateY(0);
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
@keyframes tooltipSlideUp {
|
|
103
|
+
from {
|
|
104
|
+
opacity: 0;
|
|
105
|
+
transform: translateY(10px);
|
|
106
|
+
}
|
|
107
|
+
to {
|
|
108
|
+
opacity: 1;
|
|
109
|
+
transform: translateY(0);
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
@keyframes tooltipSlideRight {
|
|
114
|
+
from {
|
|
115
|
+
opacity: 0;
|
|
116
|
+
transform: translateX(-10px);
|
|
117
|
+
}
|
|
118
|
+
to {
|
|
119
|
+
opacity: 1;
|
|
120
|
+
transform: translateX(0);
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
@keyframes tooltipSlideLeft {
|
|
125
|
+
from {
|
|
126
|
+
opacity: 0;
|
|
127
|
+
transform: translateX(10px);
|
|
128
|
+
}
|
|
129
|
+
to {
|
|
130
|
+
opacity: 1;
|
|
131
|
+
transform: translateX(0);
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
/* Dark Mode Support (inverted for visibility) */
|
|
136
|
+
@media (prefers-color-scheme: dark) {
|
|
137
|
+
.tooltip {
|
|
138
|
+
background-color: #f3f4f6;
|
|
139
|
+
color: #111827;
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
.tooltip-top.tooltip-arrow::before {
|
|
143
|
+
border-color: #f3f4f6 transparent transparent transparent;
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
.tooltip-bottom.tooltip-arrow::before {
|
|
147
|
+
border-color: transparent transparent #f3f4f6 transparent;
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
.tooltip-left.tooltip-arrow::before {
|
|
151
|
+
border-color: transparent transparent transparent #f3f4f6;
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
.tooltip-right.tooltip-arrow::before {
|
|
155
|
+
border-color: transparent #f3f4f6 transparent transparent;
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
/* Responsive adjustments */
|
|
160
|
+
@media (max-width: 768px) {
|
|
161
|
+
.tooltip {
|
|
162
|
+
max-width: calc(100vw - 2rem) !important;
|
|
163
|
+
font-size: 0.8125rem;
|
|
164
|
+
}
|
|
165
|
+
}
|
|
@@ -0,0 +1,166 @@
|
|
|
1
|
+
|
|
2
|
+
import Tooltip from './Tooltip';
|
|
3
|
+
|
|
4
|
+
function TooltipExample() {
|
|
5
|
+
return (
|
|
6
|
+
<div style={{ padding: '2rem', maxWidth: '800px', margin: '0 auto' }}>
|
|
7
|
+
<h1>Tooltip Component Examples</h1>
|
|
8
|
+
|
|
9
|
+
<section style={{ marginBottom: '3rem' }}>
|
|
10
|
+
<h2>Basic Tooltips</h2>
|
|
11
|
+
<div style={{ display: 'flex', gap: '2rem', flexWrap: 'wrap' }}>
|
|
12
|
+
<Tooltip content="This is a tooltip!">
|
|
13
|
+
<button style={buttonStyle}>Hover me</button>
|
|
14
|
+
</Tooltip>
|
|
15
|
+
|
|
16
|
+
<Tooltip content="This tooltip has a longer text to demonstrate how it handles multiple lines of content.">
|
|
17
|
+
<button style={buttonStyle}>Long content</button>
|
|
18
|
+
</Tooltip>
|
|
19
|
+
</div>
|
|
20
|
+
</section>
|
|
21
|
+
|
|
22
|
+
<section style={{ marginBottom: '3rem' }}>
|
|
23
|
+
<h2>Positions</h2>
|
|
24
|
+
<div style={{
|
|
25
|
+
display: 'grid',
|
|
26
|
+
gridTemplateColumns: 'repeat(2, 1fr)',
|
|
27
|
+
gap: '2rem',
|
|
28
|
+
placeItems: 'center',
|
|
29
|
+
minHeight: '200px'
|
|
30
|
+
}}>
|
|
31
|
+
<Tooltip content="Tooltip on top" position="top">
|
|
32
|
+
<button style={buttonStyle}>Top</button>
|
|
33
|
+
</Tooltip>
|
|
34
|
+
|
|
35
|
+
<Tooltip content="Tooltip on bottom" position="bottom">
|
|
36
|
+
<button style={buttonStyle}>Bottom</button>
|
|
37
|
+
</Tooltip>
|
|
38
|
+
|
|
39
|
+
<Tooltip content="Tooltip on left" position="left">
|
|
40
|
+
<button style={buttonStyle}>Left</button>
|
|
41
|
+
</Tooltip>
|
|
42
|
+
|
|
43
|
+
<Tooltip content="Tooltip on right" position="right">
|
|
44
|
+
<button style={buttonStyle}>Right</button>
|
|
45
|
+
</Tooltip>
|
|
46
|
+
</div>
|
|
47
|
+
</section>
|
|
48
|
+
|
|
49
|
+
<section style={{ marginBottom: '3rem' }}>
|
|
50
|
+
<h2>Different Triggers</h2>
|
|
51
|
+
<div style={{ display: 'flex', gap: '2rem', flexWrap: 'wrap' }}>
|
|
52
|
+
<Tooltip content="Appears on hover (default)" trigger="hover">
|
|
53
|
+
<button style={buttonStyle}>Hover trigger</button>
|
|
54
|
+
</Tooltip>
|
|
55
|
+
|
|
56
|
+
<Tooltip content="Appears on click" trigger="click">
|
|
57
|
+
<button style={buttonStyle}>Click trigger</button>
|
|
58
|
+
</Tooltip>
|
|
59
|
+
|
|
60
|
+
<Tooltip content="Appears on focus" trigger="focus">
|
|
61
|
+
<button style={buttonStyle}>Focus trigger</button>
|
|
62
|
+
</Tooltip>
|
|
63
|
+
</div>
|
|
64
|
+
</section>
|
|
65
|
+
|
|
66
|
+
<section style={{ marginBottom: '3rem' }}>
|
|
67
|
+
<h2>Without Arrow</h2>
|
|
68
|
+
<Tooltip content="Tooltip without arrow" arrow={false}>
|
|
69
|
+
<button style={buttonStyle}>No arrow</button>
|
|
70
|
+
</Tooltip>
|
|
71
|
+
</section>
|
|
72
|
+
|
|
73
|
+
<section style={{ marginBottom: '3rem' }}>
|
|
74
|
+
<h2>Custom Delay</h2>
|
|
75
|
+
<div style={{ display: 'flex', gap: '2rem', flexWrap: 'wrap' }}>
|
|
76
|
+
<Tooltip content="No delay" delay={0}>
|
|
77
|
+
<button style={buttonStyle}>No delay</button>
|
|
78
|
+
</Tooltip>
|
|
79
|
+
|
|
80
|
+
<Tooltip content="500ms delay" delay={500}>
|
|
81
|
+
<button style={buttonStyle}>Slow (500ms)</button>
|
|
82
|
+
</Tooltip>
|
|
83
|
+
</div>
|
|
84
|
+
</section>
|
|
85
|
+
|
|
86
|
+
<section style={{ marginBottom: '3rem' }}>
|
|
87
|
+
<h2>Custom Width</h2>
|
|
88
|
+
<Tooltip
|
|
89
|
+
content="This is a very long tooltip content that demonstrates the custom maxWidth property. You can control how wide the tooltip should be."
|
|
90
|
+
maxWidth="300px"
|
|
91
|
+
>
|
|
92
|
+
<button style={buttonStyle}>Wide tooltip</button>
|
|
93
|
+
</Tooltip>
|
|
94
|
+
</section>
|
|
95
|
+
|
|
96
|
+
<section style={{ marginBottom: '3rem' }}>
|
|
97
|
+
<h2>Disabled Tooltip</h2>
|
|
98
|
+
<Tooltip content="You won't see me!" disabled>
|
|
99
|
+
<button style={buttonStyle}>Disabled tooltip</button>
|
|
100
|
+
</Tooltip>
|
|
101
|
+
</section>
|
|
102
|
+
|
|
103
|
+
<section style={{ marginBottom: '3rem' }}>
|
|
104
|
+
<h2>On Different Elements</h2>
|
|
105
|
+
<div style={{ display: 'flex', gap: '2rem', flexWrap: 'wrap', alignItems: 'center' }}>
|
|
106
|
+
<Tooltip content="Tooltip on text">
|
|
107
|
+
<span style={{ color: '#3b82f6', textDecoration: 'underline dotted' }}>
|
|
108
|
+
Hover this text
|
|
109
|
+
</span>
|
|
110
|
+
</Tooltip>
|
|
111
|
+
|
|
112
|
+
<Tooltip content="Helpful information about this icon">
|
|
113
|
+
<span style={{ fontSize: '2rem', cursor: 'help' }}>ℹ️</span>
|
|
114
|
+
</Tooltip>
|
|
115
|
+
|
|
116
|
+
<Tooltip content="This badge has a tooltip">
|
|
117
|
+
<span style={{
|
|
118
|
+
backgroundColor: '#ef4444',
|
|
119
|
+
color: 'white',
|
|
120
|
+
padding: '0.25rem 0.5rem',
|
|
121
|
+
borderRadius: '0.25rem',
|
|
122
|
+
fontSize: '0.875rem'
|
|
123
|
+
}}>
|
|
124
|
+
NEW
|
|
125
|
+
</span>
|
|
126
|
+
</Tooltip>
|
|
127
|
+
</div>
|
|
128
|
+
</section>
|
|
129
|
+
|
|
130
|
+
<section style={{ marginBottom: '3rem' }}>
|
|
131
|
+
<h2>Interactive Example</h2>
|
|
132
|
+
<div style={{ display: 'flex', gap: '1rem', alignItems: 'center' }}>
|
|
133
|
+
<span>Username:</span>
|
|
134
|
+
<input
|
|
135
|
+
type="text"
|
|
136
|
+
style={{
|
|
137
|
+
padding: '0.5rem',
|
|
138
|
+
border: '1px solid #d1d5db',
|
|
139
|
+
borderRadius: '0.375rem'
|
|
140
|
+
}}
|
|
141
|
+
/>
|
|
142
|
+
<Tooltip
|
|
143
|
+
content="Username must be 3-20 characters long and contain only letters, numbers, and underscores."
|
|
144
|
+
position="right"
|
|
145
|
+
maxWidth="250px"
|
|
146
|
+
>
|
|
147
|
+
<span style={{ fontSize: '1.25rem', cursor: 'help', color: '#6b7280' }}>❓</span>
|
|
148
|
+
</Tooltip>
|
|
149
|
+
</div>
|
|
150
|
+
</section>
|
|
151
|
+
</div>
|
|
152
|
+
);
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
const buttonStyle = {
|
|
156
|
+
padding: '0.5rem 1rem',
|
|
157
|
+
backgroundColor: '#3b82f6',
|
|
158
|
+
color: 'white',
|
|
159
|
+
border: 'none',
|
|
160
|
+
borderRadius: '0.375rem',
|
|
161
|
+
cursor: 'pointer',
|
|
162
|
+
fontSize: '0.875rem',
|
|
163
|
+
fontWeight: '500'
|
|
164
|
+
};
|
|
165
|
+
|
|
166
|
+
export default TooltipExample;
|
|
@@ -0,0 +1,176 @@
|
|
|
1
|
+
|
|
2
|
+
import './Tooltip.css';
|
|
3
|
+
import { useState, useRef, useEffect } from 'react';
|
|
4
|
+
|
|
5
|
+
function Tooltip({
|
|
6
|
+
children,
|
|
7
|
+
content,
|
|
8
|
+
position = 'top',
|
|
9
|
+
trigger = 'hover',
|
|
10
|
+
delay = 200,
|
|
11
|
+
arrow = true,
|
|
12
|
+
maxWidth = '200px',
|
|
13
|
+
disabled = false,
|
|
14
|
+
className = ''
|
|
15
|
+
}) {
|
|
16
|
+
const [isVisible, setIsVisible] = useState(false);
|
|
17
|
+
const [tooltipPosition, setTooltipPosition] = useState({});
|
|
18
|
+
const triggerRef = useRef(null);
|
|
19
|
+
const tooltipRef = useRef(null);
|
|
20
|
+
const timeoutRef = useRef(null);
|
|
21
|
+
|
|
22
|
+
const calculatePosition = () => {
|
|
23
|
+
if (!triggerRef.current || !tooltipRef.current) return;
|
|
24
|
+
|
|
25
|
+
const triggerRect = triggerRef.current.getBoundingClientRect();
|
|
26
|
+
const tooltipRect = tooltipRef.current.getBoundingClientRect();
|
|
27
|
+
const gap = arrow ? 12 : 8;
|
|
28
|
+
|
|
29
|
+
let top, left;
|
|
30
|
+
|
|
31
|
+
switch (position) {
|
|
32
|
+
case 'top':
|
|
33
|
+
top = triggerRect.top - tooltipRect.height - gap;
|
|
34
|
+
left = triggerRect.left + (triggerRect.width - tooltipRect.width) / 2;
|
|
35
|
+
break;
|
|
36
|
+
case 'bottom':
|
|
37
|
+
top = triggerRect.bottom + gap;
|
|
38
|
+
left = triggerRect.left + (triggerRect.width - tooltipRect.width) / 2;
|
|
39
|
+
break;
|
|
40
|
+
case 'left':
|
|
41
|
+
top = triggerRect.top + (triggerRect.height - tooltipRect.height) / 2;
|
|
42
|
+
left = triggerRect.left - tooltipRect.width - gap;
|
|
43
|
+
break;
|
|
44
|
+
case 'right':
|
|
45
|
+
top = triggerRect.top + (triggerRect.height - tooltipRect.height) / 2;
|
|
46
|
+
left = triggerRect.right + gap;
|
|
47
|
+
break;
|
|
48
|
+
default:
|
|
49
|
+
top = triggerRect.top - tooltipRect.height - gap;
|
|
50
|
+
left = triggerRect.left + (triggerRect.width - tooltipRect.width) / 2;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
// Keep tooltip within viewport
|
|
54
|
+
const padding = 8;
|
|
55
|
+
if (left < padding) left = padding;
|
|
56
|
+
if (left + tooltipRect.width > window.innerWidth - padding) {
|
|
57
|
+
left = window.innerWidth - tooltipRect.width - padding;
|
|
58
|
+
}
|
|
59
|
+
if (top < padding) top = padding;
|
|
60
|
+
if (top + tooltipRect.height > window.innerHeight - padding) {
|
|
61
|
+
top = window.innerHeight - tooltipRect.height - padding;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
setTooltipPosition({ top, left });
|
|
65
|
+
};
|
|
66
|
+
|
|
67
|
+
const showTooltip = () => {
|
|
68
|
+
if (disabled) return;
|
|
69
|
+
|
|
70
|
+
if (delay > 0) {
|
|
71
|
+
timeoutRef.current = setTimeout(() => {
|
|
72
|
+
setIsVisible(true);
|
|
73
|
+
}, delay);
|
|
74
|
+
} else {
|
|
75
|
+
setIsVisible(true);
|
|
76
|
+
}
|
|
77
|
+
};
|
|
78
|
+
|
|
79
|
+
const hideTooltip = () => {
|
|
80
|
+
if (timeoutRef.current) {
|
|
81
|
+
clearTimeout(timeoutRef.current);
|
|
82
|
+
}
|
|
83
|
+
setIsVisible(false);
|
|
84
|
+
};
|
|
85
|
+
|
|
86
|
+
const handleMouseEnter = () => {
|
|
87
|
+
if (trigger === 'hover' || trigger === 'both') {
|
|
88
|
+
showTooltip();
|
|
89
|
+
}
|
|
90
|
+
};
|
|
91
|
+
|
|
92
|
+
const handleMouseLeave = () => {
|
|
93
|
+
if (trigger === 'hover' || trigger === 'both') {
|
|
94
|
+
hideTooltip();
|
|
95
|
+
}
|
|
96
|
+
};
|
|
97
|
+
|
|
98
|
+
const handleClick = () => {
|
|
99
|
+
if (trigger === 'click' || trigger === 'both') {
|
|
100
|
+
if (isVisible) {
|
|
101
|
+
hideTooltip();
|
|
102
|
+
} else {
|
|
103
|
+
showTooltip();
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
};
|
|
107
|
+
|
|
108
|
+
const handleFocus = () => {
|
|
109
|
+
if (trigger === 'focus') {
|
|
110
|
+
showTooltip();
|
|
111
|
+
}
|
|
112
|
+
};
|
|
113
|
+
|
|
114
|
+
const handleBlur = () => {
|
|
115
|
+
if (trigger === 'focus') {
|
|
116
|
+
hideTooltip();
|
|
117
|
+
}
|
|
118
|
+
};
|
|
119
|
+
|
|
120
|
+
useEffect(() => {
|
|
121
|
+
if (isVisible) {
|
|
122
|
+
calculatePosition();
|
|
123
|
+
window.addEventListener('scroll', calculatePosition);
|
|
124
|
+
window.addEventListener('resize', calculatePosition);
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
return () => {
|
|
128
|
+
window.removeEventListener('scroll', calculatePosition);
|
|
129
|
+
window.removeEventListener('resize', calculatePosition);
|
|
130
|
+
};
|
|
131
|
+
}, [isVisible]);
|
|
132
|
+
|
|
133
|
+
useEffect(() => {
|
|
134
|
+
return () => {
|
|
135
|
+
if (timeoutRef.current) {
|
|
136
|
+
clearTimeout(timeoutRef.current);
|
|
137
|
+
}
|
|
138
|
+
};
|
|
139
|
+
}, []);
|
|
140
|
+
|
|
141
|
+
if (!content) return <>{children}</>;
|
|
142
|
+
|
|
143
|
+
return (
|
|
144
|
+
<>
|
|
145
|
+
<span
|
|
146
|
+
ref={triggerRef}
|
|
147
|
+
className={`tooltip-trigger ${className}`}
|
|
148
|
+
onMouseEnter={handleMouseEnter}
|
|
149
|
+
onMouseLeave={handleMouseLeave}
|
|
150
|
+
onClick={handleClick}
|
|
151
|
+
onFocus={handleFocus}
|
|
152
|
+
onBlur={handleBlur}
|
|
153
|
+
aria-describedby={isVisible ? 'tooltip' : undefined}
|
|
154
|
+
>
|
|
155
|
+
{children}
|
|
156
|
+
</span>
|
|
157
|
+
|
|
158
|
+
{isVisible && (
|
|
159
|
+
<div
|
|
160
|
+
ref={tooltipRef}
|
|
161
|
+
id="tooltip"
|
|
162
|
+
className={`tooltip tooltip-${position} ${arrow ? 'tooltip-arrow' : ''}`}
|
|
163
|
+
style={{
|
|
164
|
+
...tooltipPosition,
|
|
165
|
+
maxWidth
|
|
166
|
+
}}
|
|
167
|
+
role="tooltip"
|
|
168
|
+
>
|
|
169
|
+
{content}
|
|
170
|
+
</div>
|
|
171
|
+
)}
|
|
172
|
+
</>
|
|
173
|
+
);
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
export default Tooltip;
|