Create profile card section (#55)
This commit is contained in:
parent
0e2d6125a8
commit
0e3bb5f849
8 changed files with 126 additions and 14 deletions
|
|
@ -1,9 +1,10 @@
|
||||||
---
|
---
|
||||||
export interface Props {
|
export interface Props {
|
||||||
href: string;
|
href: string;
|
||||||
|
download?: astroHTML.JSX.AnchorHTMLAttributes['download'];
|
||||||
}
|
}
|
||||||
|
|
||||||
const { href } = Astro.props;
|
const { href, download } = Astro.props;
|
||||||
|
|
||||||
const classes = {
|
const classes = {
|
||||||
main: 'inline-flex items-center px-4 h-10 text-base font-medium rounded-md shadow-sm text-white bg-primary-600 select-none cursor-pointer',
|
main: 'inline-flex items-center px-4 h-10 text-base font-medium rounded-md shadow-sm text-white bg-primary-600 select-none cursor-pointer',
|
||||||
|
|
@ -13,4 +14,6 @@ const classes = {
|
||||||
};
|
};
|
||||||
---
|
---
|
||||||
|
|
||||||
<a href={href} class:list={[classes.main, classes.hover, classes.active, classes.focus]}><slot /></a>
|
<a href={href} class:list={[classes.main, classes.hover, classes.active, classes.focus]} download={download}
|
||||||
|
><slot />
|
||||||
|
</a>
|
||||||
|
|
|
||||||
|
|
@ -6,13 +6,11 @@ import Icon from './icon';
|
||||||
export interface Props {
|
export interface Props {
|
||||||
icon?: {
|
icon?: {
|
||||||
name: IconName;
|
name: IconName;
|
||||||
color: string;
|
color?: string;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
const {
|
const { icon } = Astro.props;
|
||||||
props: { icon },
|
|
||||||
} = Astro;
|
|
||||||
---
|
---
|
||||||
|
|
||||||
<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">
|
||||||
|
|
|
||||||
|
|
@ -25,8 +25,8 @@ const variantToElement = {
|
||||||
} as const;
|
} as const;
|
||||||
|
|
||||||
const variantToClassName = {
|
const variantToClassName = {
|
||||||
'main-title': 'text-4xl font-extrabold text-gray-900',
|
'main-title': 'text-3xl sm:text-4xl font-extrabold text-gray-900',
|
||||||
'main-subtitle': 'text-lg font-medium text-gray-700',
|
'main-subtitle': 'text-md sm:text-lg font-medium text-gray-700',
|
||||||
'section-title': 'text-3xl font-extrabold text-gray-900',
|
'section-title': 'text-3xl font-extrabold text-gray-900',
|
||||||
'section-subtitle': 'text-lg font-extrabold text-gray-900',
|
'section-subtitle': 'text-lg font-extrabold text-gray-900',
|
||||||
'item-title': 'text-xl font-extrabold text-gray-900',
|
'item-title': 'text-xl font-extrabold text-gray-900',
|
||||||
|
|
@ -34,7 +34,7 @@ const variantToClassName = {
|
||||||
'item-subtitle': 'text-md font-medium text-gray-700',
|
'item-subtitle': 'text-md font-medium text-gray-700',
|
||||||
'tile-title': 'text-sm font-medium text-gray-700',
|
'tile-title': 'text-sm font-medium text-gray-700',
|
||||||
'tile-subtitle': 'text-sm font-normal text-gray-500',
|
'tile-subtitle': 'text-sm font-normal text-gray-500',
|
||||||
paragraph: 'text-base leading-relaxed font-normal text-gray-500',
|
paragraph: 'text-sm sm:text-base leading-relaxed font-normal text-gray-500',
|
||||||
};
|
};
|
||||||
|
|
||||||
export interface Props extends Omit<astroHTML.JSX.HTMLAttributes, 'slot'> {
|
export interface Props extends Omit<astroHTML.JSX.HTMLAttributes, 'slot'> {
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
---
|
---
|
||||||
export interface Props {
|
export interface Props {
|
||||||
className: string;
|
className?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
const { className } = Astro.props;
|
const { className } = Astro.props;
|
||||||
|
|
|
||||||
|
|
@ -12,9 +12,7 @@ const { url, icon, iconColor, name, level } = Astro.props;
|
||||||
|
|
||||||
<div class:list={['flex', 'flex-col', 'gap-2']}>
|
<div class:list={['flex', 'flex-col', 'gap-2']}>
|
||||||
<div class:list={['flex', 'gap-2', 'h-5']}>
|
<div class:list={['flex', 'gap-2', 'h-5']}>
|
||||||
<a href={url} target="_blank" rel="noopener noreferrer">
|
<Icon client:load name={icon} color={iconColor} size={20} url={url} />
|
||||||
<Icon client:load name={icon} color={iconColor} size={20} />
|
|
||||||
</a>
|
|
||||||
<Typography variant="tile-subtitle" class="text-gray-700">{name}</Typography>
|
<Typography variant="tile-subtitle" class="text-gray-700">{name}</Typography>
|
||||||
</div>
|
</div>
|
||||||
<SkillLevel skillLevel={level} />
|
<SkillLevel skillLevel={level} />
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,83 @@
|
||||||
---
|
---
|
||||||
|
import { Image } from '@astrojs/image/components';
|
||||||
|
|
||||||
|
import Button from '@/atoms/button.astro';
|
||||||
|
import IconButton from '@/atoms/icon-button.astro';
|
||||||
import SectionCard from '@/atoms/section-card.astro';
|
import SectionCard from '@/atoms/section-card.astro';
|
||||||
|
import Tag from '@/atoms/tag.astro';
|
||||||
|
import Typography from '@/atoms/typography.astro';
|
||||||
import type { MainSection } from '@/types/main-section';
|
import type { MainSection } from '@/types/main-section';
|
||||||
|
|
||||||
export interface Props extends MainSection {}
|
export interface Props extends MainSection {}
|
||||||
|
|
||||||
|
const {
|
||||||
|
image,
|
||||||
|
fullName,
|
||||||
|
role,
|
||||||
|
socials,
|
||||||
|
details,
|
||||||
|
description,
|
||||||
|
action: { label, url, downloadedFileName },
|
||||||
|
tags,
|
||||||
|
} = Astro.props;
|
||||||
---
|
---
|
||||||
|
|
||||||
<SectionCard>Main section</SectionCard>
|
<SectionCard>
|
||||||
|
<div class:list={['flex', 'gap-6', 'flex-col', 'sm:flex-row', 'items-start']}>
|
||||||
|
<div class:list={['flex', 'sm:flex-col', 'gap-4', 'items-center']}>
|
||||||
|
<Image
|
||||||
|
src={image}
|
||||||
|
alt={fullName}
|
||||||
|
class:list={['w-24', 'h-24', 'sm:w-36', 'sm:h-36', 'md:w-52', 'md:h-52', 'rounded-lg', 'max-w-none']}
|
||||||
|
/>
|
||||||
|
<Button href={url} download={downloadedFileName}>{label}</Button>
|
||||||
|
</div>
|
||||||
|
<div class:list={['w-full', 'flex', 'flex-col', 'gap-5']}>
|
||||||
|
<div class:list={['w-full', 'flex', 'flex-col', 'sm:flex-row', 'justify-between', 'gap-2']}>
|
||||||
|
<div class:list={['w-full']}>
|
||||||
|
<Typography variant="main-title">{fullName}</Typography>
|
||||||
|
<Typography variant="main-subtitle">{role}</Typography>
|
||||||
|
</div>
|
||||||
|
{
|
||||||
|
socials.length > 0 && (
|
||||||
|
<div class:list={['flex', 'gap-3', 'flex-wrap', 'sm:flex-nowrap']}>
|
||||||
|
{socials.map(({ icon, url: iconUrl }) => (
|
||||||
|
<IconButton href={iconUrl} icon={icon} size="small" target="_blank" />
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
</div>
|
||||||
|
<div class:list={['flex', 'flex-col', 'gap-6']}>
|
||||||
|
<div class:list={['inline-grid', 'xl:grid-cols-[auto_auto]']}>
|
||||||
|
{
|
||||||
|
details.map(({ label: detailLabel, value }) => (
|
||||||
|
<div class="w-fit">
|
||||||
|
<Typography variant="paragraph">
|
||||||
|
<span class="text-gray-700">{detailLabel}: </span>
|
||||||
|
<span class="break-all">{value}</span>
|
||||||
|
</Typography>
|
||||||
|
</div>
|
||||||
|
))
|
||||||
|
}
|
||||||
|
</div>
|
||||||
|
<div class:list={['flex', 'flex-col', 'gap-4']}>
|
||||||
|
<Typography variant="paragraph">{description}</Typography>
|
||||||
|
<div class:list={['flex', 'flex-wrap', 'gap-3']}>
|
||||||
|
{
|
||||||
|
tags.map(({ icon, iconColor, name, url: tagUrl }) =>
|
||||||
|
tagUrl ? (
|
||||||
|
<a href={tagUrl} target="_blank" rel="noopener noreferrer">
|
||||||
|
<Tag icon={icon ? { name: icon, color: iconColor } : undefined}>{name}</Tag>
|
||||||
|
</a>
|
||||||
|
) : (
|
||||||
|
<Tag icon={icon ? { name: icon, color: iconColor } : undefined}>{name}</Tag>
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</SectionCard>
|
||||||
|
|
|
||||||
37
src/pages/playground/main-section.astro
Normal file
37
src/pages/playground/main-section.astro
Normal file
|
|
@ -0,0 +1,37 @@
|
||||||
|
---
|
||||||
|
import MainSection from '@/sections/main-section.astro';
|
||||||
|
import type { MainSection as MainSectionData } from '@/types/main-section';
|
||||||
|
|
||||||
|
const mainSectionData: MainSectionData = {
|
||||||
|
image: import('@/assets/my-image.jpeg'),
|
||||||
|
fullName: 'Mark Freeman',
|
||||||
|
role: 'Senior React Developer',
|
||||||
|
details: [
|
||||||
|
{ label: 'Phone', value: '+48 604 343 212' },
|
||||||
|
{ label: 'Email', value: 'veeeery.long.email.address@gmail.com' },
|
||||||
|
{ label: 'From', value: 'Warsaw, Poland' },
|
||||||
|
{ label: 'Salary range', value: '18 000 - 25 000 PLN' },
|
||||||
|
],
|
||||||
|
description:
|
||||||
|
'Lorem ipsum dolor sit amet, consectetur adipiscing elit. In sodales ac dui at vestibulum. In condimentum metus id dui tincidunt, in blandit mi vehicula. Nulla lacinia, erat sit amet elementum vulputate, lectus mauris volutpat mi, vitae accumsan metus elit ut nunc. Vestibulum lacinia enim eget eros fermentum scelerisque. Proin augue leo, posuere ut imperdiet vitae, fermentum eu ipsum. Sed sed neque sagittis, posuere urna nec, commodo leo. Pellentesque posuere justo vitae massa volutpat maximus.',
|
||||||
|
tags: [{ name: 'Open for freelance' }, { name: 'Available for mentoring' }, { name: 'Working on side project' }],
|
||||||
|
action: {
|
||||||
|
label: 'Download CV',
|
||||||
|
url: '#',
|
||||||
|
},
|
||||||
|
socials: [
|
||||||
|
{ name: 'Facebook', icon: 'fa6-brands:facebook-f', url: '#' },
|
||||||
|
{ name: 'GitHub', icon: 'fa6-brands:github', url: '#' },
|
||||||
|
{ name: 'LinkedIn', icon: 'fa6-brands:linkedin-in', url: '#' },
|
||||||
|
{ name: 'Twitter', icon: 'fa6-brands:twitter', url: '#' },
|
||||||
|
],
|
||||||
|
};
|
||||||
|
---
|
||||||
|
|
||||||
|
<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">
|
||||||
|
<MainSection {...mainSectionData} />
|
||||||
|
</main>
|
||||||
|
</div>
|
||||||
|
</body>
|
||||||
|
|
@ -10,6 +10,7 @@ export interface MainSection {
|
||||||
action: {
|
action: {
|
||||||
label: string;
|
label: string;
|
||||||
url: string;
|
url: string;
|
||||||
|
downloadedFileName?: string;
|
||||||
};
|
};
|
||||||
socials: Social[];
|
socials: Social[];
|
||||||
config: Omit<SectionConfig, 'title'>;
|
config: Omit<SectionConfig, 'title'>;
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue