mirror of
https://github.com/Stirling-Tools/Stirling-PDF.git
synced 2025-08-24 13:19:23 +00:00
8.1 KiB
8.1 KiB
Tooltip Component
A flexible, accessible tooltip component that supports both regular positioning and special sidebar positioning logic with click-to-pin functionality. The tooltip is controlled by default, appearing on hover and pinning on click.
Features
- 🎯 Smart Positioning: Automatically positions tooltips to stay within viewport bounds
- 📱 Sidebar Support: Special positioning logic for sidebar/navigation elements
- ♿ Accessible: Works with mouse interactions and click-to-pin functionality
- 🎨 Customizable: Support for arrows, structured content, and custom JSX
- 🌙 Theme Support: Built-in dark mode and theme variable support
- ⚡ Performance: Memoized calculations and efficient event handling
- 📜 Scrollable: Content area scrolls when content exceeds max height
- 📌 Click-to-Pin: Click to pin tooltips open, click outside or the close button to unpin
- 🔗 Link Support: Full support for clickable links in descriptions, bullets, and body content
- 🎮 Controlled by Default: Always uses controlled state management for consistent behavior
- ⏱️ Hover Timing Controls: Optional long-hover requirement via
delayAppearance
anddelay
Behavior
Default Behavior (Controlled)
- Hover: Tooltips appear on hover with a small delay when leaving to prevent flickering
- Click: Click the trigger to pin the tooltip open
- Click tooltip: Pins the tooltip to keep it open
- Click close button: Unpins and closes the tooltip (red X button in top-right when pinned)
- Click outside: Unpins and closes the tooltip
- Visual indicator: Pinned tooltips have a blue border and close button
Manual Control (Optional)
- Use
open
andonOpenChange
props for complete external control - Useful for complex state management or custom interaction patterns
Basic Usage
import { Tooltip } from '@/components/shared';
function MyComponent() {
return (
<Tooltip content="This is a helpful tooltip">
<button>Hover me</button>
</Tooltip>
);
}
API Reference
Props
Prop | Type | Default | Description |
---|---|---|---|
content |
ReactNode |
- | Custom JSX content to display in the tooltip |
tips |
TooltipTip[] |
- | Structured content with title, description, bullets, and optional body |
children |
ReactElement |
required | Element that triggers the tooltip |
sidebarTooltip |
boolean |
false |
Enables special sidebar positioning logic |
position |
'right' | 'left' | 'top' | 'bottom' |
'right' |
Tooltip position (ignored if sidebarTooltip is true) |
offset |
number |
8 |
Distance in pixels between trigger and tooltip |
maxWidth |
number | string |
280 |
Maximum width constraint for the tooltip |
open |
boolean |
undefined |
External open state (makes component fully controlled) |
onOpenChange |
(open: boolean) => void |
undefined |
Callback for external control |
arrow |
boolean |
false |
Shows a small triangular arrow pointing to the trigger element |
portalTarget |
HTMLElement |
undefined |
DOM node to portal the tooltip into |
header |
{ title: string; logo?: ReactNode } |
- | Optional header with title and logo |
delay |
number |
0 |
Optional hover-open delay (ms). If omitted or 0, opens immediately |
TooltipTip Interface
interface TooltipTip {
title?: string; // Optional pill label
description?: string; // Optional description text (supports HTML including <a> tags)
bullets?: string[]; // Optional bullet points (supports HTML including <a> tags)
body?: React.ReactNode; // Optional custom JSX for this tip
}
Usage Examples
Default Behavior (Recommended)
// Simple tooltip with hover and click-to-pin
<Tooltip content="This tooltip appears on hover and pins on click">
<button>Hover me</button>
</Tooltip>
// Structured content with tips
<Tooltip
tips={[
{
title: "OCR Mode",
description: "Choose how to process text in your documents.",
bullets: [
"<strong>Auto</strong> skips pages that already contain text.",
"<strong>Force</strong> re-processes every page.",
"<strong>Strict</strong> stops if text is found.",
"<a href='https://docs.example.com' target='_blank'>Learn more</a>"
]
}
]}
header={{
title: "Basic Settings Overview",
logo: <img src="/logo.svg" alt="Logo" />
}}
>
<button>Settings</button>
</Tooltip>
Optional Hover Delay
// Show after a 1s hover
<Tooltip content="Appears after a long hover" delay={1000} />
// Custom long-hover duration (2 seconds)
<Tooltip content="Appears after 2s" delay={2000} />
Custom JSX Content
<Tooltip
content={
<div>
<h3>Custom Content</h3>
<p>Any JSX you want here</p>
<button>Action</button>
<a href="https://example.com">External link</a>
</div>
}
>
<button>Custom tooltip</button>
</Tooltip>
Mixed Content (Tips + Custom JSX)
<Tooltip
tips={[
{ title: "Section", description: "Description" }
]}
content={<div>Additional custom content below tips</div>}
>
<button>Mixed content</button>
</Tooltip>
Sidebar Tooltips
// For items in a sidebar/navigation
<Tooltip
content="This tooltip appears to the right of the sidebar"
sidebarTooltip={true}
>
<div className="sidebar-item">
📁 File Manager
</div>
</Tooltip>
With Arrows
<Tooltip
content="Tooltip with arrow pointing to trigger"
arrow={true}
position="top"
>
<button>Arrow tooltip</button>
</Tooltip>
Manual Control (Advanced)
function ManualControlTooltip() {
const [open, setOpen] = useState(false);
return (
<Tooltip
content="Fully controlled tooltip"
open={open}
onOpenChange={setOpen}
>
<button onClick={() => setOpen(!open)}>
Toggle tooltip
</button>
</Tooltip>
);
}
Click-to-Pin Interaction
How to Use (Default Behavior)
- Hover over the trigger element to show the tooltip
- Click the trigger element to pin the tooltip open
- Click the red X button in the top-right corner to close
- Click anywhere outside the tooltip to close
- Click the trigger again to toggle pin state
Visual States
- Unpinned: Normal tooltip appearance
- Pinned: Blue border, subtle glow, and close button (X) in top-right corner
Link Support
The tooltip fully supports clickable links in all content areas:
- Descriptions: Use
<a href="...">
in description strings - Bullets: Use
<a href="...">
in bullet point strings - Body: Use JSX
<a>
elements in the body ReactNode - Content: Use JSX
<a>
elements in custom content
Links automatically get proper styling with hover states and open in new tabs when using target="_blank"
.
Positioning Logic
Regular Tooltips
- Uses the
position
prop to determine initial placement - Automatically clamps to viewport boundaries
- Calculates optimal position based on trigger element's
getBoundingClientRect()
- Dynamic arrow positioning: Arrow stays aligned with trigger even when tooltip is clamped
Timing Details
- Opening uses
delay
(ms) if provided; otherwise opens immediately. Closing occurs immediately when the cursor leaves (unless pinned). - All internal timers are cleared on state changes, mouse transitions, and unmount to avoid overlaps.
- Only one tooltip can be open at a time; hovering a new trigger closes others immediately.
Sidebar Tooltips
- When
sidebarTooltip={true}
, horizontal positioning is locked to the right of the sidebar - Vertical positioning follows the trigger but clamps to viewport
- Smart sidebar detection: Uses
getSidebarInfo()
to determine which sidebar is active (tool panel vs quick access bar) and gets its exact position - Dynamic positioning: Adapts to whether the tool panel is expanded or collapsed
- Conditional display: Only shows tooltips when the tool panel is active (
sidebarInfo.isToolPanelActive
) - No arrows - sidebar tooltips don't show arrows