Create tooltip component (#77)
This commit is contained in:
parent
bd27dc085c
commit
e6bb39e3b3
10 changed files with 154 additions and 22 deletions
51
package-lock.json
generated
51
package-lock.json
generated
|
|
@ -10,6 +10,7 @@
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@astrojs/image": "0.11.4",
|
"@astrojs/image": "0.11.4",
|
||||||
"@iconify-icon/react": "1.0.1",
|
"@iconify-icon/react": "1.0.1",
|
||||||
|
"@tippyjs/react": "4.2.6",
|
||||||
"clsx": "1.2.1",
|
"clsx": "1.2.1",
|
||||||
"prettier-plugin-astro": "0.7.0",
|
"prettier-plugin-astro": "0.7.0",
|
||||||
"react": "18.2.0",
|
"react": "18.2.0",
|
||||||
|
|
@ -898,6 +899,15 @@
|
||||||
"integrity": "sha512-a5Sab1C4/icpTZVzZc5Ghpz88yQtGOyNqYXcZgOssB2uuAr+wF/MvN6bgtW32q7HHrvBki+BsZ0OuNv6EV3K9g==",
|
"integrity": "sha512-a5Sab1C4/icpTZVzZc5Ghpz88yQtGOyNqYXcZgOssB2uuAr+wF/MvN6bgtW32q7HHrvBki+BsZ0OuNv6EV3K9g==",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
|
"node_modules/@popperjs/core": {
|
||||||
|
"version": "2.11.6",
|
||||||
|
"resolved": "https://registry.npmjs.org/@popperjs/core/-/core-2.11.6.tgz",
|
||||||
|
"integrity": "sha512-50/17A98tWUfQ176raKiOGXuYpLyyVMkxxG6oylzL3BPOlA6ADGdK7EYunSa4I064xerltq9TGXs8HmOk5E+vw==",
|
||||||
|
"funding": {
|
||||||
|
"type": "opencollective",
|
||||||
|
"url": "https://opencollective.com/popperjs"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/@proload/core": {
|
"node_modules/@proload/core": {
|
||||||
"version": "0.3.3",
|
"version": "0.3.3",
|
||||||
"resolved": "https://registry.npmjs.org/@proload/core/-/core-0.3.3.tgz",
|
"resolved": "https://registry.npmjs.org/@proload/core/-/core-0.3.3.tgz",
|
||||||
|
|
@ -920,6 +930,18 @@
|
||||||
"@proload/core": "^0.3.2"
|
"@proload/core": "^0.3.2"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/@tippyjs/react": {
|
||||||
|
"version": "4.2.6",
|
||||||
|
"resolved": "https://registry.npmjs.org/@tippyjs/react/-/react-4.2.6.tgz",
|
||||||
|
"integrity": "sha512-91RicDR+H7oDSyPycI13q3b7o4O60wa2oRbjlz2fyRLmHImc4vyDwuUP8NtZaN0VARJY5hybvDYrFzhY9+Lbyw==",
|
||||||
|
"dependencies": {
|
||||||
|
"tippy.js": "^6.3.1"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"react": ">=16.8",
|
||||||
|
"react-dom": ">=16.8"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/@types/acorn": {
|
"node_modules/@types/acorn": {
|
||||||
"version": "4.0.6",
|
"version": "4.0.6",
|
||||||
"resolved": "https://registry.npmjs.org/@types/acorn/-/acorn-4.0.6.tgz",
|
"resolved": "https://registry.npmjs.org/@types/acorn/-/acorn-4.0.6.tgz",
|
||||||
|
|
@ -9280,6 +9302,14 @@
|
||||||
"globrex": "^0.1.2"
|
"globrex": "^0.1.2"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/tippy.js": {
|
||||||
|
"version": "6.3.7",
|
||||||
|
"resolved": "https://registry.npmjs.org/tippy.js/-/tippy.js-6.3.7.tgz",
|
||||||
|
"integrity": "sha512-E1d3oP2emgJ9dRQZdf3Kkn0qJgI6ZLpyS5z6ZkY1DF3kaQaBsGZsndEpHwx+eC+tYM41HaSNvNtLx8tU57FzTQ==",
|
||||||
|
"dependencies": {
|
||||||
|
"@popperjs/core": "^2.9.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/to-fast-properties": {
|
"node_modules/to-fast-properties": {
|
||||||
"version": "2.0.0",
|
"version": "2.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz",
|
||||||
|
|
@ -11189,6 +11219,11 @@
|
||||||
"integrity": "sha512-a5Sab1C4/icpTZVzZc5Ghpz88yQtGOyNqYXcZgOssB2uuAr+wF/MvN6bgtW32q7HHrvBki+BsZ0OuNv6EV3K9g==",
|
"integrity": "sha512-a5Sab1C4/icpTZVzZc5Ghpz88yQtGOyNqYXcZgOssB2uuAr+wF/MvN6bgtW32q7HHrvBki+BsZ0OuNv6EV3K9g==",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
|
"@popperjs/core": {
|
||||||
|
"version": "2.11.6",
|
||||||
|
"resolved": "https://registry.npmjs.org/@popperjs/core/-/core-2.11.6.tgz",
|
||||||
|
"integrity": "sha512-50/17A98tWUfQ176raKiOGXuYpLyyVMkxxG6oylzL3BPOlA6ADGdK7EYunSa4I064xerltq9TGXs8HmOk5E+vw=="
|
||||||
|
},
|
||||||
"@proload/core": {
|
"@proload/core": {
|
||||||
"version": "0.3.3",
|
"version": "0.3.3",
|
||||||
"resolved": "https://registry.npmjs.org/@proload/core/-/core-0.3.3.tgz",
|
"resolved": "https://registry.npmjs.org/@proload/core/-/core-0.3.3.tgz",
|
||||||
|
|
@ -11208,6 +11243,14 @@
|
||||||
"tsm": "^2.1.4"
|
"tsm": "^2.1.4"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"@tippyjs/react": {
|
||||||
|
"version": "4.2.6",
|
||||||
|
"resolved": "https://registry.npmjs.org/@tippyjs/react/-/react-4.2.6.tgz",
|
||||||
|
"integrity": "sha512-91RicDR+H7oDSyPycI13q3b7o4O60wa2oRbjlz2fyRLmHImc4vyDwuUP8NtZaN0VARJY5hybvDYrFzhY9+Lbyw==",
|
||||||
|
"requires": {
|
||||||
|
"tippy.js": "^6.3.1"
|
||||||
|
}
|
||||||
|
},
|
||||||
"@types/acorn": {
|
"@types/acorn": {
|
||||||
"version": "4.0.6",
|
"version": "4.0.6",
|
||||||
"resolved": "https://registry.npmjs.org/@types/acorn/-/acorn-4.0.6.tgz",
|
"resolved": "https://registry.npmjs.org/@types/acorn/-/acorn-4.0.6.tgz",
|
||||||
|
|
@ -17105,6 +17148,14 @@
|
||||||
"globrex": "^0.1.2"
|
"globrex": "^0.1.2"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"tippy.js": {
|
||||||
|
"version": "6.3.7",
|
||||||
|
"resolved": "https://registry.npmjs.org/tippy.js/-/tippy.js-6.3.7.tgz",
|
||||||
|
"integrity": "sha512-E1d3oP2emgJ9dRQZdf3Kkn0qJgI6ZLpyS5z6ZkY1DF3kaQaBsGZsndEpHwx+eC+tYM41HaSNvNtLx8tU57FzTQ==",
|
||||||
|
"requires": {
|
||||||
|
"@popperjs/core": "^2.9.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"to-fast-properties": {
|
"to-fast-properties": {
|
||||||
"version": "2.0.0",
|
"version": "2.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz",
|
||||||
|
|
|
||||||
|
|
@ -21,6 +21,7 @@
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@astrojs/image": "0.11.4",
|
"@astrojs/image": "0.11.4",
|
||||||
"@iconify-icon/react": "1.0.1",
|
"@iconify-icon/react": "1.0.1",
|
||||||
|
"@tippyjs/react": "4.2.6",
|
||||||
"clsx": "1.2.1",
|
"clsx": "1.2.1",
|
||||||
"prettier-plugin-astro": "0.7.0",
|
"prettier-plugin-astro": "0.7.0",
|
||||||
"react": "18.2.0",
|
"react": "18.2.0",
|
||||||
|
|
|
||||||
15
src/components/atoms/icon-with-tooltip.tsx
Normal file
15
src/components/atoms/icon-with-tooltip.tsx
Normal file
|
|
@ -0,0 +1,15 @@
|
||||||
|
import type { IconProps } from './icon';
|
||||||
|
import Icon from './icon';
|
||||||
|
import Tooltip, { TooltipProps } from './tooltip';
|
||||||
|
|
||||||
|
type Props = IconProps & Omit<TooltipProps, 'children'>;
|
||||||
|
|
||||||
|
const IconWithTooltip = ({ name, color, size, ...tooltipProps }: Props) => (
|
||||||
|
<Tooltip {...tooltipProps}>
|
||||||
|
<div>
|
||||||
|
<Icon name={name} color={color} size={size} />
|
||||||
|
</div>
|
||||||
|
</Tooltip>
|
||||||
|
);
|
||||||
|
|
||||||
|
export default IconWithTooltip;
|
||||||
|
|
@ -5,19 +5,22 @@ import type { Section } from '@/types/data';
|
||||||
import type { IconName } from '@/types/icon';
|
import type { IconName } from '@/types/icon';
|
||||||
|
|
||||||
import Icon from './icon';
|
import Icon from './icon';
|
||||||
|
import Tooltip from './tooltip';
|
||||||
|
|
||||||
export interface Props {
|
export interface SidebarItemProps {
|
||||||
section: Section;
|
section: Section;
|
||||||
icon: IconName;
|
icon: IconName;
|
||||||
|
title?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
const SidebarItem = ({ section, icon }: Props) => {
|
const SidebarItem = ({ section, icon, title = '' }: SidebarItemProps) => {
|
||||||
const { hash } = useLocation();
|
const { hash } = useLocation();
|
||||||
const href = `#${section}`;
|
const href = `#${section}`;
|
||||||
|
|
||||||
const active = hash === '' ? section === MAIN_SECTION : hash === href;
|
const active = hash === '' ? section === MAIN_SECTION : hash === href;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
<Tooltip content={`${title || section.charAt(0).toUpperCase() + section.slice(1)} section`} placement="left">
|
||||||
<a
|
<a
|
||||||
href={href}
|
href={href}
|
||||||
className={`inline-flex justify-center items-center h-10 w-10 rounded-lg transition
|
className={`inline-flex justify-center items-center h-10 w-10 rounded-lg transition
|
||||||
|
|
@ -27,6 +30,7 @@ const SidebarItem = ({ section, icon }: Props) => {
|
||||||
>
|
>
|
||||||
<Icon name={icon} size={20} />
|
<Icon name={icon} size={20} />
|
||||||
</a>
|
</a>
|
||||||
|
</Tooltip>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
||||||
25
src/components/atoms/tooltip.tsx
Normal file
25
src/components/atoms/tooltip.tsx
Normal file
|
|
@ -0,0 +1,25 @@
|
||||||
|
import Tippy, { TippyProps } from '@tippyjs/react/headless';
|
||||||
|
import type { ReactElement } from 'react';
|
||||||
|
|
||||||
|
export interface TooltipProps {
|
||||||
|
children: ReactElement;
|
||||||
|
content: string;
|
||||||
|
placement?: TippyProps['placement'];
|
||||||
|
}
|
||||||
|
|
||||||
|
const Tooltip = ({ children, content, placement = 'top' }: TooltipProps) => {
|
||||||
|
return (
|
||||||
|
<Tippy
|
||||||
|
render={(attrs) => (
|
||||||
|
<div {...attrs} className="bg-gray-700 rounded-lg px-2 py-1.5 text-white max-w-[95%] sm:max-w-xs">
|
||||||
|
{content}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
placement={placement}
|
||||||
|
>
|
||||||
|
{children}
|
||||||
|
</Tippy>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default Tooltip;
|
||||||
|
|
@ -1,18 +1,21 @@
|
||||||
---
|
---
|
||||||
import Icon from '@/atoms/icon';
|
import Icon from '@/atoms/icon';
|
||||||
|
import IconWithTooltip from '@/atoms/icon-with-tooltip';
|
||||||
import Typography from '@/atoms/typography.astro';
|
import Typography from '@/atoms/typography.astro';
|
||||||
|
import type { IconName } from '@/types/icon';
|
||||||
import type { LevelledSkill } from '@/types/skills-section';
|
import type { LevelledSkill } from '@/types/skills-section';
|
||||||
|
|
||||||
import SkillLevel from './skill-level.astro';
|
import SkillLevel from './skill-level.astro';
|
||||||
|
|
||||||
export interface Props extends LevelledSkill {}
|
export interface Props extends LevelledSkill {}
|
||||||
|
|
||||||
const { url, icon, iconColor, name, level } = Astro.props;
|
const { url, icon, iconColor, name, level, description } = Astro.props;
|
||||||
|
|
||||||
const IconWrapper = url ? 'a' : 'div';
|
const IconWrapper = url ? 'a' : 'div';
|
||||||
---
|
---
|
||||||
|
|
||||||
<div class:list={['flex', 'flex-col', 'gap-2']}>
|
<div class:list={['flex', 'flex-col', 'gap-2']}>
|
||||||
|
<div class:list={['flex', 'items-center', 'justify-between', 'h-5']}>
|
||||||
<IconWrapper
|
<IconWrapper
|
||||||
class:list={['flex', 'gap-2', 'h-5']}
|
class:list={['flex', 'gap-2', 'h-5']}
|
||||||
{...(url && { href: url, target: "_blank", rel: "noopener noreferrer" })}
|
{...(url && { href: url, target: "_blank", rel: "noopener noreferrer" })}
|
||||||
|
|
@ -22,5 +25,18 @@ const IconWrapper = url ? 'a' : 'div';
|
||||||
<span class="text-gray-700">{name}</span>
|
<span class="text-gray-700">{name}</span>
|
||||||
</Typography>
|
</Typography>
|
||||||
</IconWrapper>
|
</IconWrapper>
|
||||||
|
{
|
||||||
|
description && (
|
||||||
|
<IconWithTooltip
|
||||||
|
client:load
|
||||||
|
name={'akar-icons:info-fill' as IconName}
|
||||||
|
color="#D1D5DB"
|
||||||
|
size={14}
|
||||||
|
content={description}
|
||||||
|
placement="top"
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
</div>
|
||||||
<SkillLevel skillLevel={level} />
|
<SkillLevel skillLevel={level} />
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
|
|
@ -9,6 +9,7 @@ const data: Data = {
|
||||||
main: {
|
main: {
|
||||||
config: {
|
config: {
|
||||||
icon: 'fa6-solid:user',
|
icon: 'fa6-solid:user',
|
||||||
|
title: 'Profile',
|
||||||
},
|
},
|
||||||
image: import('@/assets/my-image.jpeg'),
|
image: import('@/assets/my-image.jpeg'),
|
||||||
fullName: 'Mark Freeman',
|
fullName: 'Mark Freeman',
|
||||||
|
|
|
||||||
|
|
@ -26,12 +26,21 @@ const { seo, ...dataWithoutSeo } = data;
|
||||||
</head>
|
</head>
|
||||||
<body class="flex justify-center bg-gray-50">
|
<body class="flex justify-center bg-gray-50">
|
||||||
<div class="flex relative transform-none gap-8 w-full max-w-5xl px-2 py-3 sm:px-8 sm:py-12 lg:py-20">
|
<div class="flex relative transform-none gap-8 w-full max-w-5xl px-2 py-3 sm:px-8 sm:py-12 lg:py-20">
|
||||||
<div class="absolute -right-2">
|
<div class="absolute z-40 -right-2">
|
||||||
<Sidebar className="hidden xl:flex fixed">
|
<Sidebar className="hidden xl:flex fixed">
|
||||||
{
|
{
|
||||||
getObjectKeys(dataWithoutSeo).map((key) => {
|
getObjectKeys(dataWithoutSeo).map((key) => {
|
||||||
const sectionData = dataWithoutSeo[key];
|
const sectionData = dataWithoutSeo[key];
|
||||||
return sectionData && <SidebarItem client:only="react" icon={sectionData.config.icon} section={key} />;
|
return (
|
||||||
|
sectionData && (
|
||||||
|
<SidebarItem
|
||||||
|
client:load
|
||||||
|
title={sectionData.config.title}
|
||||||
|
icon={sectionData.config.icon}
|
||||||
|
section={key}
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
);
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
</Sidebar>
|
</Sidebar>
|
||||||
|
|
|
||||||
10
src/pages/playground/tooltip.astro
Normal file
10
src/pages/playground/tooltip.astro
Normal file
|
|
@ -0,0 +1,10 @@
|
||||||
|
---
|
||||||
|
import SidebarItem from '@/atoms/sidebar-item';
|
||||||
|
import Tooltip from '@/atoms/tooltip';
|
||||||
|
---
|
||||||
|
|
||||||
|
<div class="p-10">
|
||||||
|
<Tooltip client:only="react" content="siemano">
|
||||||
|
<SidebarItem client:load section="experience" icon="fa6-solid:user" />
|
||||||
|
</Tooltip>
|
||||||
|
</div>
|
||||||
|
|
@ -13,5 +13,5 @@ export interface MainSection {
|
||||||
downloadedFileName?: string;
|
downloadedFileName?: string;
|
||||||
};
|
};
|
||||||
socials: Social[];
|
socials: Social[];
|
||||||
config: Omit<SectionConfig, 'title'>;
|
config: SectionConfig;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue