import * as React from 'react'; import { FC, useRef, useState, useEffect, useContext, MouseEvent } from 'react'; import { style } from 'typestyle'; import useBorderRadius from '../../hooks/useBorderRadius'; import { ISettings, IProduct } from '../../types'; import { StoreContext } from '../../services/store'; interface PackshotProps { products: IProduct[]; shape: ISettings['thumbnail_shape']; cornerType: ISettings['thumbnail_corners']; heightPercentage?: number; hidden?: boolean; onContainerClick?: () => void; } export const getHeightPercentage = (shape: ISettings['thumbnail_shape'], isMobile: boolean) => { const mobileReductionFactor = isMobile ? 0.8 : 1; if (shape === 'square') return 27 * mobileReductionFactor; if (shape === 'portrait') return 24 * mobileReductionFactor; return 20 * mobileReductionFactor; }; export const Packshot: FC = ({ products, shape, cornerType, heightPercentage, hidden = false, onContainerClick }) => { const containerRef = useRef(null); const [containerHeight, setContainerHeight] = useState(0); const context = useContext(StoreContext); const handlePackshotClick = (e: MouseEvent, product: IProduct) => { // If container click handler is provided, use it and prevent default behavior if (onContainerClick) { e.preventDefault(); e.stopPropagation(); onContainerClick(); return; } if (context.post) { context.triggerEvent( 'packshotClicked', { post: context.post, originalEvent: e.nativeEvent, product: product.id, }, context.data.id, ); setTimeout(() => { window.open(product.link, '_blank'); }, 300); } }; // observe container height (to calculate border radius based on image size) useEffect(() => { if (!containerRef.current) return; const updateSize = () => { const height = containerRef.current?.clientHeight || 0; setContainerHeight(height); }; const resizeObserver = new ResizeObserver(updateSize); resizeObserver.observe(containerRef.current); updateSize(); // eslint-disable-next-line consistent-return return () => resizeObserver.disconnect(); }, []); // calculate image size from container height (90%) const imageSizePx = Math.floor(containerHeight * 0.9); const borderRadius = useBorderRadius(imageSizePx, imageSizePx, cornerType); if (!products?.length) return null; const finalHeightPercentage = heightPercentage !== undefined ? heightPercentage : getHeightPercentage(shape, context.isMobile); const containerProps = onContainerClick ? { onClick: onContainerClick, role: "button", tabIndex: 0, } : {}; return (
{products.slice(0, 4).map((product) => (
{product.title} handlePackshotClick(e, product)} />
))}
); }; const styles = { packshotContainer: ( heightPercentage: number, hidden: boolean, isMobile: boolean, clickable: boolean = false ) => `${style({ position: 'absolute', zIndex: 2, bottom: '-4px', left: 0, right: 0, height: `${heightPercentage}%`, backgroundColor: 'rgba(255, 255, 255, 0.1)', backdropFilter: 'blur(6px)', '-webkit-backdrop-filter': 'blur(6px)', display: 'flex', alignItems: 'center', alignContent: 'center', justifyContent: 'flex-start', gap: '2px', padding: '4px', visibility: hidden ? 'hidden' : 'visible', pointerEvents: hidden ? 'none' : 'auto', cursor: clickable ? 'pointer' : 'default', $nest: { '&:hover': { backgroundColor: 'rgba(255, 255, 255, 0.2)', backdropFilter: 'blur(20px)', '-webkit-backdrop-filter': 'blur(20px)', }, }, })} packshot`, productWrapper: style({ flex: '1 1 0', minWidth: 0, maxWidth: '22.5%', // 4 products max (90/4) aspectRatio: '1', // square ratio position: 'relative', marginLeft: '4px', }), productImageStyle: (borderRadius: string) => ({ width: '90%', height: '90%', objectFit: 'cover', borderRadius, cursor: 'pointer', margin: '4px', } as const), }; export default Packshot;