@placeholderco/placeholder-ui 1.0.3 → 1.0.6
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/LICENSE +26 -26
- package/README.md +179 -179
- package/dist/display/Alert.svelte +179 -179
- package/dist/display/Avatar.svelte +166 -166
- package/dist/display/LinkCollection.svelte +161 -161
- package/dist/display/Paper.svelte +118 -118
- package/dist/form/Autocomplete.svelte +223 -191
- package/dist/form/Autocomplete.svelte.d.ts +3 -1
- package/dist/form/AutocompleteMulti.svelte +356 -0
- package/dist/form/AutocompleteMulti.svelte.d.ts +28 -0
- package/dist/form/Checkbox.svelte +201 -201
- package/dist/form/Chips.svelte +128 -128
- package/dist/form/ComboBox.svelte +158 -158
- package/dist/form/ComboBox.svelte.d.ts +1 -1
- package/dist/form/ComboBoxItemBuilder.svelte +460 -460
- package/dist/form/ComboBoxMulti.svelte +197 -197
- package/dist/form/ComboBoxMulti.svelte.d.ts +1 -1
- package/dist/form/CronBuilder.svelte +693 -693
- package/dist/form/DatePicker.svelte +672 -672
- package/dist/form/DateTimePicker.svelte +712 -712
- package/dist/form/FileInput.svelte +235 -235
- package/dist/form/FormGroup.svelte +68 -68
- package/dist/form/Number.svelte +238 -238
- package/dist/form/PasswordInput.svelte +252 -252
- package/dist/form/RadioGroup.svelte +210 -210
- package/dist/form/Rating.svelte +235 -235
- package/dist/form/SegmentedControl.svelte +149 -149
- package/dist/form/Select.svelte +590 -590
- package/dist/form/Select.svelte.d.ts +1 -1
- package/dist/form/SelectMulti.svelte +613 -613
- package/dist/form/SelectMulti.svelte.d.ts +1 -1
- package/dist/form/Slider.svelte +358 -358
- package/dist/form/Switch.svelte +147 -147
- package/dist/form/TextArea.svelte +148 -148
- package/dist/form/Textbox.svelte +228 -228
- package/dist/form/TimePicker.svelte +267 -267
- package/dist/icon/Icon.svelte +52 -52
- package/dist/icon/alert-octagon.svg +5 -5
- package/dist/icon/alert-triangle.svg +5 -5
- package/dist/icon/archive.svg +1 -1
- package/dist/icon/arrow-down.svg +1 -1
- package/dist/icon/arrow-left.svg +1 -1
- package/dist/icon/arrow-right.svg +1 -1
- package/dist/icon/arrow-up.svg +1 -1
- package/dist/icon/at.svg +1 -1
- package/dist/icon/bell.svg +1 -1
- package/dist/icon/bookmark.svg +1 -1
- package/dist/icon/calendar.svg +1 -1
- package/dist/icon/camera.svg +1 -1
- package/dist/icon/chart-bar.svg +1 -1
- package/dist/icon/chart-line.svg +1 -1
- package/dist/icon/chart-pie.svg +1 -1
- package/dist/icon/checkbox.svg +1 -1
- package/dist/icon/checklist.svg +1 -1
- package/dist/icon/circle-check.svg +1 -1
- package/dist/icon/circle-x.svg +1 -1
- package/dist/icon/clock.svg +1 -1
- package/dist/icon/credit-card.svg +1 -1
- package/dist/icon/dots-vertical.svg +1 -1
- package/dist/icon/dots.svg +1 -1
- package/dist/icon/external-link.svg +1 -1
- package/dist/icon/eye-off.svg +1 -1
- package/dist/icon/eye.svg +1 -1
- package/dist/icon/filter.svg +1 -1
- package/dist/icon/fingerprint.svg +1 -1
- package/dist/icon/flag.svg +1 -1
- package/dist/icon/heart.svg +1 -1
- package/dist/icon/home.svg +1 -1
- package/dist/icon/key.svg +1 -1
- package/dist/icon/list-check.svg +1 -1
- package/dist/icon/login.svg +1 -1
- package/dist/icon/logout.svg +1 -1
- package/dist/icon/map-pin.svg +1 -1
- package/dist/icon/maximize.svg +1 -1
- package/dist/icon/microphone.svg +1 -1
- package/dist/icon/minimize.svg +1 -1
- package/dist/icon/note.svg +1 -1
- package/dist/icon/player-pause.svg +1 -1
- package/dist/icon/printer.svg +1 -1
- package/dist/icon/qrcode.svg +1 -1
- package/dist/icon/send.svg +1 -1
- package/dist/icon/settings.svg +1 -1
- package/dist/icon/share.svg +1 -1
- package/dist/icon/shopping-cart.svg +1 -1
- package/dist/icon/sort-ascending.svg +1 -1
- package/dist/icon/sort-descending.svg +1 -1
- package/dist/icon/star.svg +1 -1
- package/dist/icon/tag.svg +1 -1
- package/dist/icon/trending-down.svg +1 -1
- package/dist/icon/trending-up.svg +1 -1
- package/dist/icon/upload.svg +1 -1
- package/dist/icon/volume-off.svg +1 -1
- package/dist/icon/volume.svg +1 -1
- package/dist/icon/world.svg +1 -1
- package/dist/icon/zoom-in.svg +1 -1
- package/dist/icon/zoom-out.svg +1 -1
- package/dist/index.d.ts +1 -0
- package/dist/index.js +1 -0
- package/dist/layout/AppShell.svelte +169 -169
- package/dist/layout/CustomNavbar.svelte +61 -61
- package/dist/layout/Navbar.svelte +206 -206
- package/dist/layout/NavbarItemDisplay.svelte +29 -29
- package/dist/layout/Sidenav.svelte +712 -712
- package/dist/styles/components.css +199 -199
- package/dist/styles/dark.css +146 -146
- package/dist/styles/index.css +116 -116
- package/dist/styles/reset.css +110 -110
- package/dist/styles/semantic.css +86 -86
- package/dist/styles/tokens.css +203 -197
- package/dist/styles/utilities.css +523 -523
- package/dist/ui/Accordion.svelte +289 -289
- package/dist/ui/ActionIcon.svelte +76 -76
- package/dist/ui/Badge.svelte +329 -279
- package/dist/ui/Breadcrumbs.svelte +131 -131
- package/dist/ui/Button.svelte +432 -370
- package/dist/ui/ButtonVariant.d.ts +1 -1
- package/dist/ui/Dialog.svelte +307 -307
- package/dist/ui/Drawer.svelte +524 -524
- package/dist/ui/Dropdown.svelte +97 -97
- package/dist/ui/Dropzone.svelte +122 -122
- package/dist/ui/Link.svelte +32 -32
- package/dist/ui/Loader.svelte +70 -70
- package/dist/ui/LoadingOverlay.svelte +53 -53
- package/dist/ui/Pagination.svelte +135 -135
- package/dist/ui/Popover.svelte +225 -225
- package/dist/ui/Progress.svelte +191 -191
- package/dist/ui/RingProgress.svelte +141 -141
- package/dist/ui/Skeleton.svelte +85 -85
- package/dist/ui/Stepper.svelte +355 -355
- package/dist/ui/Table.svelte +345 -345
- package/dist/ui/Tabs.svelte +146 -146
- package/dist/ui/ThemeSwitcher.svelte +39 -39
- package/dist/ui/Timeline.svelte +225 -225
- package/dist/ui/Toaster.svelte +6 -6
- package/dist/ui/Tooltip.svelte +434 -434
- package/package.json +14 -14
package/dist/ui/Tooltip.svelte
CHANGED
|
@@ -1,435 +1,435 @@
|
|
|
1
|
-
<script lang="ts">
|
|
2
|
-
import type { Snippet } from 'svelte';
|
|
3
|
-
import { fade } from 'svelte/transition';
|
|
4
|
-
|
|
5
|
-
interface Props {
|
|
6
|
-
/** The element to attach the tooltip to */
|
|
7
|
-
children: Snippet;
|
|
8
|
-
/** Rich content using a Svelte snippet */
|
|
9
|
-
tooltipContent?: Snippet;
|
|
10
|
-
/** Simple text content (alternative to snippet) */
|
|
11
|
-
text?: string;
|
|
12
|
-
/** HTML content (use with caution - sanitize user input) */
|
|
13
|
-
html?: string;
|
|
14
|
-
/** Preferred tooltip position */
|
|
15
|
-
location?: 'top' | 'right' | 'bottom' | 'left' | 'top-start' | 'top-end' | 'right-start' | 'right-end' | 'bottom-start' | 'bottom-end' | 'left-start' | 'left-end';
|
|
16
|
-
/** Maximum width of tooltip */
|
|
17
|
-
maxWidth?: string;
|
|
18
|
-
/** Offset distance from target element (px) */
|
|
19
|
-
offsetDistance?: number;
|
|
20
|
-
/** Show arrow pointing to target */
|
|
21
|
-
showArrow?: boolean;
|
|
22
|
-
/** Delay before showing tooltip (ms) */
|
|
23
|
-
delay?: number;
|
|
24
|
-
/** Callback when tooltip opens/closes */
|
|
25
|
-
onOpen?: (open: boolean) => void;
|
|
26
|
-
/** Disable the tooltip */
|
|
27
|
-
disabled?: boolean;
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
let {
|
|
31
|
-
children,
|
|
32
|
-
tooltipContent,
|
|
33
|
-
text,
|
|
34
|
-
html,
|
|
35
|
-
location = 'top',
|
|
36
|
-
maxWidth = '300px',
|
|
37
|
-
offsetDistance = 4,
|
|
38
|
-
showArrow = true,
|
|
39
|
-
delay = 0,
|
|
40
|
-
onOpen,
|
|
41
|
-
disabled = false
|
|
42
|
-
}: Props = $props();
|
|
43
|
-
|
|
44
|
-
// State
|
|
45
|
-
let open = $state(false);
|
|
46
|
-
let referenceElement: HTMLElement | null = $state(null);
|
|
47
|
-
let tooltipElement: HTMLElement | null = $state(null);
|
|
48
|
-
let arrowElement: HTMLElement | null = $state(null);
|
|
49
|
-
let timeoutId: number | null = null;
|
|
50
|
-
|
|
51
|
-
// Position state
|
|
52
|
-
let tooltipStyle = $state('');
|
|
53
|
-
let arrowStyle = $state('');
|
|
54
|
-
let actualPlacement = $state<'top' | 'right' | 'bottom' | 'left' | 'top-start' | 'top-end' | 'right-start' | 'right-end' | 'bottom-start' | 'bottom-end' | 'left-start' | 'left-end'>('top');
|
|
55
|
-
|
|
56
|
-
// Sync actualPlacement with location prop
|
|
57
|
-
$effect(() => {
|
|
58
|
-
actualPlacement = location;
|
|
59
|
-
});
|
|
60
|
-
|
|
61
|
-
// Check if tooltip has any content
|
|
62
|
-
const hasContent = $derived(!!(text || html || tooltipContent));
|
|
63
|
-
|
|
64
|
-
function handleMouseEnter() {
|
|
65
|
-
if (disabled || !hasContent) return;
|
|
66
|
-
|
|
67
|
-
if (delay > 0) {
|
|
68
|
-
timeoutId = window.setTimeout(() => {
|
|
69
|
-
showTooltip();
|
|
70
|
-
}, delay);
|
|
71
|
-
} else {
|
|
72
|
-
showTooltip();
|
|
73
|
-
}
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
function handleMouseLeave() {
|
|
77
|
-
if (timeoutId) {
|
|
78
|
-
clearTimeout(timeoutId);
|
|
79
|
-
timeoutId = null;
|
|
80
|
-
}
|
|
81
|
-
hideTooltip();
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
function showTooltip() {
|
|
85
|
-
open = true;
|
|
86
|
-
onOpen?.(true);
|
|
87
|
-
|
|
88
|
-
// Position tooltip after it's rendered
|
|
89
|
-
// Use multiple RAF calls and verify dimensions to ensure layout is complete
|
|
90
|
-
requestAnimationFrame(() => {
|
|
91
|
-
requestAnimationFrame(() => {
|
|
92
|
-
if (referenceElement && tooltipElement) {
|
|
93
|
-
positionTooltip();
|
|
94
|
-
|
|
95
|
-
// Verify and reposition if dimensions changed
|
|
96
|
-
requestAnimationFrame(() => {
|
|
97
|
-
if (tooltipElement) {
|
|
98
|
-
const rect = tooltipElement.getBoundingClientRect();
|
|
99
|
-
if (rect.width > 0 && rect.height > 0) {
|
|
100
|
-
positionTooltip();
|
|
101
|
-
}
|
|
102
|
-
}
|
|
103
|
-
});
|
|
104
|
-
}
|
|
105
|
-
});
|
|
106
|
-
});
|
|
107
|
-
}
|
|
108
|
-
|
|
109
|
-
function hideTooltip() {
|
|
110
|
-
open = false;
|
|
111
|
-
onOpen?.(false);
|
|
112
|
-
}
|
|
113
|
-
|
|
114
|
-
function positionTooltip() {
|
|
115
|
-
if (!referenceElement || !tooltipElement) return;
|
|
116
|
-
|
|
117
|
-
const refRect = referenceElement.getBoundingClientRect();
|
|
118
|
-
const tooltipRect = tooltipElement.getBoundingClientRect();
|
|
119
|
-
const arrowSize = showArrow ? 12 : 0;
|
|
120
|
-
const offset = offsetDistance + arrowSize;
|
|
121
|
-
|
|
122
|
-
let position = calculatePosition(location, refRect, tooltipRect, offset);
|
|
123
|
-
|
|
124
|
-
// Check if tooltip would overflow viewport and flip if needed
|
|
125
|
-
const flippedPosition = checkAndFlipPosition(position, tooltipRect, location, refRect, offset);
|
|
126
|
-
position = flippedPosition.position;
|
|
127
|
-
actualPlacement = flippedPosition.placement as typeof actualPlacement;
|
|
128
|
-
|
|
129
|
-
// Apply position
|
|
130
|
-
tooltipStyle = `left: ${position.left}px; top: ${position.top}px;`;
|
|
131
|
-
|
|
132
|
-
// Position arrow
|
|
133
|
-
if (showArrow && arrowElement) {
|
|
134
|
-
arrowStyle = calculateArrowPosition(actualPlacement, refRect, position);
|
|
135
|
-
}
|
|
136
|
-
}
|
|
137
|
-
|
|
138
|
-
function calculatePosition(
|
|
139
|
-
placement: string,
|
|
140
|
-
refRect: DOMRect,
|
|
141
|
-
tooltipRect: DOMRect,
|
|
142
|
-
offset: number
|
|
143
|
-
): { left: number; top: number } {
|
|
144
|
-
let left = 0;
|
|
145
|
-
let top = 0;
|
|
146
|
-
|
|
147
|
-
const [side, alignment] = placement.split('-');
|
|
148
|
-
|
|
149
|
-
switch (side) {
|
|
150
|
-
case 'top':
|
|
151
|
-
top = refRect.top - tooltipRect.height - offset;
|
|
152
|
-
left = refRect.left + refRect.width / 2 - tooltipRect.width / 2;
|
|
153
|
-
if (alignment === 'start') left = refRect.left;
|
|
154
|
-
if (alignment === 'end') left = refRect.right - tooltipRect.width;
|
|
155
|
-
break;
|
|
156
|
-
|
|
157
|
-
case 'bottom':
|
|
158
|
-
top = refRect.bottom + offset;
|
|
159
|
-
left = refRect.left + refRect.width / 2 - tooltipRect.width / 2;
|
|
160
|
-
if (alignment === 'start') left = refRect.left;
|
|
161
|
-
if (alignment === 'end') left = refRect.right - tooltipRect.width;
|
|
162
|
-
break;
|
|
163
|
-
|
|
164
|
-
case 'left':
|
|
165
|
-
left = refRect.left - tooltipRect.width - offset;
|
|
166
|
-
top = refRect.top + refRect.height / 2 - tooltipRect.height / 2;
|
|
167
|
-
if (alignment === 'start') top = refRect.top;
|
|
168
|
-
if (alignment === 'end') top = refRect.bottom - tooltipRect.height;
|
|
169
|
-
break;
|
|
170
|
-
|
|
171
|
-
case 'right':
|
|
172
|
-
left = refRect.right + offset;
|
|
173
|
-
top = refRect.top + refRect.height / 2 - tooltipRect.height / 2;
|
|
174
|
-
if (alignment === 'start') top = refRect.top;
|
|
175
|
-
if (alignment === 'end') top = refRect.bottom - tooltipRect.height;
|
|
176
|
-
break;
|
|
177
|
-
}
|
|
178
|
-
|
|
179
|
-
return { left, top };
|
|
180
|
-
}
|
|
181
|
-
|
|
182
|
-
function checkAndFlipPosition(
|
|
183
|
-
position: { left: number; top: number },
|
|
184
|
-
tooltipRect: DOMRect,
|
|
185
|
-
placement: string,
|
|
186
|
-
refRect: DOMRect,
|
|
187
|
-
offset: number
|
|
188
|
-
): { position: { left: number; top: number }; placement: string } {
|
|
189
|
-
const viewportWidth = window.innerWidth;
|
|
190
|
-
const viewportHeight = window.innerHeight;
|
|
191
|
-
const padding = 5;
|
|
192
|
-
|
|
193
|
-
let newPosition = { ...position };
|
|
194
|
-
let newPlacement = placement;
|
|
195
|
-
const [side] = placement.split('-');
|
|
196
|
-
|
|
197
|
-
// Check vertical overflow
|
|
198
|
-
if (side === 'top' && position.top < padding) {
|
|
199
|
-
// Flip to bottom
|
|
200
|
-
newPlacement = placement.replace('top', 'bottom');
|
|
201
|
-
newPosition = calculatePosition(newPlacement, refRect, tooltipRect, offset);
|
|
202
|
-
} else if (side === 'bottom' && position.top + tooltipRect.height > viewportHeight - padding) {
|
|
203
|
-
// Flip to top
|
|
204
|
-
newPlacement = placement.replace('bottom', 'top');
|
|
205
|
-
newPosition = calculatePosition(newPlacement, refRect, tooltipRect, offset);
|
|
206
|
-
}
|
|
207
|
-
|
|
208
|
-
// Check horizontal overflow
|
|
209
|
-
if (side === 'left' && position.left < padding) {
|
|
210
|
-
// Flip to right
|
|
211
|
-
newPlacement = placement.replace('left', 'right');
|
|
212
|
-
newPosition = calculatePosition(newPlacement, refRect, tooltipRect, offset);
|
|
213
|
-
} else if (side === 'right' && position.left + tooltipRect.width > viewportWidth - padding) {
|
|
214
|
-
// Flip to left
|
|
215
|
-
newPlacement = placement.replace('right', 'left');
|
|
216
|
-
newPosition = calculatePosition(newPlacement, refRect, tooltipRect, offset);
|
|
217
|
-
}
|
|
218
|
-
|
|
219
|
-
// Shift within bounds if still overflowing
|
|
220
|
-
if (newPosition.left < padding) {
|
|
221
|
-
newPosition.left = padding;
|
|
222
|
-
} else if (newPosition.left + tooltipRect.width > viewportWidth - padding) {
|
|
223
|
-
newPosition.left = viewportWidth - tooltipRect.width - padding;
|
|
224
|
-
}
|
|
225
|
-
|
|
226
|
-
if (newPosition.top < padding) {
|
|
227
|
-
newPosition.top = padding;
|
|
228
|
-
} else if (newPosition.top + tooltipRect.height > viewportHeight - padding) {
|
|
229
|
-
newPosition.top = viewportHeight - tooltipRect.height - padding;
|
|
230
|
-
}
|
|
231
|
-
|
|
232
|
-
return { position: newPosition, placement: newPlacement };
|
|
233
|
-
}
|
|
234
|
-
|
|
235
|
-
function calculateArrowPosition(
|
|
236
|
-
placement: string,
|
|
237
|
-
refRect: DOMRect,
|
|
238
|
-
tooltipPos: { left: number; top: number }
|
|
239
|
-
): string {
|
|
240
|
-
const [side] = placement.split('-');
|
|
241
|
-
|
|
242
|
-
const refCenter = {
|
|
243
|
-
x: refRect.left + refRect.width / 2,
|
|
244
|
-
y: refRect.top + refRect.height / 2
|
|
245
|
-
};
|
|
246
|
-
|
|
247
|
-
const tooltipHeight = tooltipElement?.offsetHeight || 0;
|
|
248
|
-
const tooltipWidth = tooltipElement?.offsetWidth || 0;
|
|
249
|
-
|
|
250
|
-
// Calculate arrow center position relative to tooltip
|
|
251
|
-
const arrowX = refCenter.x - tooltipPos.left;
|
|
252
|
-
const arrowY = refCenter.y - tooltipPos.top;
|
|
253
|
-
|
|
254
|
-
switch (side) {
|
|
255
|
-
case 'top':
|
|
256
|
-
// Arrow at bottom edge, pointing down
|
|
257
|
-
return `
|
|
258
|
-
left: ${arrowX}px;
|
|
259
|
-
top: ${tooltipHeight}px;
|
|
260
|
-
border-left: 12px solid transparent;
|
|
261
|
-
border-right: 12px solid transparent;
|
|
262
|
-
border-top: 12px solid var(--tooltip-bg);
|
|
263
|
-
transform: translateX(-50%) translateY(-50%);
|
|
264
|
-
`;
|
|
265
|
-
|
|
266
|
-
case 'bottom':
|
|
267
|
-
// Arrow at top edge, pointing up
|
|
268
|
-
return `
|
|
269
|
-
left: ${arrowX}px;
|
|
270
|
-
top: 0px;
|
|
271
|
-
border-left: 12px solid transparent;
|
|
272
|
-
border-right: 12px solid transparent;
|
|
273
|
-
border-bottom: 12px solid var(--tooltip-bg);
|
|
274
|
-
transform: translateX(-50%) translateY(-50%);
|
|
275
|
-
`;
|
|
276
|
-
|
|
277
|
-
case 'left':
|
|
278
|
-
// Arrow at right edge, pointing left
|
|
279
|
-
return `
|
|
280
|
-
left: ${tooltipWidth}px;
|
|
281
|
-
top: ${arrowY}px;
|
|
282
|
-
border-top: 12px solid transparent;
|
|
283
|
-
border-bottom: 12px solid transparent;
|
|
284
|
-
border-left: 12px solid var(--tooltip-bg);
|
|
285
|
-
transform: translateX(-50%) translateY(-50%);
|
|
286
|
-
`;
|
|
287
|
-
|
|
288
|
-
case 'right':
|
|
289
|
-
// Arrow at left edge, pointing right
|
|
290
|
-
return `
|
|
291
|
-
left: 0px;
|
|
292
|
-
top: ${arrowY}px;
|
|
293
|
-
border-top: 12px solid transparent;
|
|
294
|
-
border-bottom: 12px solid transparent;
|
|
295
|
-
border-right: 12px solid var(--tooltip-bg);
|
|
296
|
-
transform: translateX(-50%) translateY(-50%);
|
|
297
|
-
`;
|
|
298
|
-
|
|
299
|
-
default:
|
|
300
|
-
return '';
|
|
301
|
-
}
|
|
302
|
-
}
|
|
303
|
-
|
|
304
|
-
function handleKeydown(event: KeyboardEvent) {
|
|
305
|
-
if (event.key === 'Escape' && open) {
|
|
306
|
-
hideTooltip();
|
|
307
|
-
}
|
|
308
|
-
}
|
|
309
|
-
|
|
310
|
-
$effect(() => {
|
|
311
|
-
if (open) {
|
|
312
|
-
window.addEventListener('keydown', handleKeydown);
|
|
313
|
-
window.addEventListener('scroll', positionTooltip, true);
|
|
314
|
-
window.addEventListener('resize', positionTooltip);
|
|
315
|
-
|
|
316
|
-
return () => {
|
|
317
|
-
window.removeEventListener('keydown', handleKeydown);
|
|
318
|
-
window.removeEventListener('scroll', positionTooltip, true);
|
|
319
|
-
window.removeEventListener('resize', positionTooltip);
|
|
320
|
-
};
|
|
321
|
-
}
|
|
322
|
-
});
|
|
323
|
-
</script>
|
|
324
|
-
|
|
325
|
-
<div class="tooltip-v2-container">
|
|
326
|
-
<!-- Reference Element -->
|
|
327
|
-
<div
|
|
328
|
-
bind:this={referenceElement}
|
|
329
|
-
class="tooltip-v2-trigger"
|
|
330
|
-
onmouseenter={handleMouseEnter}
|
|
331
|
-
onmouseleave={handleMouseLeave}
|
|
332
|
-
onfocus={handleMouseEnter}
|
|
333
|
-
onblur={handleMouseLeave}
|
|
334
|
-
role="button"
|
|
335
|
-
tabindex="0"
|
|
336
|
-
>
|
|
337
|
-
{@render children()}
|
|
338
|
-
</div>
|
|
339
|
-
|
|
340
|
-
<!-- Floating Tooltip -->
|
|
341
|
-
{#if open && hasContent && !disabled}
|
|
342
|
-
<div
|
|
343
|
-
bind:this={tooltipElement}
|
|
344
|
-
class="tooltip-v2-floating"
|
|
345
|
-
style={tooltipStyle}
|
|
346
|
-
role="tooltip"
|
|
347
|
-
transition:fade={{ duration: 150 }}
|
|
348
|
-
>
|
|
349
|
-
<div class="tooltip-v2-content" style="max-width: {maxWidth}">
|
|
350
|
-
{#if text}
|
|
351
|
-
<span>{text}</span>
|
|
352
|
-
{:else if html}
|
|
353
|
-
{@html html}
|
|
354
|
-
{:else if tooltipContent}
|
|
355
|
-
{@render tooltipContent()}
|
|
356
|
-
{/if}
|
|
357
|
-
</div>
|
|
358
|
-
|
|
359
|
-
{#if showArrow}
|
|
360
|
-
<div bind:this={arrowElement} class="tooltip-v2-arrow" style={arrowStyle}></div>
|
|
361
|
-
{/if}
|
|
362
|
-
</div>
|
|
363
|
-
{/if}
|
|
364
|
-
</div>
|
|
365
|
-
|
|
366
|
-
<style>
|
|
367
|
-
.tooltip-v2-container {
|
|
368
|
-
display: inline-block;
|
|
369
|
-
}
|
|
370
|
-
|
|
371
|
-
.tooltip-v2-trigger {
|
|
372
|
-
width: auto;
|
|
373
|
-
display: inline-block;
|
|
374
|
-
}
|
|
375
|
-
|
|
376
|
-
.tooltip-v2-floating {
|
|
377
|
-
position: fixed;
|
|
378
|
-
z-index: var(--pui-z-tooltip);
|
|
379
|
-
pointer-events: none;
|
|
380
|
-
--tooltip-bg: var(--pui-color-gray-200);
|
|
381
|
-
--tooltip-text: var(--pui-color-gray-800);
|
|
382
|
-
}
|
|
383
|
-
|
|
384
|
-
:global(.dark) .tooltip-v2-floating {
|
|
385
|
-
--tooltip-bg: var(--pui-color-gray-800);
|
|
386
|
-
--tooltip-text: var(--pui-color-gray-200);
|
|
387
|
-
}
|
|
388
|
-
|
|
389
|
-
.tooltip-v2-content {
|
|
390
|
-
background: var(--tooltip-bg);
|
|
391
|
-
color: var(--tooltip-text);
|
|
392
|
-
padding: var(--pui-spacing-2) var(--pui-spacing-3);
|
|
393
|
-
border-radius: var(--pui-radius-lg);
|
|
394
|
-
font-size: var(--pui-font-size-sm);
|
|
395
|
-
line-height: var(--pui-line-height-normal);
|
|
396
|
-
position: relative;
|
|
397
|
-
}
|
|
398
|
-
|
|
399
|
-
:global(.dark) .tooltip-v2-content {
|
|
400
|
-
box-shadow: var(--pui-shadow-md);
|
|
401
|
-
}
|
|
402
|
-
|
|
403
|
-
.tooltip-v2-arrow {
|
|
404
|
-
position: absolute;
|
|
405
|
-
width: 0;
|
|
406
|
-
height: 0;
|
|
407
|
-
pointer-events: none;
|
|
408
|
-
}
|
|
409
|
-
|
|
410
|
-
/* Handle HTML content spacing */
|
|
411
|
-
.tooltip-v2-content :global(p) {
|
|
412
|
-
margin: 0;
|
|
413
|
-
}
|
|
414
|
-
|
|
415
|
-
.tooltip-v2-content :global(p + p) {
|
|
416
|
-
margin-top: 0.5rem;
|
|
417
|
-
}
|
|
418
|
-
|
|
419
|
-
.tooltip-v2-content :global(ul),
|
|
420
|
-
.tooltip-v2-content :global(ol) {
|
|
421
|
-
margin: 0;
|
|
422
|
-
padding-left: 1.25rem;
|
|
423
|
-
}
|
|
424
|
-
|
|
425
|
-
.tooltip-v2-content :global(code) {
|
|
426
|
-
background: rgba(255, 255, 255, 0.1);
|
|
427
|
-
padding: 0.125rem 0.25rem;
|
|
428
|
-
border-radius: 3px;
|
|
429
|
-
font-size: 0.85em;
|
|
430
|
-
}
|
|
431
|
-
|
|
432
|
-
:global(.dark) .tooltip-v2-content :global(code) {
|
|
433
|
-
background: rgba(255, 255, 255, 0.05);
|
|
434
|
-
}
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import type { Snippet } from 'svelte';
|
|
3
|
+
import { fade } from 'svelte/transition';
|
|
4
|
+
|
|
5
|
+
interface Props {
|
|
6
|
+
/** The element to attach the tooltip to */
|
|
7
|
+
children: Snippet;
|
|
8
|
+
/** Rich content using a Svelte snippet */
|
|
9
|
+
tooltipContent?: Snippet;
|
|
10
|
+
/** Simple text content (alternative to snippet) */
|
|
11
|
+
text?: string;
|
|
12
|
+
/** HTML content (use with caution - sanitize user input) */
|
|
13
|
+
html?: string;
|
|
14
|
+
/** Preferred tooltip position */
|
|
15
|
+
location?: 'top' | 'right' | 'bottom' | 'left' | 'top-start' | 'top-end' | 'right-start' | 'right-end' | 'bottom-start' | 'bottom-end' | 'left-start' | 'left-end';
|
|
16
|
+
/** Maximum width of tooltip */
|
|
17
|
+
maxWidth?: string;
|
|
18
|
+
/** Offset distance from target element (px) */
|
|
19
|
+
offsetDistance?: number;
|
|
20
|
+
/** Show arrow pointing to target */
|
|
21
|
+
showArrow?: boolean;
|
|
22
|
+
/** Delay before showing tooltip (ms) */
|
|
23
|
+
delay?: number;
|
|
24
|
+
/** Callback when tooltip opens/closes */
|
|
25
|
+
onOpen?: (open: boolean) => void;
|
|
26
|
+
/** Disable the tooltip */
|
|
27
|
+
disabled?: boolean;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
let {
|
|
31
|
+
children,
|
|
32
|
+
tooltipContent,
|
|
33
|
+
text,
|
|
34
|
+
html,
|
|
35
|
+
location = 'top',
|
|
36
|
+
maxWidth = '300px',
|
|
37
|
+
offsetDistance = 4,
|
|
38
|
+
showArrow = true,
|
|
39
|
+
delay = 0,
|
|
40
|
+
onOpen,
|
|
41
|
+
disabled = false
|
|
42
|
+
}: Props = $props();
|
|
43
|
+
|
|
44
|
+
// State
|
|
45
|
+
let open = $state(false);
|
|
46
|
+
let referenceElement: HTMLElement | null = $state(null);
|
|
47
|
+
let tooltipElement: HTMLElement | null = $state(null);
|
|
48
|
+
let arrowElement: HTMLElement | null = $state(null);
|
|
49
|
+
let timeoutId: number | null = null;
|
|
50
|
+
|
|
51
|
+
// Position state
|
|
52
|
+
let tooltipStyle = $state('');
|
|
53
|
+
let arrowStyle = $state('');
|
|
54
|
+
let actualPlacement = $state<'top' | 'right' | 'bottom' | 'left' | 'top-start' | 'top-end' | 'right-start' | 'right-end' | 'bottom-start' | 'bottom-end' | 'left-start' | 'left-end'>('top');
|
|
55
|
+
|
|
56
|
+
// Sync actualPlacement with location prop
|
|
57
|
+
$effect(() => {
|
|
58
|
+
actualPlacement = location;
|
|
59
|
+
});
|
|
60
|
+
|
|
61
|
+
// Check if tooltip has any content
|
|
62
|
+
const hasContent = $derived(!!(text || html || tooltipContent));
|
|
63
|
+
|
|
64
|
+
function handleMouseEnter() {
|
|
65
|
+
if (disabled || !hasContent) return;
|
|
66
|
+
|
|
67
|
+
if (delay > 0) {
|
|
68
|
+
timeoutId = window.setTimeout(() => {
|
|
69
|
+
showTooltip();
|
|
70
|
+
}, delay);
|
|
71
|
+
} else {
|
|
72
|
+
showTooltip();
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
function handleMouseLeave() {
|
|
77
|
+
if (timeoutId) {
|
|
78
|
+
clearTimeout(timeoutId);
|
|
79
|
+
timeoutId = null;
|
|
80
|
+
}
|
|
81
|
+
hideTooltip();
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
function showTooltip() {
|
|
85
|
+
open = true;
|
|
86
|
+
onOpen?.(true);
|
|
87
|
+
|
|
88
|
+
// Position tooltip after it's rendered
|
|
89
|
+
// Use multiple RAF calls and verify dimensions to ensure layout is complete
|
|
90
|
+
requestAnimationFrame(() => {
|
|
91
|
+
requestAnimationFrame(() => {
|
|
92
|
+
if (referenceElement && tooltipElement) {
|
|
93
|
+
positionTooltip();
|
|
94
|
+
|
|
95
|
+
// Verify and reposition if dimensions changed
|
|
96
|
+
requestAnimationFrame(() => {
|
|
97
|
+
if (tooltipElement) {
|
|
98
|
+
const rect = tooltipElement.getBoundingClientRect();
|
|
99
|
+
if (rect.width > 0 && rect.height > 0) {
|
|
100
|
+
positionTooltip();
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
});
|
|
104
|
+
}
|
|
105
|
+
});
|
|
106
|
+
});
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
function hideTooltip() {
|
|
110
|
+
open = false;
|
|
111
|
+
onOpen?.(false);
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
function positionTooltip() {
|
|
115
|
+
if (!referenceElement || !tooltipElement) return;
|
|
116
|
+
|
|
117
|
+
const refRect = referenceElement.getBoundingClientRect();
|
|
118
|
+
const tooltipRect = tooltipElement.getBoundingClientRect();
|
|
119
|
+
const arrowSize = showArrow ? 12 : 0;
|
|
120
|
+
const offset = offsetDistance + arrowSize;
|
|
121
|
+
|
|
122
|
+
let position = calculatePosition(location, refRect, tooltipRect, offset);
|
|
123
|
+
|
|
124
|
+
// Check if tooltip would overflow viewport and flip if needed
|
|
125
|
+
const flippedPosition = checkAndFlipPosition(position, tooltipRect, location, refRect, offset);
|
|
126
|
+
position = flippedPosition.position;
|
|
127
|
+
actualPlacement = flippedPosition.placement as typeof actualPlacement;
|
|
128
|
+
|
|
129
|
+
// Apply position
|
|
130
|
+
tooltipStyle = `left: ${position.left}px; top: ${position.top}px;`;
|
|
131
|
+
|
|
132
|
+
// Position arrow
|
|
133
|
+
if (showArrow && arrowElement) {
|
|
134
|
+
arrowStyle = calculateArrowPosition(actualPlacement, refRect, position);
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
function calculatePosition(
|
|
139
|
+
placement: string,
|
|
140
|
+
refRect: DOMRect,
|
|
141
|
+
tooltipRect: DOMRect,
|
|
142
|
+
offset: number
|
|
143
|
+
): { left: number; top: number } {
|
|
144
|
+
let left = 0;
|
|
145
|
+
let top = 0;
|
|
146
|
+
|
|
147
|
+
const [side, alignment] = placement.split('-');
|
|
148
|
+
|
|
149
|
+
switch (side) {
|
|
150
|
+
case 'top':
|
|
151
|
+
top = refRect.top - tooltipRect.height - offset;
|
|
152
|
+
left = refRect.left + refRect.width / 2 - tooltipRect.width / 2;
|
|
153
|
+
if (alignment === 'start') left = refRect.left;
|
|
154
|
+
if (alignment === 'end') left = refRect.right - tooltipRect.width;
|
|
155
|
+
break;
|
|
156
|
+
|
|
157
|
+
case 'bottom':
|
|
158
|
+
top = refRect.bottom + offset;
|
|
159
|
+
left = refRect.left + refRect.width / 2 - tooltipRect.width / 2;
|
|
160
|
+
if (alignment === 'start') left = refRect.left;
|
|
161
|
+
if (alignment === 'end') left = refRect.right - tooltipRect.width;
|
|
162
|
+
break;
|
|
163
|
+
|
|
164
|
+
case 'left':
|
|
165
|
+
left = refRect.left - tooltipRect.width - offset;
|
|
166
|
+
top = refRect.top + refRect.height / 2 - tooltipRect.height / 2;
|
|
167
|
+
if (alignment === 'start') top = refRect.top;
|
|
168
|
+
if (alignment === 'end') top = refRect.bottom - tooltipRect.height;
|
|
169
|
+
break;
|
|
170
|
+
|
|
171
|
+
case 'right':
|
|
172
|
+
left = refRect.right + offset;
|
|
173
|
+
top = refRect.top + refRect.height / 2 - tooltipRect.height / 2;
|
|
174
|
+
if (alignment === 'start') top = refRect.top;
|
|
175
|
+
if (alignment === 'end') top = refRect.bottom - tooltipRect.height;
|
|
176
|
+
break;
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
return { left, top };
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
function checkAndFlipPosition(
|
|
183
|
+
position: { left: number; top: number },
|
|
184
|
+
tooltipRect: DOMRect,
|
|
185
|
+
placement: string,
|
|
186
|
+
refRect: DOMRect,
|
|
187
|
+
offset: number
|
|
188
|
+
): { position: { left: number; top: number }; placement: string } {
|
|
189
|
+
const viewportWidth = window.innerWidth;
|
|
190
|
+
const viewportHeight = window.innerHeight;
|
|
191
|
+
const padding = 5;
|
|
192
|
+
|
|
193
|
+
let newPosition = { ...position };
|
|
194
|
+
let newPlacement = placement;
|
|
195
|
+
const [side] = placement.split('-');
|
|
196
|
+
|
|
197
|
+
// Check vertical overflow
|
|
198
|
+
if (side === 'top' && position.top < padding) {
|
|
199
|
+
// Flip to bottom
|
|
200
|
+
newPlacement = placement.replace('top', 'bottom');
|
|
201
|
+
newPosition = calculatePosition(newPlacement, refRect, tooltipRect, offset);
|
|
202
|
+
} else if (side === 'bottom' && position.top + tooltipRect.height > viewportHeight - padding) {
|
|
203
|
+
// Flip to top
|
|
204
|
+
newPlacement = placement.replace('bottom', 'top');
|
|
205
|
+
newPosition = calculatePosition(newPlacement, refRect, tooltipRect, offset);
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
// Check horizontal overflow
|
|
209
|
+
if (side === 'left' && position.left < padding) {
|
|
210
|
+
// Flip to right
|
|
211
|
+
newPlacement = placement.replace('left', 'right');
|
|
212
|
+
newPosition = calculatePosition(newPlacement, refRect, tooltipRect, offset);
|
|
213
|
+
} else if (side === 'right' && position.left + tooltipRect.width > viewportWidth - padding) {
|
|
214
|
+
// Flip to left
|
|
215
|
+
newPlacement = placement.replace('right', 'left');
|
|
216
|
+
newPosition = calculatePosition(newPlacement, refRect, tooltipRect, offset);
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
// Shift within bounds if still overflowing
|
|
220
|
+
if (newPosition.left < padding) {
|
|
221
|
+
newPosition.left = padding;
|
|
222
|
+
} else if (newPosition.left + tooltipRect.width > viewportWidth - padding) {
|
|
223
|
+
newPosition.left = viewportWidth - tooltipRect.width - padding;
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
if (newPosition.top < padding) {
|
|
227
|
+
newPosition.top = padding;
|
|
228
|
+
} else if (newPosition.top + tooltipRect.height > viewportHeight - padding) {
|
|
229
|
+
newPosition.top = viewportHeight - tooltipRect.height - padding;
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
return { position: newPosition, placement: newPlacement };
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
function calculateArrowPosition(
|
|
236
|
+
placement: string,
|
|
237
|
+
refRect: DOMRect,
|
|
238
|
+
tooltipPos: { left: number; top: number }
|
|
239
|
+
): string {
|
|
240
|
+
const [side] = placement.split('-');
|
|
241
|
+
|
|
242
|
+
const refCenter = {
|
|
243
|
+
x: refRect.left + refRect.width / 2,
|
|
244
|
+
y: refRect.top + refRect.height / 2
|
|
245
|
+
};
|
|
246
|
+
|
|
247
|
+
const tooltipHeight = tooltipElement?.offsetHeight || 0;
|
|
248
|
+
const tooltipWidth = tooltipElement?.offsetWidth || 0;
|
|
249
|
+
|
|
250
|
+
// Calculate arrow center position relative to tooltip
|
|
251
|
+
const arrowX = refCenter.x - tooltipPos.left;
|
|
252
|
+
const arrowY = refCenter.y - tooltipPos.top;
|
|
253
|
+
|
|
254
|
+
switch (side) {
|
|
255
|
+
case 'top':
|
|
256
|
+
// Arrow at bottom edge, pointing down
|
|
257
|
+
return `
|
|
258
|
+
left: ${arrowX}px;
|
|
259
|
+
top: ${tooltipHeight}px;
|
|
260
|
+
border-left: 12px solid transparent;
|
|
261
|
+
border-right: 12px solid transparent;
|
|
262
|
+
border-top: 12px solid var(--tooltip-bg);
|
|
263
|
+
transform: translateX(-50%) translateY(-50%);
|
|
264
|
+
`;
|
|
265
|
+
|
|
266
|
+
case 'bottom':
|
|
267
|
+
// Arrow at top edge, pointing up
|
|
268
|
+
return `
|
|
269
|
+
left: ${arrowX}px;
|
|
270
|
+
top: 0px;
|
|
271
|
+
border-left: 12px solid transparent;
|
|
272
|
+
border-right: 12px solid transparent;
|
|
273
|
+
border-bottom: 12px solid var(--tooltip-bg);
|
|
274
|
+
transform: translateX(-50%) translateY(-50%);
|
|
275
|
+
`;
|
|
276
|
+
|
|
277
|
+
case 'left':
|
|
278
|
+
// Arrow at right edge, pointing left
|
|
279
|
+
return `
|
|
280
|
+
left: ${tooltipWidth}px;
|
|
281
|
+
top: ${arrowY}px;
|
|
282
|
+
border-top: 12px solid transparent;
|
|
283
|
+
border-bottom: 12px solid transparent;
|
|
284
|
+
border-left: 12px solid var(--tooltip-bg);
|
|
285
|
+
transform: translateX(-50%) translateY(-50%);
|
|
286
|
+
`;
|
|
287
|
+
|
|
288
|
+
case 'right':
|
|
289
|
+
// Arrow at left edge, pointing right
|
|
290
|
+
return `
|
|
291
|
+
left: 0px;
|
|
292
|
+
top: ${arrowY}px;
|
|
293
|
+
border-top: 12px solid transparent;
|
|
294
|
+
border-bottom: 12px solid transparent;
|
|
295
|
+
border-right: 12px solid var(--tooltip-bg);
|
|
296
|
+
transform: translateX(-50%) translateY(-50%);
|
|
297
|
+
`;
|
|
298
|
+
|
|
299
|
+
default:
|
|
300
|
+
return '';
|
|
301
|
+
}
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
function handleKeydown(event: KeyboardEvent) {
|
|
305
|
+
if (event.key === 'Escape' && open) {
|
|
306
|
+
hideTooltip();
|
|
307
|
+
}
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
$effect(() => {
|
|
311
|
+
if (open) {
|
|
312
|
+
window.addEventListener('keydown', handleKeydown);
|
|
313
|
+
window.addEventListener('scroll', positionTooltip, true);
|
|
314
|
+
window.addEventListener('resize', positionTooltip);
|
|
315
|
+
|
|
316
|
+
return () => {
|
|
317
|
+
window.removeEventListener('keydown', handleKeydown);
|
|
318
|
+
window.removeEventListener('scroll', positionTooltip, true);
|
|
319
|
+
window.removeEventListener('resize', positionTooltip);
|
|
320
|
+
};
|
|
321
|
+
}
|
|
322
|
+
});
|
|
323
|
+
</script>
|
|
324
|
+
|
|
325
|
+
<div class="tooltip-v2-container">
|
|
326
|
+
<!-- Reference Element -->
|
|
327
|
+
<div
|
|
328
|
+
bind:this={referenceElement}
|
|
329
|
+
class="tooltip-v2-trigger"
|
|
330
|
+
onmouseenter={handleMouseEnter}
|
|
331
|
+
onmouseleave={handleMouseLeave}
|
|
332
|
+
onfocus={handleMouseEnter}
|
|
333
|
+
onblur={handleMouseLeave}
|
|
334
|
+
role="button"
|
|
335
|
+
tabindex="0"
|
|
336
|
+
>
|
|
337
|
+
{@render children()}
|
|
338
|
+
</div>
|
|
339
|
+
|
|
340
|
+
<!-- Floating Tooltip -->
|
|
341
|
+
{#if open && hasContent && !disabled}
|
|
342
|
+
<div
|
|
343
|
+
bind:this={tooltipElement}
|
|
344
|
+
class="tooltip-v2-floating"
|
|
345
|
+
style={tooltipStyle}
|
|
346
|
+
role="tooltip"
|
|
347
|
+
transition:fade={{ duration: 150 }}
|
|
348
|
+
>
|
|
349
|
+
<div class="tooltip-v2-content" style="max-width: {maxWidth}">
|
|
350
|
+
{#if text}
|
|
351
|
+
<span>{text}</span>
|
|
352
|
+
{:else if html}
|
|
353
|
+
{@html html}
|
|
354
|
+
{:else if tooltipContent}
|
|
355
|
+
{@render tooltipContent()}
|
|
356
|
+
{/if}
|
|
357
|
+
</div>
|
|
358
|
+
|
|
359
|
+
{#if showArrow}
|
|
360
|
+
<div bind:this={arrowElement} class="tooltip-v2-arrow" style={arrowStyle}></div>
|
|
361
|
+
{/if}
|
|
362
|
+
</div>
|
|
363
|
+
{/if}
|
|
364
|
+
</div>
|
|
365
|
+
|
|
366
|
+
<style>
|
|
367
|
+
.tooltip-v2-container {
|
|
368
|
+
display: inline-block;
|
|
369
|
+
}
|
|
370
|
+
|
|
371
|
+
.tooltip-v2-trigger {
|
|
372
|
+
width: auto;
|
|
373
|
+
display: inline-block;
|
|
374
|
+
}
|
|
375
|
+
|
|
376
|
+
.tooltip-v2-floating {
|
|
377
|
+
position: fixed;
|
|
378
|
+
z-index: var(--pui-z-tooltip);
|
|
379
|
+
pointer-events: none;
|
|
380
|
+
--tooltip-bg: var(--pui-color-gray-200);
|
|
381
|
+
--tooltip-text: var(--pui-color-gray-800);
|
|
382
|
+
}
|
|
383
|
+
|
|
384
|
+
:global(.dark) .tooltip-v2-floating {
|
|
385
|
+
--tooltip-bg: var(--pui-color-gray-800);
|
|
386
|
+
--tooltip-text: var(--pui-color-gray-200);
|
|
387
|
+
}
|
|
388
|
+
|
|
389
|
+
.tooltip-v2-content {
|
|
390
|
+
background: var(--tooltip-bg);
|
|
391
|
+
color: var(--tooltip-text);
|
|
392
|
+
padding: var(--pui-spacing-2) var(--pui-spacing-3);
|
|
393
|
+
border-radius: var(--pui-radius-lg);
|
|
394
|
+
font-size: var(--pui-font-size-sm);
|
|
395
|
+
line-height: var(--pui-line-height-normal);
|
|
396
|
+
position: relative;
|
|
397
|
+
}
|
|
398
|
+
|
|
399
|
+
:global(.dark) .tooltip-v2-content {
|
|
400
|
+
box-shadow: var(--pui-shadow-md);
|
|
401
|
+
}
|
|
402
|
+
|
|
403
|
+
.tooltip-v2-arrow {
|
|
404
|
+
position: absolute;
|
|
405
|
+
width: 0;
|
|
406
|
+
height: 0;
|
|
407
|
+
pointer-events: none;
|
|
408
|
+
}
|
|
409
|
+
|
|
410
|
+
/* Handle HTML content spacing */
|
|
411
|
+
.tooltip-v2-content :global(p) {
|
|
412
|
+
margin: 0;
|
|
413
|
+
}
|
|
414
|
+
|
|
415
|
+
.tooltip-v2-content :global(p + p) {
|
|
416
|
+
margin-top: 0.5rem;
|
|
417
|
+
}
|
|
418
|
+
|
|
419
|
+
.tooltip-v2-content :global(ul),
|
|
420
|
+
.tooltip-v2-content :global(ol) {
|
|
421
|
+
margin: 0;
|
|
422
|
+
padding-left: 1.25rem;
|
|
423
|
+
}
|
|
424
|
+
|
|
425
|
+
.tooltip-v2-content :global(code) {
|
|
426
|
+
background: rgba(255, 255, 255, 0.1);
|
|
427
|
+
padding: 0.125rem 0.25rem;
|
|
428
|
+
border-radius: 3px;
|
|
429
|
+
font-size: 0.85em;
|
|
430
|
+
}
|
|
431
|
+
|
|
432
|
+
:global(.dark) .tooltip-v2-content :global(code) {
|
|
433
|
+
background: rgba(255, 255, 255, 0.05);
|
|
434
|
+
}
|
|
435
435
|
</style>
|