Skip to content

Commit 1e57c99

Browse files
committed
feat: add component tooltip
1 parent 0b602dc commit 1e57c99

File tree

12 files changed

+338
-0
lines changed

12 files changed

+338
-0
lines changed

packages/ui/package.json

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,12 +35,14 @@
3535
"@radix-ui/react-hover-card": "^1.1.14",
3636
"@radix-ui/react-label": "2.1.7",
3737
"@radix-ui/react-menu": "^2.1.15",
38+
"@radix-ui/react-menubar": "^1.1.15",
3839
"@radix-ui/react-popover": "1.1.14",
3940
"@radix-ui/react-scroll-area": "1.2.9",
4041
"@radix-ui/react-separator": "1.1.7",
4142
"@radix-ui/react-slot": "1.2.3",
4243
"@radix-ui/react-switch": "^1.2.5",
4344
"@radix-ui/react-tabs": "1.1.12",
45+
"@radix-ui/react-tooltip": "^1.2.7",
4446
"@soybean-react-ui/tailwind-plugin": "workspace:*",
4547
"clsx": "2.1.1",
4648
"cmdk": "^1.1.1",
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
import { Root, Trigger } from '@radix-ui/react-tooltip';
2+
3+
import TooltipArrow from './TooltipArrow';
4+
import TooltipContent from './TooltipContent';
5+
import type { TooltipProps } from './types';
6+
7+
const Tooltip = (props: TooltipProps) => {
8+
const { children, className, classNames, content, contentProps, showArrow, size, ...rest } = props;
9+
10+
return (
11+
<Root {...rest}>
12+
<Trigger asChild>{children}</Trigger>
13+
14+
<TooltipContent
15+
className={className || classNames?.content}
16+
size={size}
17+
{...contentProps}
18+
>
19+
{content}
20+
21+
{showArrow && (
22+
<TooltipArrow
23+
className={classNames?.arrow}
24+
size={size}
25+
/>
26+
)}
27+
</TooltipContent>
28+
</Root>
29+
);
30+
};
31+
32+
export default Tooltip;
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
import { Arrow } from '@radix-ui/react-tooltip';
2+
3+
import { cn } from '@/lib/utils';
4+
5+
import { tooltipVariants } from './tooltip-variants';
6+
import type { TooltipArrowProps } from './types';
7+
8+
const TooltipArrow = (props: TooltipArrowProps) => {
9+
const { className, size, ...rest } = props;
10+
11+
const { arrow } = tooltipVariants({ size });
12+
13+
const mergedCls = cn(arrow(), className);
14+
15+
return (
16+
<Arrow
17+
className={mergedCls}
18+
{...rest}
19+
/>
20+
);
21+
};
22+
23+
export default TooltipArrow;
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
import { Content, Portal } from '@radix-ui/react-tooltip';
2+
import type { ComponentRef } from 'react';
3+
import { forwardRef } from 'react';
4+
5+
import { cn } from '@/lib/utils';
6+
7+
import { tooltipVariants } from './tooltip-variants';
8+
import type { TooltipContentProps } from './types';
9+
10+
const TooltipContent = forwardRef<ComponentRef<typeof Content>, TooltipContentProps>((props, ref) => {
11+
const { avoidCollisions = true, className, sideOffset = 8, size, ...rest } = props;
12+
13+
const { content } = tooltipVariants({ size });
14+
15+
const mergedCls = cn(content(), className);
16+
17+
return (
18+
<Portal>
19+
<Content
20+
avoidCollisions={avoidCollisions}
21+
className={mergedCls}
22+
ref={ref}
23+
sideOffset={sideOffset}
24+
{...rest}
25+
/>
26+
</Portal>
27+
);
28+
});
29+
30+
TooltipContent.displayName = 'TooltipContent';
31+
32+
export default TooltipContent;
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
export { Tooltip as TooltipRoot, TooltipProvider, TooltipTrigger } from '@radix-ui/react-tooltip';
2+
3+
export { default as Tooltip } from './Tooltip';
4+
export { default as TooltipArrow } from './TooltipArrow';
5+
export { default as TooltipContent } from './TooltipContent';
6+
7+
export * from './types';
Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
import { tv } from 'tailwind-variants';
2+
3+
export const tooltipVariants = tv({
4+
defaultVariants: {
5+
size: 'md'
6+
},
7+
slots: {
8+
arrow: 'w-1em h-0.5em fill-carbon stroke-border',
9+
content: [
10+
'w-auto rounded-md border bg-carbon text-carbon-foreground shadow-md outline-none z-50 will-change-transform',
11+
'animate-in fade-in-0 zoom-in-95',
12+
'data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=closed]:zoom-out-95',
13+
'data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2'
14+
]
15+
},
16+
variants: {
17+
size: {
18+
'2xl': {
19+
arrow: 'text-lg',
20+
content: 'px-4.5 py-2.5 text-xl'
21+
},
22+
lg: {
23+
arrow: 'text-sm',
24+
content: 'px-3.5 py-1.75 text-base'
25+
},
26+
md: {
27+
arrow: 'text-xs',
28+
content: 'px-3 py-1.5 text-sm'
29+
},
30+
sm: {
31+
arrow: 'text-2xs',
32+
content: 'px-2.5 py-1.25 text-xs'
33+
},
34+
xl: {
35+
arrow: 'text-base',
36+
content: 'px-4 py-2 text-lg'
37+
},
38+
xs: {
39+
arrow: 'text-3xs',
40+
content: 'px-2 py-1 text-2xs'
41+
}
42+
}
43+
}
44+
});
45+
46+
export type TooltipSlots = keyof typeof tooltipVariants.slots;
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
import type {
2+
TooltipArrowProps as _TooltipArrowProps,
3+
TooltipContentProps as _TooltipContentProps,
4+
TooltipProps as _TooltipProps
5+
} from '@radix-ui/react-tooltip';
6+
7+
import type { BaseNodeProps, ClassValue, ThemeAlign, ThemeSide } from '@/types/other';
8+
9+
import type { TooltipSlots } from './tooltip-variants';
10+
11+
export type TooltipClassNames = Partial<Record<TooltipSlots, ClassValue>>;
12+
13+
export interface TooltipContentProps extends BaseNodeProps<_TooltipContentProps> {}
14+
15+
export interface TooltipArrowProps extends BaseNodeProps<_TooltipArrowProps> {}
16+
17+
export interface TooltipProps extends BaseNodeProps<_TooltipProps> {
18+
classNames?: TooltipClassNames;
19+
content: React.ReactNode;
20+
contentProps?: Omit<TooltipContentProps, 'children' | 'className'>;
21+
showArrow?: boolean;
22+
}
23+
24+
export type TooltipSide = ThemeSide;
25+
26+
export type TooltipAlign = ThemeAlign;

