import * as React from 'react'; import { useState, useCallback, useRef, FC, MouseEvent, useContext, useEffect } from 'react'; import { keyframes, style } from 'typestyle'; import useBorderRadius from '../../../hooks/useBorderRadius'; import useAccessibilityMessage from '../../../hooks/useAccessibilityMessage'; import { getContentAnnouncementMessage, getSrOnlyStyle, } from '../../Layouts/Carousel/shared/accessibility'; import Arrow from '../../Icons/Arrows/Arrow'; import CloseIcon from '../../Icons/Close'; import { StoreContext, useStore } from '../../../services/store'; import { IMedia, Direction, ISettings, DisplayCustomShopThisLook } from '../../../types'; import { productListZIndex } from '../../consts'; import Content from '../../PostViewer/components/Content'; import ProductModal from '../../PostViewer/components/Products/ProductModal'; interface ModalProps { post: IMedia; settings: ISettings; totalPosts: number; onClose: () => void; onPostChange: (post: IMedia | null) => void; classPrefix: string; } const ShopThisLookModal: FC = ({ post, totalPosts, onClose, onPostChange, settings, classPrefix, }) => { const store = useStore(); const context = useContext(StoreContext); const contentRef = useRef(null); const containerRef = useRef(null); const { accessibilityMessage, announce } = useAccessibilityMessage(); const [isClosing, setIsClosing] = useState(false); const baseSize = Math.min(window.innerHeight * 0.7, window.innerWidth * 0.7); const borderRadius = useBorderRadius(baseSize, baseSize, settings.thumbnail_corners); const isShopThisLookModalEnabled = settings.shop_this_look_display === 'modal' && !!(post.products && post.products.length > 0); // Announce content details when modal opens or post changes useEffect(() => { announce(getContentAnnouncementMessage(post, post.postIndex, totalPosts)); }, [post.id, totalPosts, announce]); // Announce whenever the post changes const handleArrowClick = (dir: Direction) => { const newIndex = dir === 'left' ? post.postIndex - 1 : post.postIndex + 1; // Handle infinite scroll with wrap-around let validIndex = newIndex; if (validIndex < 0) { validIndex = totalPosts - 1; } else if (validIndex >= totalPosts) { validIndex = 0; } const targetPost = store.data.content.medias[validIndex]; if (!targetPost) return; store.setStoreState((state) => ({ ...state, currentPosition: validIndex, })); onPostChange(targetPost); // Announcement handled by useEffect when post changes }; const handleClose = useCallback( (e?: MouseEvent) => { e?.stopPropagation(); setIsClosing(true); setTimeout(() => { onClose(); }, 300); }, [onClose], ); return (
{accessibilityMessage}
e.stopPropagation()}> handleArrowClick('left')} show={true} classPrefix={classPrefix} size='large' ariaLabel='Previous post' />
{isShopThisLookModalEnabled ? (
) : null}
handleArrowClick('right')} show={true} classPrefix={classPrefix} size='large' ariaLabel='Next post' />
); }; // Keyframes are defined once at module level so the animation name is stable across re-renders. const scaleUp = keyframes({ from: { transform: 'scale(0.5)', opacity: 0, }, to: { transform: 'scale(1)', opacity: 1, }, }); const scaleDown = keyframes({ from: { transform: 'scale(1)', opacity: 1, }, to: { transform: 'scale(0.5)', opacity: 0, }, }); const styles = { desktop: style({ position: 'fixed', top: 0, left: 0, right: 0, bottom: 0, padding: 40, overflow: 'auto', backgroundColor: 'rgba(0, 0, 0, 0.5)', zIndex: productListZIndex, }), containerWrapper: style({ display: 'flex', alignItems: 'center', justifyContent: 'center', width: 'fit-content', maxWidth: '100%', margin: '0 auto', position: 'relative', gap: '20px', }), container: (props: { borderRadius: string; shopThisLook: boolean; baseSize: number; isClosing: boolean; }) => style({ borderRadius: props.borderRadius, backgroundColor: 'white', position: 'relative', alignItems: 'center', width: props.shopThisLook ? props.baseSize * 2 : props.baseSize, height: props.baseSize, display: 'flex', animation: props.isClosing ? `${scaleDown} 0.3s ease-in forwards` : `${scaleUp} 0.3s ease-out forwards`, }), blurredBackground: style({ position: 'absolute', top: 0, left: 0, width: '100%', height: '100%', backgroundSize: 'cover', backgroundPosition: 'center', filter: 'blur(20px)', transform: 'scale(1.2)', zIndex: 0, }), content: (shopThisLook: boolean) => style({ display: 'flex', flexDirection: 'row', width: shopThisLook ? '50%' : '100%', height: '100%', overflow: 'hidden', position: 'relative', borderTopLeftRadius: 'inherit', borderTopRightRadius: shopThisLook ? 0 : 'inherit', borderBottomRightRadius: shopThisLook ? 0 : 'inherit', borderBottomLeftRadius: 'inherit', }), details: (customShopThisLook?: DisplayCustomShopThisLook) => style({ width: '50%', height: '100%', overflow: typeof customShopThisLook === 'function' ? 'auto' : 'hidden', borderTopRightRadius: 'inherit', borderBottomRightRadius: 'inherit', }), closeButton: style({ position: 'absolute', top: 27, background: 'none', border: 'none', padding: 0, right: 20, cursor: 'pointer', zIndex: 2, }), }; export default ShopThisLookModal;