Create skills section (#67)
This commit is contained in:
parent
222924fa4b
commit
c76651e841
11 changed files with 276 additions and 22 deletions
|
|
@ -4,16 +4,14 @@ import type { IconName } from '@/types/icon';
|
||||||
import Icon from './icon';
|
import Icon from './icon';
|
||||||
|
|
||||||
export interface Props {
|
export interface Props {
|
||||||
icon?: {
|
name?: IconName;
|
||||||
name: IconName;
|
color?: string;
|
||||||
color?: string;
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const { icon } = Astro.props;
|
const { name, color } = Astro.props;
|
||||||
---
|
---
|
||||||
|
|
||||||
<div class="flex items-center w-fit h-6 px-2.5 rounded text-sm font-medium text-gray-700 bg-gray-100 gap-x-1.5">
|
<div class="flex items-center w-fit h-6 px-2.5 rounded text-sm font-medium text-gray-700 bg-gray-100 gap-x-1.5">
|
||||||
{icon && <Icon client:load name={icon.name} color={icon.color} size={16} />}
|
<Icon client:load name={name} color={color} size={16} />
|
||||||
<slot />
|
<slot />
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
15
src/components/organisms/levelled-skill-subsection.astro
Normal file
15
src/components/organisms/levelled-skill-subsection.astro
Normal file
|
|
@ -0,0 +1,15 @@
|
||||||
|
---
|
||||||
|
import type { LevelledSkill } from '@/types/skills-section';
|
||||||
|
|
||||||
|
import Skill from './skill.astro';
|
||||||
|
|
||||||
|
export interface Props {
|
||||||
|
skills: LevelledSkill[];
|
||||||
|
}
|
||||||
|
|
||||||
|
const { skills } = Astro.props;
|
||||||
|
---
|
||||||
|
|
||||||
|
<div class:list={['flex', 'flex-wrap', 'gap-8']}>
|
||||||
|
{skills.map((skill) => <Skill {...skill} />)}
|
||||||
|
</div>
|
||||||
33
src/components/organisms/skill-subsection.astro
Normal file
33
src/components/organisms/skill-subsection.astro
Normal file
|
|
@ -0,0 +1,33 @@
|
||||||
|
---
|
||||||
|
import Typography from '@/atoms/typography.astro';
|
||||||
|
import type { Tag } from '@/types/common';
|
||||||
|
import type { LevelledSkill, SkillSet } from '@/types/skills-section';
|
||||||
|
|
||||||
|
import LevelledSkillSubsection from './levelled-skill-subsection.astro';
|
||||||
|
import TagSkillSubsection from './tag-skill-subsection.astro';
|
||||||
|
|
||||||
|
export interface Props {
|
||||||
|
skillSet: SkillSet<Tag> | SkillSet<LevelledSkill>;
|
||||||
|
}
|
||||||
|
|
||||||
|
const {
|
||||||
|
skillSet: { skills, title },
|
||||||
|
} = Astro.props;
|
||||||
|
|
||||||
|
const isLevelledSkillSection = (skillsSectionData: Tag[] | LevelledSkill[]): skillsSectionData is LevelledSkill[] => {
|
||||||
|
if (!skillsSectionData[0]) return false;
|
||||||
|
|
||||||
|
return (skillsSectionData[0] as LevelledSkill).level !== undefined;
|
||||||
|
};
|
||||||
|
---
|
||||||
|
|
||||||
|
<div class:list={['flex', 'flex-col', 'gap-3']}>
|
||||||
|
<Typography variant="section-subtitle">{title}</Typography>
|
||||||
|
{
|
||||||
|
isLevelledSkillSection(skills) ? (
|
||||||
|
<LevelledSkillSubsection skills={skills} />
|
||||||
|
) : (
|
||||||
|
<TagSkillSubsection skills={skills} />
|
||||||
|
)
|
||||||
|
}
|
||||||
|
</div>
|
||||||
|
|
@ -18,7 +18,9 @@ const IconWrapper = url ? 'a' : 'div';
|
||||||
{...(url && { href: url, target: "_blank", rel: "noopener noreferrer" })}
|
{...(url && { href: url, target: "_blank", rel: "noopener noreferrer" })}
|
||||||
>
|
>
|
||||||
<Icon client:load name={icon} color={iconColor} size={20} />
|
<Icon client:load name={icon} color={iconColor} size={20} />
|
||||||
<Typography variant="tile-subtitle" class="text-gray-700">{name}</Typography>
|
<Typography variant="tile-subtitle">
|
||||||
|
<span class="text-gray-700">{name}</span>
|
||||||
|
</Typography>
|
||||||
</IconWrapper>
|
</IconWrapper>
|
||||||
<SkillLevel skillLevel={level} />
|
<SkillLevel skillLevel={level} />
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
28
src/components/organisms/tag-skill-subsection.astro
Normal file
28
src/components/organisms/tag-skill-subsection.astro
Normal file
|
|
@ -0,0 +1,28 @@
|
||||||
|
---
|
||||||
|
import Tag from '@/atoms/tag.astro';
|
||||||
|
import type { Tag as TagData } from '@/types/common';
|
||||||
|
|
||||||
|
export interface Props {
|
||||||
|
skills: TagData[];
|
||||||
|
}
|
||||||
|
|
||||||
|
const { skills } = Astro.props;
|
||||||
|
---
|
||||||
|
|
||||||
|
<div class:list={['flex', 'flex-wrap', 'gap-3', 'items-center']}>
|
||||||
|
{
|
||||||
|
skills.map(({ name, icon, iconColor, url }) =>
|
||||||
|
url ? (
|
||||||
|
<a href={url} target="_blank" rel="noopener noreferrer">
|
||||||
|
<Tag name={icon} color={iconColor}>
|
||||||
|
{name}
|
||||||
|
</Tag>
|
||||||
|
</a>
|
||||||
|
) : (
|
||||||
|
<Tag name={icon} color={iconColor}>
|
||||||
|
{name}
|
||||||
|
</Tag>
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
</div>
|
||||||
|
|
@ -68,10 +68,14 @@ const {
|
||||||
tags.map(({ icon, iconColor, name, url: tagUrl }) =>
|
tags.map(({ icon, iconColor, name, url: tagUrl }) =>
|
||||||
tagUrl ? (
|
tagUrl ? (
|
||||||
<a href={tagUrl} target="_blank" rel="noopener noreferrer">
|
<a href={tagUrl} target="_blank" rel="noopener noreferrer">
|
||||||
<Tag icon={icon ? { name: icon, color: iconColor } : undefined}>{name}</Tag>
|
<Tag name={icon} color={iconColor}>
|
||||||
|
{name}
|
||||||
|
</Tag>
|
||||||
</a>
|
</a>
|
||||||
) : (
|
) : (
|
||||||
<Tag icon={icon ? { name: icon, color: iconColor } : undefined}>{name}</Tag>
|
<Tag name={icon} color={iconColor}>
|
||||||
|
{name}
|
||||||
|
</Tag>
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,20 @@
|
||||||
---
|
---
|
||||||
import SectionCard from '@/atoms/section-card.astro';
|
import SectionCard from '@/atoms/section-card.astro';
|
||||||
|
import Typography from '@/atoms/typography.astro';
|
||||||
|
import SkillSubsection from '@/organisms/skill-subsection.astro';
|
||||||
import type { SkillsSection } from '@/types/skills-section';
|
import type { SkillsSection } from '@/types/skills-section';
|
||||||
|
|
||||||
export interface Props extends SkillsSection {}
|
export interface Props extends SkillsSection {}
|
||||||
|
|
||||||
|
const {
|
||||||
|
config: { title },
|
||||||
|
skillSets,
|
||||||
|
} = Astro.props;
|
||||||
---
|
---
|
||||||
|
|
||||||
<SectionCard>Skills section</SectionCard>
|
<SectionCard>
|
||||||
|
<Typography variant="section-title">{title}</Typography>
|
||||||
|
<div class:list={['flex', 'flex-col', 'gap-10']}>
|
||||||
|
{skillSets.map((skillSet) => <SkillSubsection skillSet={skillSet} />)}
|
||||||
|
</div>
|
||||||
|
</SectionCard>
|
||||||
|
|
|
||||||
|
|
@ -22,8 +22,18 @@ import data from '../data';
|
||||||
<meta name="description" content={data.seo.description} />
|
<meta name="description" content={data.seo.description} />
|
||||||
</head>
|
</head>
|
||||||
<body class="flex justify-center bg-gray-50">
|
<body class="flex justify-center bg-gray-50">
|
||||||
<div class="flex gap-8 w-full max-w-6xl px-2 py-3 sm:px-8 sm:py-12 lg:px-16 lg:py-20 lg:ml-22">
|
<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">
|
||||||
<main class="w-full space-y-4 sm:space-y-6 lg:space-y-8">
|
<div class="absolute -right-2">
|
||||||
|
<Sidebar className="hidden xl:flex fixed">
|
||||||
|
<SidebarItem icon="fa6-solid:user" />
|
||||||
|
{data.skills && <SidebarItem icon="fa6-solid:bars-progress" />}
|
||||||
|
{data.experience && <SidebarItem icon="fa6-solid:suitcase" />}
|
||||||
|
{data.portfolio && <SidebarItem icon="fa6-solid:rocket" />}
|
||||||
|
{data.testimonials && <SidebarItem icon="fa6-solid:comment" />}
|
||||||
|
{data.favorites && <SidebarItem icon="fa6-solid:star" />}
|
||||||
|
</Sidebar>
|
||||||
|
</div>
|
||||||
|
<main class="w-full relative space-y-4 sm:space-y-6 lg:space-y-8">
|
||||||
<MainSection {...data.main} />
|
<MainSection {...data.main} />
|
||||||
{data.skills && <SkillsSection {...data.skills} />}
|
{data.skills && <SkillsSection {...data.skills} />}
|
||||||
{data.experience && <ExperienceSection {...data.experience} />}
|
{data.experience && <ExperienceSection {...data.experience} />}
|
||||||
|
|
@ -31,14 +41,6 @@ import data from '../data';
|
||||||
{data.testimonials && <TestimonialsSection {...data.testimonials} />}
|
{data.testimonials && <TestimonialsSection {...data.testimonials} />}
|
||||||
{data.favorites && <FavoritesSection {...data.favorites} />}
|
{data.favorites && <FavoritesSection {...data.favorites} />}
|
||||||
</main>
|
</main>
|
||||||
<Sidebar className="hidden lg:flex">
|
|
||||||
<SidebarItem icon="fa6-solid:user" />
|
|
||||||
{data.skills && <SidebarItem icon="fa6-solid:bars-progress" />}
|
|
||||||
{data.experience && <SidebarItem icon="fa6-solid:suitcase" />}
|
|
||||||
{data.portfolio && <SidebarItem icon="fa6-solid:rocket" />}
|
|
||||||
{data.testimonials && <SidebarItem icon="fa6-solid:comment" />}
|
|
||||||
{data.favorites && <SidebarItem icon="fa6-solid:star" />}
|
|
||||||
</Sidebar>
|
|
||||||
</div>
|
</div>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|
|
||||||
160
src/pages/playground/skills-section.astro
Normal file
160
src/pages/playground/skills-section.astro
Normal file
|
|
@ -0,0 +1,160 @@
|
||||||
|
---
|
||||||
|
import SkillsSection from '@/sections/skills-section.astro';
|
||||||
|
import type { SkillsSection as SkillsSectionData } from '@/types/skills-section';
|
||||||
|
|
||||||
|
const skills: SkillsSectionData = {
|
||||||
|
config: {
|
||||||
|
title: 'Skills',
|
||||||
|
icon: 'fa6-solid:bars-progress',
|
||||||
|
},
|
||||||
|
skillSets: [
|
||||||
|
{
|
||||||
|
title: 'I already know',
|
||||||
|
skills: [
|
||||||
|
{
|
||||||
|
icon: 'simple-icons:react',
|
||||||
|
iconColor: '#61DAFB',
|
||||||
|
name: 'React.js',
|
||||||
|
level: 5,
|
||||||
|
url: 'https://reactjs.org/',
|
||||||
|
description:
|
||||||
|
'Proin ut erat sed massa tempus suscipit. Mauris efficitur nunc sem, nec scelerisque ligula bibendum ut.',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
icon: 'simple-icons:typescript',
|
||||||
|
iconColor: '#3178C6',
|
||||||
|
name: 'TypeScript',
|
||||||
|
level: 4,
|
||||||
|
url: 'https://www.typescriptlang.org/',
|
||||||
|
description: 'Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas.',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
icon: 'simple-icons:sass',
|
||||||
|
iconColor: '#CC6699',
|
||||||
|
name: 'SASS',
|
||||||
|
level: 4,
|
||||||
|
url: 'https://sass-lang.com/',
|
||||||
|
description: 'Nulla interdum pellentesque ultricies. Ut id eros commodo, ultrices ligula eu, elementum ante.',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
icon: 'simple-icons:chakraui',
|
||||||
|
iconColor: '#319795',
|
||||||
|
name: 'Chakra UI',
|
||||||
|
level: 5,
|
||||||
|
url: 'https://chakra-ui.com/',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
icon: 'simple-icons:tailwindcss',
|
||||||
|
iconColor: '#06B6D4',
|
||||||
|
name: 'Tailwind CSS',
|
||||||
|
level: 2,
|
||||||
|
url: 'https://tailwindcss.com/',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
icon: 'simple-icons:prettier',
|
||||||
|
iconColor: '#F7B93E',
|
||||||
|
name: 'Prettier',
|
||||||
|
level: 5,
|
||||||
|
url: 'https://prettier.io/',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
icon: 'simple-icons:eslint',
|
||||||
|
iconColor: '#4B32C3',
|
||||||
|
name: 'ESLint',
|
||||||
|
level: 4,
|
||||||
|
url: 'https://eslint.org/',
|
||||||
|
description:
|
||||||
|
'Nulla tempor turpis at vehicula pharetra. Vestibulum tellus tortor, commodo et suscipit id, lobortis id nunc.',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
icon: 'simple-icons:nestjs',
|
||||||
|
iconColor: '#E0234E',
|
||||||
|
name: 'NestJS',
|
||||||
|
level: 2,
|
||||||
|
url: 'https://nestjs.com/',
|
||||||
|
description:
|
||||||
|
'Praesent feugiat ultricies iaculis. In posuere vehicula odio, sed consequat velit porta viverra.',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
icon: 'simple-icons:postgresql',
|
||||||
|
iconColor: '#4169E1',
|
||||||
|
name: 'PostgreSQL',
|
||||||
|
level: 2,
|
||||||
|
url: 'https://www.postgresql.org/',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
icon: 'simple-icons:mongodb',
|
||||||
|
iconColor: '#47A248',
|
||||||
|
name: 'MongoDB',
|
||||||
|
level: 1,
|
||||||
|
url: 'https://www.mongodb.com/',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
icon: 'simple-icons:firebase',
|
||||||
|
iconColor: '#FFCA28',
|
||||||
|
name: 'Firebase',
|
||||||
|
level: 1,
|
||||||
|
url: 'https://firebase.google.com/',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
icon: 'simple-icons:pnpm',
|
||||||
|
iconColor: '#F69220',
|
||||||
|
name: 'pnpm',
|
||||||
|
level: 3,
|
||||||
|
url: 'https://pnpm.io/',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: 'I want to learn',
|
||||||
|
skills: [
|
||||||
|
{
|
||||||
|
icon: 'simple-icons:apollographql',
|
||||||
|
iconColor: '#311C87',
|
||||||
|
name: 'Apollo GraphQL',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
icon: 'simple-icons:astro',
|
||||||
|
iconColor: '#FF5D01',
|
||||||
|
name: 'Astro',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
icon: 'simple-icons:supabase',
|
||||||
|
iconColor: '#3ECF8E',
|
||||||
|
name: 'Supabase',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
icon: 'simple-icons:cypress',
|
||||||
|
iconColor: '#17202C',
|
||||||
|
name: 'Cypress',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: 'I speak',
|
||||||
|
skills: [
|
||||||
|
{
|
||||||
|
icon: 'circle-flags:pl',
|
||||||
|
name: 'Polish - native',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
icon: 'circle-flags:us',
|
||||||
|
name: 'English - C1',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
icon: 'circle-flags:es-variant',
|
||||||
|
name: 'Spanish - B1',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
};
|
||||||
|
---
|
||||||
|
|
||||||
|
<body class="flex justify-center bg-gray-50">
|
||||||
|
<div class="flex gap-8 w-full max-w-6xl px-2 py-3 sm:px-8 sm:py-12 lg:px-16 lg:py-20 lg:ml-22">
|
||||||
|
<main class="w-full space-y-4 sm:space-y-6 lg:space-y-8">
|
||||||
|
<SkillsSection config={skills.config} skillSets={skills.skillSets} />
|
||||||
|
</main>
|
||||||
|
</div>
|
||||||
|
</body>
|
||||||
|
|
@ -7,5 +7,5 @@ import Tag from '@/atoms/tag.astro';
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="p-5">
|
<div class="p-5">
|
||||||
<Tag icon={{ name: 'simple-icons:react', color: '#61DAFB' }}>Tag text</Tag>
|
<Tag name="simple-icons:react" color="#61DAFB">Tag text</Tag>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
|
|
@ -4,7 +4,7 @@ export interface LevelledSkill extends Tag {
|
||||||
level: 1 | 2 | 3 | 4 | 5;
|
level: 1 | 2 | 3 | 4 | 5;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface SkillSet<SkillType> {
|
export interface SkillSet<SkillType> {
|
||||||
title: string;
|
title: string;
|
||||||
skills: SkillType[];
|
skills: SkillType[];
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue