Some checks failed
Main Branch / Run Prettier check (push) Has been cancelled
Main Branch / Run TypeScript check (push) Has been cancelled
Main Branch / Run Astro check (push) Has been cancelled
Main Branch / Run Percy check (push) Has been cancelled
Main Branch / Create release (push) Has been cancelled
Main Branch / Deploy to Netlify (push) Has been cancelled
Main Branch / Run Lighthouse check (push) Has been cancelled
95 lines
3.4 KiB
Text
95 lines
3.4 KiB
Text
---
|
|
import { nanoid } from 'nanoid';
|
|
import type { PortfolioSection, Project } from '@/types/sections/portfolio-section.types';
|
|
import Description from '@/web/components/description.astro';
|
|
import LabelledValue from '@/web/components/labelled-value.astro';
|
|
import LinkButton from '@/web/components/link-button.astro';
|
|
import Photo from '@/components/photo.astro';
|
|
import TagsList from '@/web/components/tags-list.astro';
|
|
import Timestamp from '@/web/components/timestamp.astro';
|
|
import Typography from '@/web/components/typography.astro';
|
|
import Thumbnail from '@/web/components/thumbnail.astro';
|
|
|
|
export interface Props extends Project {
|
|
screenshotsConfig?: PortfolioSection['config']['screenshots'];
|
|
}
|
|
|
|
const { dates, description, details, image, links, name, tagsList, screenshots, screenshotsConfig } = Astro.props;
|
|
|
|
const alt = `${name} project thumbnail`;
|
|
|
|
const galleryId = nanoid(8);
|
|
const hasScreenshots = screenshots?.length && screenshots.length > 0;
|
|
const screenshotsIcon = screenshotsConfig?.icon || 'fa6-solid:image';
|
|
const screenshotsTooltip = screenshotsConfig?.title || 'Screenshots';
|
|
---
|
|
|
|
<div class="flex flex-col">
|
|
<!-- gap-6">-->
|
|
<div class="flex flex-col">
|
|
<!-- gap-4">-->
|
|
<div class="flex">
|
|
<!-- gap-6">-->
|
|
<div class="flex w-full flex-col gap-4">
|
|
<div class="flex gap-4">
|
|
<Thumbnail src={image} alt={alt} size="small" />
|
|
<div class="flex w-full justify-between">
|
|
<div>
|
|
<Typography variant="item-title">{name}</Typography>
|
|
<!--<Timestamp dates={dates} />-->
|
|
<Typography variant="item-subtitle-secondary">{description}</Typography>
|
|
</div>
|
|
<div class="flex gap-2">
|
|
{links.map((link) => <LinkButton {...link} />)}
|
|
{
|
|
hasScreenshots && (
|
|
<LinkButton icon={screenshotsIcon} name={screenshotsTooltip} as="button" data-gallery={galleryId} />
|
|
)
|
|
}
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div class="inline-grid w-full xl:grid-cols-[auto_auto]">
|
|
{details.map((detail) => <LabelledValue {...detail} />)}
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<!--<Description content={description} class="col-span-3 col-start-1" /> -->
|
|
</div>
|
|
<TagsList {...tagsList} />
|
|
<div class="hidden" id={galleryId}>
|
|
{screenshots?.map((screenshot) => <Photo {...screenshot} />)}
|
|
</div>
|
|
</div>
|
|
|
|
<script>
|
|
import type { PhotoSwipeOptions, DataSource } from 'photoswipe';
|
|
|
|
const buttons = [...document.querySelectorAll('[data-gallery]')] as HTMLButtonElement[];
|
|
|
|
if (buttons.length > 0) {
|
|
import('photoswipe/style.css');
|
|
|
|
const getOptionsForButton = (button: HTMLButtonElement): PhotoSwipeOptions => {
|
|
const galleryId = String(button.dataset.gallery);
|
|
const galleryWrapper = document.getElementById(galleryId) as HTMLElement;
|
|
const screenshots = [...galleryWrapper.children] as HTMLImageElement[];
|
|
const dataSource: DataSource = screenshots.map((img) => ({
|
|
src: img.src,
|
|
width: img.width,
|
|
height: img.height,
|
|
alt: img.alt,
|
|
}));
|
|
|
|
return { dataSource, showHideAnimationType: 'none', index: 0 };
|
|
};
|
|
|
|
import('photoswipe').then(({ default: PhotoSwipe }) => {
|
|
buttons.forEach((button) =>
|
|
button.addEventListener('click', () => {
|
|
new PhotoSwipe(getOptionsForButton(button)).init();
|
|
})
|
|
);
|
|
});
|
|
}
|
|
</script>
|