Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
53 changes: 53 additions & 0 deletions apps/web/vibes/soul/docs/tooltip.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
---
title: Tooltip
preview: tooltip-example
previewSize: sm
---

## Usage

{/* prettier-ignore-start */}

<CodeBlock lang="ts">{`

import { Tooltip } from '@/vibes/soul/primitives/tooltip';

function Usage() {
return (
<Tooltip trigger={<div>Tooltip trigger</div>}>
Tooltip content
</Tooltip>
);
}
`}
</CodeBlock>

{/* prettier-ignore-end */}

## API Reference

This comoponent uses the Sonner toast component. For more information, please refer to the [Sonner documentation](https://sonner.emilkowal.ski/).

### TooltipProps

| Prop | Type | Default |
| ------------------- | ---------------------------------------- | ---------- |
| `align` | `'center' \| 'end' \| 'start'` | `'center'` |
| `className` | `string` | |
| `delayDuration` | `number` | |
| `skipDelayDuration` | `number` | |
| `trigger` | `React.ReactNode` | |
| `open` | `boolean` | |
| `setOpen` | `(open: boolean) => void` | |
| `side` | `'top' \| 'right' \| 'bottom' \| 'left'` | `'top'` |
| `sideOffset` | `number` | `6` |

### CSS Variables

This component supports various CSS variables for theming. Here's a comprehensive list.

```css
:root {
--tooltip-background: hsl(var(--background));
}
```
7 changes: 7 additions & 0 deletions apps/web/vibes/soul/examples.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1156,6 +1156,13 @@ export const examples = [
files: ['examples/primitives/toaster/index.tsx'],
component: lazy(() => import('./examples/primitives/toaster')),
},
{
name: 'tooltip-example',
dependencies: [],
registryDependencies: ['tooltip'],
files: ['examples/primitives/tooltip/index.tsx'],
component: lazy(() => import('./examples/primitives/tooltip')),
},
{
name: 'section-layout-example-warm',
dependencies: [],
Expand Down
13 changes: 13 additions & 0 deletions apps/web/vibes/soul/examples/primitives/tooltip/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import { Tooltip } from '@/vibes/soul/primitives/tooltip';

export default function Preview() {
return (
<div className="bg-background p-8 @container">
<div className="mx-auto flex max-w-screen-md flex-col items-center gap-8">
<Tooltip trigger={<div>Hover me!</div>} delayDuration={0}>
<p className="text-sm text-contrast-500">Put your tooltip content here!</p>
</Tooltip>
</div>
</div>
);
}
6 changes: 6 additions & 0 deletions apps/web/vibes/soul/navigation.ts
Original file line number Diff line number Diff line change
Expand Up @@ -159,6 +159,12 @@ const primitivePages: [Page, ...Page[]] = [
file: 'docs/toaster.mdx',
component: 'toaster',
},
{
title: 'Tooltip',
slug: 'tooltip',
file: 'docs/tooltip.mdx',
component: 'tooltip',
},
];

const sectionPages: [Page, ...Page[]] = [
Expand Down
62 changes: 62 additions & 0 deletions apps/web/vibes/soul/primitives/tooltip/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
import * as TooltipPrimitive from '@radix-ui/react-tooltip';
import { clsx } from 'clsx';

interface TooltipProps extends React.PropsWithChildren {
align?: 'center' | 'end' | 'start';
className?: string;
delayDuration?: number;
skipDelayDuration?: number;
trigger?: React.ReactNode;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In what scenarios would we want a trigger being optional for tooltip?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If it's being controlled externally by some other state or if the trigger needs to be detached from the component. For our use-case in catalyst it doesn't need to be optional, so I can make it required if needed!

But also, the trigger isn't required in the underlying Radix UI component IIRC!

open?: boolean;
setOpen?: (open: boolean) => void;
side?: 'top' | 'right' | 'bottom' | 'left';
sideOffset?: number;
}

// eslint-disable-next-line valid-jsdoc

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should we remove this eslint-disable in VIBES?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ope yep! Good call

/**
* This component supports various CSS variables for theming. Here's a comprehensive list, along
* with their default values:
*
* ```css
* :root {
* --tooltip-background: hsl(var(--background));
* ```
*/
export const Tooltip = ({
align = 'center',
className = '',
delayDuration,
skipDelayDuration,
trigger,
open,
setOpen,
side = 'top',
sideOffset = 6,
children,
}: TooltipProps) => {
return (
<TooltipPrimitive.Provider delayDuration={delayDuration} skipDelayDuration={skipDelayDuration}>
<TooltipPrimitive.Root onOpenChange={setOpen} open={open}>
{trigger != null && (
<TooltipPrimitive.Trigger asChild={typeof trigger !== 'string'}>
{trigger}
</TooltipPrimitive.Trigger>
)}
<TooltipPrimitive.Portal>
<TooltipPrimitive.Content
align={align}
className={clsx(
'z-50 max-h-80 rounded-2xl border border-contrast-100 bg-[var(--tooltip-background,hsl(var(--background)))] p-3 shadow-xl data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95',
className,
)}
side={side}
sideOffset={sideOffset}
>
{children}
</TooltipPrimitive.Content>
</TooltipPrimitive.Portal>
</TooltipPrimitive.Root>
</TooltipPrimitive.Provider>
);
};