packages/ui/src/index.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,8 @@ export * from './components/switch';
6262

6363
export * from './components/tabs';
6464

65+
export * from './components/tooltip';
66+
6567
export * from './lib';
6668

6769
export * from './types';
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
import { Button, Card, Tooltip } from 'soybean-react-ui';
2+
3+
const TooltipArrow = () => {
4+
return (
5+
<Card
6+
split
7+
title="Tooltip Arrow"
8+
>
9+
<Tooltip
10+
showArrow
11+
content="Tooltip content"
12+
>
13+
<Button variant="plain">with arrow</Button>
14+
</Tooltip>
15+
</Card>
16+
);
17+
};
18+
19+
export default TooltipArrow;
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
import type { TooltipAlign, TooltipSide } from 'soybean-react-ui';
2+
import { Button, Card, Tooltip } from 'soybean-react-ui';
3+
4+
const sides: TooltipSide[] = ['top', 'right', 'bottom', 'left'];
5+
6+
const aligns: TooltipAlign[] = ['start', 'center', 'end'];
7+
8+
const TooltipDemo = () => {
9+
return (
10+
<Card
11+
split
12+
title="Tooltip Position"
13+
>
14+
{sides.map(side => {
15+
return (
16+
<div key={side}>
17+
<div className="py-[12px] text-[18px] font-medium">Side: {side}</div>
18+
<div className="flex flex-wrap gap-[12px]">
19+
{aligns.map(align => {
20+
return (
21+
<Tooltip
22+
content="Tooltip content"
23+
key={align}
24+
contentProps={{
25+
align,
26+
side
27+
}}
28+
>
29+
<Button variant="plain">align:{align}</Button>
30+
</Tooltip>
31+
);
32+
})}
33+
</div>
34+
</div>
35+
);
36+
})}
37+
</Card>
38+
);
39+
};
40+
41+
export default TooltipDemo;

0 commit comments

Comments
 (0)