import React, { useReducer, useEffect, Fragment } from 'react'; import classNames from 'classnames'; import './VariantCell.scss'; import isTouchDevice from 'is-touch-device'; import { AddToBag } from './AddToBag'; import { Button } from './Button'; import { Badge } from './Badge'; import { Image } from './Image'; import { Size } from './Size'; import { ColorPicker } from './ColorPicker'; import { ProductPrice } from './ProductPrice'; import { returnHoverClass } from '../utils/Helpers'; import { MasterVariant, Variant, Inventory, ImageUrls, } from '../types/VariantTypes'; interface State { activeImage: number; hover: boolean; transition: boolean; imageCount: number; selectedVariant: Variant; selectedSize: Inventory | null; showUnlikeConfirmation: boolean; } type Action = | { type: 'StartRotate' } | { type: 'Transition' } | { type: 'StopRotate' } | { type: 'UpdateSelectedVariant'; variant: Variant } | { type: 'UpdatedSelectedSize'; size: Inventory } | { type: 'SetHover'; hover: boolean } | { type: 'ClearAllSelections'; attributes: MasterVariant } | { type: 'SetShowUnlikeConfirmation'; showUnlikeConfirmation: boolean }; interface Props { attributes?: MasterVariant; trunkshowId?: string; imageVersion?: 'small' | 'medium' | 'large'; unlikeConfirmation?: boolean; showFallEssentialBadge?: boolean; isBestSeller?: boolean; isFavorited?: boolean; centerCommonSizes?: boolean; onFavorite?: Function; onAddToBag?: Function; openInNewTab?: boolean; onClick?: Function; } const getImageUrls = (variantData: Variant): Array => new Array(variantData.primary_image_urls).concat( variantData.alternate_image_urls, ); const isSingleOSSize = (selectedVariant: Variant): boolean => { if (selectedVariant.inventory.length > 1) { return selectedVariant.inventory[0].size == 'OS'; } else { return false; } }; const getDefaultSelectedSize = (selectedVariant: Variant): Inventory | null => isSingleOSSize(selectedVariant) ? selectedVariant.inventory[0] : null; const commonSizes = ['US 4', 'S', 'IT 40', 'FR 36', 'UK 8', 'EU 36', '0', '27']; const getInitialSize = (selectedVariant: Variant): Inventory => { const commonSize = selectedVariant.inventory.find( size => size && commonSizes.includes(size.size) && size.can_sell, ); const firstAvailableSize = selectedVariant.inventory.find( size => size && size.can_sell, ); return commonSize || firstAvailableSize || selectedVariant.inventory[0]; }; const reducer = (state: State, action: Action): State => { switch (action.type) { case 'StartRotate': if (state.imageCount != 0 && state.activeImage < state.imageCount - 1) { return { ...state, activeImage: state.activeImage + 1, transition: false, }; } else { return { ...state, activeImage: 0, transition: false }; } case 'Transition': return { ...state, transition: true }; case 'StopRotate': return { ...state, activeImage: 0, transition: false }; case 'SetHover': return { ...state, hover: action.hover }; case 'UpdateSelectedVariant': return { ...state, selectedVariant: action.variant, selectedSize: getDefaultSelectedSize(action.variant), imageCount: getImageUrls(action.variant).length, activeImage: 0, }; case 'UpdatedSelectedSize': return { ...state, selectedSize: action.size }; case 'ClearAllSelections': return { ...state, selectedSize: getDefaultSelectedSize( action.attributes.master_variants_data, ), selectedVariant: action.attributes.master_variants_data, }; case 'SetShowUnlikeConfirmation': return { ...state, showUnlikeConfirmation: action.showUnlikeConfirmation, }; } }; export const VariantCell: React.FC = ({ attributes, trunkshowId, imageVersion = 'large', unlikeConfirmation = false, showFallEssentialBadge = false, isBestSeller = false, isFavorited = false, centerCommonSizes = false, onFavorite, onAddToBag, openInNewTab = false, onClick = undefined, }) => { const timerId: React.MutableRefObject = React.useRef(null); const initialState: State = { hover: false, transition: false, activeImage: 0, imageCount: getImageUrls(attributes.master_variants_data).length, selectedVariant: attributes.master_variants_data, selectedSize: getDefaultSelectedSize(attributes.master_variants_data), showUnlikeConfirmation: false, }; const [state, dispatch] = useReducer(reducer, initialState); useEffect(() => { timerId.current = window.setTimeout(() => { if (state.hover) { dispatch({ type: 'Transition' }); } }, 3000); return (): void => window.clearTimeout(timerId.current); }, [state.activeImage, state.hover]); useEffect(() => { if (timerId.current) { window.clearTimeout(timerId.current); } }, [state.selectedVariant]); useEffect(() => { if (state.hover) { dispatch({ type: 'Transition' }); } else { dispatch({ type: 'StopRotate' }); dispatch({ type: 'ClearAllSelections', attributes }); } }, [attributes, state.hover]); const onSetSize = (size: Inventory): void => dispatch({ type: 'UpdatedSelectedSize', size: size }); const onChangeVariant = (variant: Variant): void => dispatch({ type: 'UpdateSelectedVariant', variant: variant }); const onClickFavorite = (): void => { if (unlikeConfirmation && isFavorited) { dispatch({ type: 'SetShowUnlikeConfirmation', showUnlikeConfirmation: true, }); } else { onFavorite(!isFavorited, state.selectedVariant.id); } }; const onConfirmUnlike = (): void => { dispatch({ type: 'SetShowUnlikeConfirmation', showUnlikeConfirmation: false, }); onFavorite(false, state.selectedVariant.id); }; const getImageUrl = (imageUrls: ImageUrls): string => { return imageUrls[imageVersion]; }; const promoLabel = showFallEssentialBadge && state.selectedVariant.collections.includes('Sept19Offer') ? { label: 'Fall Essential', theme: 'brick' } : isBestSeller ? { label: 'Bestseller', theme: 'forest-green' } : null; return (
{ if (!isTouchDevice()) { dispatch({ type: 'SetHover', hover: true }); } }} onMouseLeave={(): void => { if (!isTouchDevice()) { dispatch({ type: 'SetHover', hover: false }); } }} onClick={(event: React.MouseEvent): void => onClick(event, state.selectedVariant) } data-variant-pk={attributes.id} data-product-id={attributes.product_id} id={'variant-cell-' + attributes.id} >
onClickFavorite()} className={`mo-display-icon-heart ${ isFavorited ? '-favorited' : '-unfavorited' } ${isTouchDevice() ? '' : '-non-touch'}`} /> {state.showUnlikeConfirmation && (
Do you really want to remove this item from your wishlist?
)}
{state.selectedVariant.label}
{attributes.designer_name}
{attributes.name}
{attributes.other_variants_data.length > 0 && (
+ More Colors
)} {attributes.tag && {attributes.tag}}
); }; interface QuickShopOverlayProps { attributes: MasterVariant; onChangeVariant: (variant: Variant) => void; selectedSize: Inventory; centerCommonSizes: boolean; onSetSize: (size: Inventory) => void; trunkshowId: string; onAddToBag: Function; selectedVariant: Variant; } const QuickShopOverlay: React.FC = ({ attributes, onChangeVariant, selectedSize, centerCommonSizes, onSetSize, trunkshowId, onAddToBag, selectedVariant, }) => { const variantsDataArray: Variant[] = [ attributes.master_variants_data, ...attributes.other_variants_data, ]; return (
{variantsDataArray.length > 1 && ( )} {isSingleOSSize(selectedVariant) && variantsDataArray.length <= 1 && (
{attributes.designer_name}
{attributes.name}
)} {!isSingleOSSize(selectedVariant) && ( )}
); };