'\n );\n return;\n }\n\n portal.appendChild(currEl);\n return () => {\n portal.removeChild(currEl);\n };\n }, [el]);\n\n return createPortal(children, el.current);\n}\n\nfunction Portal({ children }) {\n const [isHydrated, setIsHydrated] = React.useState(false);\n\n React.useEffect(() => {\n setIsHydrated(true);\n }, []);\n\n return isHydrated ? (\n
{children}\n ) : (\n
{children}\n );\n}\n\nexport { Portal };\n","import { css } from 'styled-components';\n\nimport { TooltipModel } from '@notino/react-styleguide';\n\nexport const TOOLTIP_OFFSET_PX = 16;\n\nexport const getTooltipPositionStyles = ({\n position,\n interactive,\n distanceFromArrowToWindowEdge,\n}: {\n position: TooltipModel.Position;\n distanceFromArrowToWindowEdge: number;\n interactive: boolean;\n}) => {\n const offset = `${TOOLTIP_OFFSET_PX}px`;\n const offsetProp = interactive ? 'padding' : 'margin';\n const sideOffset = Math.min(distanceFromArrowToWindowEdge, 30);\n\n switch (position) {\n case TooltipModel.Position.topAutomatic:\n case TooltipModel.Position.topCenter:\n return css`\n bottom: 100%;\n left: 50%;\n transform: translateX(-50%);\n ${offsetProp}-bottom: ${offset};\n `;\n case TooltipModel.Position.topLeft:\n return css`\n bottom: 100%;\n left: 50%;\n transform: translateX(-${sideOffset}px);\n ${offsetProp}-bottom: ${offset};\n `;\n case TooltipModel.Position.topRight:\n return css`\n bottom: 100%;\n right: 50%;\n transform: translateX(${sideOffset}px);\n ${offsetProp}-bottom: ${offset};\n `;\n case TooltipModel.Position.bottomAutomatic:\n case TooltipModel.Position.bottomCenter:\n return css`\n top: 100%;\n left: 50%;\n transform: translateX(-50%);\n ${offsetProp}-top: ${offset};\n `;\n case TooltipModel.Position.bottomLeft:\n return css`\n top: 100%;\n left: 50%;\n transform: translateX(-${sideOffset}px);\n ${offsetProp}-top: ${offset};\n `;\n case TooltipModel.Position.bottomRight:\n return css`\n top: 100%;\n right: 50%;\n transform: translateX(${sideOffset}px);\n ${offsetProp}-top: ${offset};\n `;\n case TooltipModel.Position.leftCenter:\n return css`\n top: 50%;\n transform: translateY(-50%);\n right: 100%;\n ${offsetProp}-right: ${offset};\n `;\n case TooltipModel.Position.leftTop:\n return css`\n top: 50%;\n transform: translateY(-20px);\n right: 100%;\n ${offsetProp}-right: ${offset};\n `;\n case TooltipModel.Position.leftBottom:\n return css`\n bottom: 50%;\n transform: translateY(20px);\n right: 100%;\n ${offsetProp}-right: ${offset};\n `;\n case TooltipModel.Position.rightCenter:\n return css`\n top: 50%;\n transform: translateY(-50%);\n left: 100%;\n ${offsetProp}-left: ${offset};\n `;\n case TooltipModel.Position.rightTop:\n return css`\n top: 50%;\n transform: translateY(-20px);\n left: 100%;\n ${offsetProp}-left: ${offset};\n `;\n case TooltipModel.Position.rightBottom:\n return css`\n bottom: 50%;\n transform: translateY(20px);\n left: 100%;\n ${offsetProp}-left: ${offset};\n `;\n }\n};\n","import { css } from 'styled-components';\n\nimport { TooltipModel, theme } from '@notino/react-styleguide';\n\nimport { TOOLTIP_OFFSET_PX } from './getTooltipPositionStyles';\n\nconst topAndBottomCommon = css`\n left: 50%;\n transform: translateX(-50%);\n`;\nconst leftAndRightCommon = css`\n top: 50%;\n transform: translateY(-50%);\n`;\n\nconst arrowHeightPx = 10;\nconst arrowHeight = `${arrowHeightPx}px`;\nconst arrowOffset = TOOLTIP_OFFSET_PX - arrowHeightPx;\n\nconst topCommon = css`\n ::before {\n bottom: calc(100% + ${arrowOffset + 1}px);\n }\n ::after {\n bottom: calc(100% + ${arrowOffset}px);\n border-top: ${arrowHeight} solid ${theme.palette.neutral};\n }\n`;\nconst bottomCommon = css`\n ::before {\n top: calc(100% + ${arrowOffset + 1}px);\n }\n ::after {\n top: calc(100% + ${arrowOffset}px);\n border-bottom: ${arrowHeight} solid ${theme.palette.neutral};\n }\n`;\nconst leftCommon = css`\n ::before {\n right: calc(100% + ${arrowOffset + 1}px);\n }\n ::after {\n right: calc(100% + ${arrowOffset}px);\n border-left: ${arrowHeight} solid ${theme.palette.neutral};\n }\n`;\nconst rightCommon = css`\n ::before {\n left: calc(100% + ${arrowOffset + 1}px);\n }\n ::after {\n left: calc(100% + ${arrowOffset}px);\n border-right: ${arrowHeight} solid ${theme.palette.neutral};\n }\n`;\n\nexport const getArrowPositionStyles = ({\n position,\n}: {\n position: TooltipModel.Position;\n}) => {\n switch (position) {\n case TooltipModel.Position.topCenter:\n return css`\n &:after,\n &:before {\n ${topAndBottomCommon}\n border-left: ${arrowHeight} solid transparent;\n border-right: ${arrowHeight} solid transparent;\n border-top: ${arrowHeight} solid white;\n }\n ${topCommon}\n `;\n case TooltipModel.Position.topLeft:\n return css`\n &:after,\n &:before {\n ${topAndBottomCommon}\n border-left: ${arrowHeight} solid transparent;\n border-right: ${arrowHeight} solid transparent;\n border-top: ${arrowHeight} solid white;\n }\n ${topCommon}\n `;\n case TooltipModel.Position.topRight:\n return css`\n &:after,\n &:before {\n ${topAndBottomCommon}\n\n border-left: ${arrowHeight} solid transparent;\n border-right: ${arrowHeight} solid transparent;\n border-top: ${arrowHeight} solid white;\n }\n ${topCommon}\n `;\n case TooltipModel.Position.bottomCenter:\n return css`\n &:after,\n &:before {\n ${topAndBottomCommon}\n border-left: ${arrowHeight} solid transparent;\n border-right: ${arrowHeight} solid transparent;\n border-bottom: ${arrowHeight} solid white;\n }\n ${bottomCommon}\n `;\n case TooltipModel.Position.bottomLeft:\n return css`\n &:after,\n &:before {\n ${topAndBottomCommon}\n border-left: ${arrowHeight} solid transparent;\n border-right: ${arrowHeight} solid transparent;\n border-bottom: ${arrowHeight} solid white;\n }\n ${bottomCommon}\n `;\n case TooltipModel.Position.bottomRight:\n return css`\n &:after,\n &:before {\n ${topAndBottomCommon}\n border-left: ${arrowHeight} solid transparent;\n border-right: ${arrowHeight} solid transparent;\n border-bottom: ${arrowHeight} solid white;\n }\n ${bottomCommon}\n `;\n case TooltipModel.Position.leftCenter:\n return css`\n &:after,\n &:before {\n ${leftAndRightCommon}\n border-top: ${arrowHeight} solid transparent;\n border-bottom: ${arrowHeight} solid transparent;\n border-left: ${arrowHeight} solid white;\n }\n ${leftCommon}\n `;\n case TooltipModel.Position.leftTop:\n return css`\n &:after,\n &:before {\n ${leftAndRightCommon}\n border-top: ${arrowHeight} solid transparent;\n border-bottom: ${arrowHeight} solid transparent;\n border-left: ${arrowHeight} solid white;\n }\n ${leftCommon}\n `;\n case TooltipModel.Position.leftBottom:\n return css`\n &:after,\n &:before {\n ${leftAndRightCommon}\n border-top: ${arrowHeight} solid transparent;\n border-bottom: ${arrowHeight} solid transparent;\n border-left: ${arrowHeight} solid white;\n }\n ${leftCommon}\n `;\n case TooltipModel.Position.rightCenter:\n return css`\n &:after,\n &:before {\n ${leftAndRightCommon}\n border-top: ${arrowHeight} solid transparent;\n border-bottom: ${arrowHeight} solid transparent;\n border-right: ${arrowHeight} solid white;\n }\n ${rightCommon}\n `;\n case TooltipModel.Position.rightTop:\n return css`\n &:after,\n &:before {\n ${leftAndRightCommon}\n border-top: ${arrowHeight} solid transparent;\n border-bottom: ${arrowHeight} solid transparent;\n border-right: ${arrowHeight} solid white;\n }\n ${rightCommon}\n `;\n case TooltipModel.Position.rightBottom:\n return css`\n &:after,\n &:before {\n ${leftAndRightCommon}\n border-top: ${arrowHeight} solid transparent;\n border-bottom: ${arrowHeight} solid transparent;\n border-right: ${arrowHeight} solid white;\n }\n ${rightCommon}\n `;\n\n default:\n throw Error('Invalid position' + position);\n }\n};\n","import styled, { css } from 'styled-components';\n\nimport { TooltipModel, theme } from '@notino/react-styleguide';\n\nimport { getArrowPositionStyles } from './getArrowPositionStyles';\nimport { getTooltipPositionStyles } from './getTooltipPositionStyles';\n\nexport const Wrapper = styled.div`\n display: inline-block;\n`;\n\nexport const Content = styled.div.attrs({ role: 'tooltip' })`\n padding: 0.5rem;\n font-size: 0.8rem;\n min-width: 15rem;\n background: white;\n border: 1px solid ${theme.palette.neutral};\n`;\n\nexport const Placeholder = styled.div`\n position: absolute;\n z-index: 99;\n`;\n\nexport const RelativeWrapper = styled.div<{\n position: TooltipModel.Position;\n visible: boolean;\n}>`\n position: relative;\n width: 100%;\n height: 100%;\n\n &:after,\n &:before {\n content: '';\n position: absolute;\n width: 0;\n height: 0;\n pointer-events: none;\n opacity: ${({ visible }) => (visible ? 1 : 0)};\n }\n ::before {\n z-index: 1;\n }\n ${getArrowPositionStyles}\n`;\n\nexport const TooltipWrapper = styled.div<{\n position: TooltipModel.Position;\n visible: boolean;\n interactive: boolean;\n distanceFromArrowToWindowEdge: number;\n}>`\n position: absolute;\n pointer-events: ${({ visible }) => (visible ? 'all' : 'none')};\n ${({ visible }) =>\n css`\n opacity: ${visible ? 1 : 0};\n `}\n\n ${getTooltipPositionStyles}\n`;\n","import * as React from 'react';\nimport { PropsWithChildren } from 'react';\n\nimport { StyledComponent } from 'styled-components';\n\nimport { TooltipModel } from '@notino/react-styleguide';\n\nimport { Portal } from './Portal';\nimport {\n Content,\n Placeholder,\n RelativeWrapper,\n TooltipWrapper,\n Wrapper,\n} from './styled';\n\ntype Props = PropsWithChildren<{\n content: React.ReactNode;\n position?: TooltipModel.Position;\n interactive?: boolean;\n isOpen?: boolean;\n disabled?: boolean;\n onShow?: () => void;\n onHide?: () => void;\n}>;\n\ntype CompoundComponent = React.FC
& {\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n Content: StyledComponent<'div', any>;\n};\n\nexport const PortalTooltip: CompoundComponent = ({\n children,\n content,\n onShow,\n onHide,\n disabled,\n interactive = false,\n isOpen: isOpenProp,\n position: initialPosition = TooltipModel.Position.topCenter,\n}) => {\n const [position, setPosition] = React.useState(initialPosition);\n const [isOpen, setIsOpen] = React.useState(false);\n const [placeholderStyle, setPlaceHolderStyle] =\n React.useState({});\n\n const wrapperRef = React.useRef();\n const tooltipWrapperRef = React.useRef();\n const distanceFromArrowToWindowEdge = React.useRef(Number.MAX_VALUE);\n\n const handleSetStyles = React.useCallback((el: HTMLElement) => {\n const { left, top, width, height } = el.getBoundingClientRect();\n setPlaceHolderStyle({\n top: top + window.pageYOffset,\n left,\n width,\n height,\n });\n }, []);\n\n const handleShowTooltip: React.MouseEventHandler = (e) => {\n if (disabled) {\n return;\n }\n const el = e.currentTarget as HTMLElement;\n handleSetStyles(el);\n setIsOpen(true);\n onShow?.();\n };\n\n const handleHideTooltip = () => {\n if (disabled) {\n return;\n }\n setIsOpen(false);\n onHide?.();\n };\n\n React.useEffect(() => {\n const { left, right, width } = wrapperRef.current.getBoundingClientRect();\n const gutter = 4;\n // since arrow is in the middle of wrapped component\n // I add 'width/2' to each computation\n distanceFromArrowToWindowEdge.current = Math.min(\n left + width / 2 - gutter,\n window.innerWidth - right + width / 2 - gutter\n );\n });\n\n React.useEffect(() => {\n handleSetStyles(wrapperRef.current);\n }, [handleSetStyles]);\n\n const positionRef = React.useRef(position);\n React.useEffect(() => {\n positionRef.current = position;\n });\n\n // adjust position if tooltip gets off screen\n React.useEffect(() => {\n const el = tooltipWrapperRef.current;\n const { left, width } = el.getBoundingClientRect();\n\n const overflowsFromRight = left + width > window.innerWidth;\n if (left < 0 && overflowsFromRight) {\n return;\n }\n\n // using positionRef instead of position to prevent infinite update loop\n if (positionRef.current.startsWith('top')) {\n if (left < 0) {\n setPosition(TooltipModel.Position.topLeft);\n }\n if (overflowsFromRight) {\n setPosition(TooltipModel.Position.topRight);\n }\n } else {\n if (left < 0) {\n setPosition(TooltipModel.Position.bottomLeft);\n }\n if (overflowsFromRight) {\n setPosition(TooltipModel.Position.bottomRight);\n }\n }\n }, [placeholderStyle]);\n\n const isVisible = typeof isOpenProp !== 'undefined' ? isOpenProp : isOpen;\n\n return (\n \n {children}\n\n \n \n \n \n {content}\n \n \n \n \n \n );\n};\n\nPortalTooltip.Content = Content;\n","enum LineHeight {\n NORMAL = 'normal',\n MID = '3.5rem',\n LOW = '2.7rem',\n}\n\nexport default LineHeight;\n","import * as React from 'react';\n\nimport { CurrencyStyled } from './styled';\n\nexport interface ICurrencyProps {\n isSpaced: boolean;\n isPrefix: boolean;\n content: string;\n}\n\nconst Currency = (props: ICurrencyProps) => ;\n\nexport default Currency;\n","import LineHeight from './LineHeight';\nimport { Wrapper } from './styled';\n\nexport interface IWrapperProps {\n lineHeight?: LineHeight;\n}\n\nexport default Wrapper;\n","import * as React from 'react';\n\nimport Currency from './Currency';\nimport LineHeight from './LineHeight';\nimport Wrapper from './Wrapper';\n\nconst Span = (props) => ;\n\nexport { Wrapper, Currency, Span, LineHeight };\n","import styled, { css } from 'styled-components';\n\nimport { breakpoints } from '@notino/react-styleguide';\n\nimport { ICurrencyProps } from './Currency';\nimport LineHeight from './LineHeight';\nimport { IWrapperProps } from './Wrapper';\n\nexport const CurrencyStyled = styled.span`\n font-size: 1.5rem;\n font-weight: normal;\n\n @media (min-width: ${breakpoints.sm}) {\n font-size: 2rem;\n }\n\n ${({ isSpaced, isPrefix }: ICurrencyProps) => {\n if (isSpaced)\n return isPrefix\n ? css`\n :after {\n content: ' ';\n }\n `\n : css`\n :before {\n content: ' ';\n }\n `;\n }}\n`;\n\nexport const Wrapper = styled.div`\n font-weight: bold;\n font-size: 1.87rem;\n -webkit-flex-grow: 0;\n flex-grow: 0;\n -webkit-flex-shrink: 0;\n flex-shrink: 0;\n line-height: ${LineHeight.NORMAL};\n white-space: nowrap;\n\n @media (min-width: ${breakpoints.sm}) {\n font-size: 2.6rem;\n line-height: ${(props: IWrapperProps) =>\n props.lineHeight || LineHeight.NORMAL};\n }\n`;\n","import * as React from 'react';\n\nimport ErrorBoundary from '@components/ErrorBoundary';\nimport { IPrice } from '@containers/ProductListing/model';\n\nimport { Wrapper, Currency, Span, LineHeight } from './components';\nimport { useFormattedPrice } from './useFormattedPrice';\n\ninterface IPriceLabel {\n price: IPrice;\n lineHeight?: LineHeight;\n currencyComponent?: React.ComponentType>;\n wrapperComponent?: React.ComponentType>;\n id?: string;\n currencySymbolOverride?: string;\n}\n\nconst PriceLabel: React.FC = ({\n price,\n lineHeight,\n currencyComponent: CurrencyComponent = Currency,\n wrapperComponent: WrapperComponent = Wrapper,\n id,\n currencySymbolOverride,\n}) => {\n const formattedPriceObj = useFormattedPrice(price);\n\n if (!formattedPriceObj) {\n return null;\n }\n\n const {\n formattedPrice,\n currentLocale: { priceFormat },\n currencySymbol,\n currentCurrency,\n } = formattedPriceObj;\n\n // format is specified for ex like 'v s' where 'v' is value, 's' is symbol\n const isPrefix = priceFormat.startsWith('s');\n const isSpaced = priceFormat.charAt(1) === ' ';\n\n const symbol = (\n \n {currencySymbolOverride || currencySymbol}\n \n );\n\n return (\n \n \n {isPrefix && symbol}\n {formattedPrice}\n {!isPrefix && symbol}\n \n \n );\n};\n\nexport default PriceLabel;\n","import { useIntl, IntlFormatters } from 'react-intl';\n\nimport { GetPriceConfigQuery } from '@notino/shared/definitions/types';\n\nimport { usePriceConfig } from '@components/PriceLabel/usePriceConfig';\nimport { IPrice } from '@containers/ProductListing/model';\n\ninterface IFormattedPrice extends GetPriceConfigQuery {\n formattedPrice: string;\n currencySymbol: string;\n formattedPriceWithCurrency: string;\n}\n\nconst formatPrice = (\n formatNumber: IntlFormatters['formatNumber'],\n priceConfig: GetPriceConfigQuery,\n price: IPrice\n) => {\n const { currentCurrency, currentLocale } = priceConfig;\n const decimalPlaces = price.decimalPlaces || currentLocale.priceDecimalPlaces;\n\n const formattedPrice = formatNumber(price.value, {\n minimumFractionDigits: decimalPlaces,\n maximumFractionDigits: decimalPlaces,\n }).toString();\n\n const currencySymbol =\n currentCurrency.symbol || currentLocale.currency || price.currency;\n\n const isPrefix = currentLocale.priceFormat?.startsWith('s');\n const isSpaced = currentLocale.priceFormat?.charAt(1) === ' ';\n\n const formattedPriceWithCurrency = `${isPrefix ? currencySymbol : ''}${\n isPrefix && isSpaced ? ' ' : ''\n }${formattedPrice}${!isPrefix && isSpaced ? ' ' : ''}${\n !isPrefix ? currencySymbol : ''\n }`;\n\n return {\n formattedPrice,\n currencySymbol,\n currentCurrency,\n currentLocale,\n formattedPriceWithCurrency,\n };\n};\n\nexport const useFormatPrice = () => {\n const { formatNumber } = useIntl();\n const { loading, error, data } = usePriceConfig();\n\n return (price: IPrice) => {\n if (loading || error || !data || !price) {\n return null;\n }\n return formatPrice(formatNumber, data, price);\n };\n};\n\nexport const useFormattedPrice = (price: IPrice): IFormattedPrice => {\n const formatPriceFn = useFormatPrice();\n\n return formatPriceFn(price);\n};\n","import { useQuery, QueryResult } from '@apollo/client';\n\nimport { GetPriceConfigQuery } from '@notino/shared/definitions/types';\n\nimport getPriceConfigQuery from './priceConfig.graphql';\n\nexport const usePriceConfig = (): QueryResult => {\n return useQuery(getPriceConfigQuery);\n};\n","import loadable from '@loadable/component';\n\nexport const ProductSlider = loadable(() => import('./LoadableProductSlider'), {\n ssr: false,\n});\n","import * as React from 'react';\n\nimport styled from 'styled-components';\n\nimport { Colors, IBasicStyledProps } from '@notino/react-styleguide';\n\nimport { registerKeyboardListeners } from '@helpers/accessibility/registerKeyboardListeners';\nimport { arrayFromNumber } from '@helpers/utils';\n\ninterface IProgressProps extends IBasicStyledProps {\n heightInPx?: number;\n}\n\nconst Progress = styled.div`\n height: ${(props: IProgressProps) =>\n props.heightInPx ? props.heightInPx : 8}px;\n background: ${(props: IProgressProps) => props.theme.palette.neutralLight};\n position: relative;\n`;\n\ninterface IBarProps extends IProgressProps {\n lineWidth: number;\n color?: Colors;\n animationDuration?: number;\n}\nconst Bar = styled.div`\n width: ${(props: IBarProps) => `${props.lineWidth}%`};\n height: ${(props: IBarProps) => (props.heightInPx ? props.heightInPx : 8)}px;\n background: ${(props: IBarProps) =>\n props.color ? props.theme.palette[props.color] : props.theme.palette.basic};\n transition: ${(props: IBarProps) =>\n props.animationDuration ? props.animationDuration : 0.2}s\n ease;\n`;\n\nconst Segments = styled.div`\n position: absolute;\n height: 100%;\n width: 100%;\n line-height: 0;\n`;\n\ninterface ISegmentProps {\n selectable: boolean;\n}\nconst Segment = styled.div`\n display: inline-block;\n width: 20%;\n height: 100%;\n cursor: ${(props: ISegmentProps) => (props.selectable ? 'pointer' : 'auto')};\n\n :not(:last-child) {\n border-right: 1px solid ${(props) => props.theme.palette.basicInverse};\n }\n`;\n\ninterface IProgressBarProps {\n max: number;\n value: number;\n segments?: boolean;\n selectable?: boolean;\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n onSelect?: any;\n color?: Colors;\n height?: number;\n animationDuration?: number;\n animationDelay?: number;\n name: string;\n label?: string;\n}\n\nconst ProgressBar = ({\n selectable,\n onSelect,\n segments,\n color,\n height,\n name,\n value,\n max,\n animationDuration,\n label,\n}: IProgressBarProps) => {\n const calcWidth = (segment: number): number => {\n let percent = selectable ? segment * 20 : (value / max) * 100;\n\n if (segments) {\n percent = (percent > 0 ? Math.round(percent / 20) : 0) * 20;\n }\n\n if (percent > 100) {\n percent = 100;\n }\n\n return percent;\n };\n\n const [selectedSegment, setSelectedSegment] = React.useState(0);\n const [width, setWidth] = React.useState(calcWidth(0));\n\n const onSegmentClick = (segment) => {\n if (selectable) {\n onSelect(segment);\n setSelectedSegment(segment);\n setWidth(calcWidth(segment));\n }\n };\n\n const onSegmentHover = (segment) => {\n if (selectable) {\n setWidth(calcWidth(segment));\n }\n };\n\n const onProgressLeave = () => {\n if (selectable) {\n setWidth(calcWidth(selectedSegment));\n }\n };\n\n return (\n \n );\n};\n\nexport default ProgressBar;\n","import loadable from '@loadable/component';\n\nexport default loadable(() => import('../SpecialPageComponent'));\n","import{_ as e}from\"./_rollupPluginBabelHelpers-fc5c8eb1.js\";import*as t from\"react\";import{Icon as r}from\"./index.js\";import\"./ThemeContext.js\";import\"styled-components\";var o=function(o){return t.createElement(r,e({},o,{viewBox:\"0 0 57 64\"}),t.createElement(\"path\",{fill:\"currentColor\",d:\"M11.77,32A11.78,11.78,0,1,1,23.54,20.2,11.79,11.79,0,0,1,11.77,32Zm0-17.88a6.1,6.1,0,1,0,6.1,6.1A6.1,6.1,0,0,0,11.77,14.1Z\"}),t.createElement(\"path\",{fill:\"currentColor\",d:\"M44.28,55.57A11.78,11.78,0,1,1,56.05,43.79,11.79,11.79,0,0,1,44.28,55.57Zm0-17.87a6.1,6.1,0,1,0,6.1,6.09A6.1,6.1,0,0,0,44.28,37.7Z\"}),t.createElement(\"rect\",{fill:\"currentColor\",x:\"-8.73\",y:\"29.16\",width:\"73.52\",height:\"5.68\",transform:\"translate(-14.42 29.19) rotate(-45)\"}))};export{o as DiscountIcon};\n//# sourceMappingURL=DiscountIcon.js.map\n","import * as React from 'react';\nimport { MessageDescriptor } from 'react-intl';\n\nimport {\n DiscountIcon,\n TagModel,\n TruckIcon,\n StarIcon,\n ProductTileModel,\n} from '@notino/react-styleguide';\n\nimport messages from '@containers/ProductDetailContainer/messages';\nimport {\n AttributeTypes,\n AttributeType,\n} from '@containers/ProductListing/model';\n\nexport interface ILabelAttribute {\n icon: JSX.Element;\n style: TagModel.Styles;\n tooltipMessage?: MessageDescriptor;\n}\n\nexport enum ClientOnlyAttributeTypes {\n Damaged = 'Damaged',\n Returned = 'Returned',\n}\n\nexport type ILabelAttributes = {\n [key in ProductTileModel.TagAttributes]: ILabelAttribute;\n};\n\n/** Label attributes definition */\nexport const labelAttributes: ILabelAttributes = {\n [AttributeTypes.Action]: {\n style: TagModel.Styles.primary,\n icon: ,\n },\n [AttributeTypes.FreeDelivery]: {\n style: TagModel.Styles.neutral,\n icon: ,\n },\n [AttributeTypes.New]: {\n style: TagModel.Styles.muted,\n icon: ,\n },\n [AttributeTypes.Clearance]: {\n style: TagModel.Styles.primary,\n icon: ,\n },\n [ClientOnlyAttributeTypes.Damaged]: {\n style: TagModel.Styles.muted,\n icon: ,\n tooltipMessage: messages.productDamaged,\n },\n [ClientOnlyAttributeTypes.Returned]: {\n style: TagModel.Styles.muted,\n icon: ,\n tooltipMessage: messages.productReturned,\n },\n};\n\nexport interface ITagsProps {\n attributes?: AttributeType;\n stockAvailability: string;\n small?: boolean;\n multiple?: boolean;\n type?: TagModel.Types;\n icon?: string;\n onClick?: (attribute: ProductTileModel.TagAttributes) => void;\n nowrapLg?: boolean;\n}\n","import { flowRight as compose } from 'lodash';\n\nimport { ProductTileModel } from '@notino/react-styleguide';\n\nimport {\n AttributeTypes,\n MaximumVolumeInPercent,\n AttributeType,\n} from '@containers/ProductListing/model';\n\nimport {\n ILabelAttributes,\n labelAttributes,\n ClientOnlyAttributeTypes,\n ILabelAttribute,\n} from './model';\n\ninterface LabelPair {\n label: Label;\n attributes: ILabelAttribute;\n}\n\ntype Label = AttributeTypes | ClientOnlyAttributeTypes;\n\n/**\n * Returns an object of variant attributes, where keys are attributes (label) and values are its properties.\n * It respects the order according to labelAttributes keys\n */\nexport const getLabelAttrs = (attributes: Label[]): Partial =>\n Object.keys(labelAttributes)\n .map((label) => attributes.find((variantLabel) => variantLabel === label))\n .filter((element) => element !== undefined)\n .reduce((acc, label) => {\n acc[label] = labelAttributes[label];\n return acc;\n }, {});\n\nexport const getPairs = (attrs: Partial): LabelPair[] =>\n Object.keys(attrs).reduce(\n (acc, label) => [...acc, { label, attributes: labelAttributes[label] }],\n []\n );\n\nexport const resolveDamageAttribute = (attributeValue: {\n volumeInPercent: number;\n}): ClientOnlyAttributeTypes.Damaged | ClientOnlyAttributeTypes.Returned => {\n return attributeValue.volumeInPercent === MaximumVolumeInPercent\n ? ClientOnlyAttributeTypes.Damaged\n : ClientOnlyAttributeTypes.Returned;\n};\n\nexport const mapClientOnlyAttributes = (\n label: AttributeTypes,\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n attributeValue: any\n): Label => {\n if (label === AttributeTypes.Damage) {\n return resolveDamageAttribute(attributeValue);\n }\n\n return label;\n};\n\nexport const orderTags = (tags: ProductTileModel.TagAttributes[]) => {\n const tagsOrder = [\n ProductTileModel.TagAttributes.Action,\n ProductTileModel.TagAttributes.Clearance,\n ProductTileModel.TagAttributes.FreeDelivery,\n ];\n\n return [...tags].sort((a, b) => {\n const indexA = tagsOrder.indexOf(a);\n const indexB = tagsOrder.indexOf(b);\n\n if (indexA !== -1 && indexB !== -1) {\n return indexA - indexB;\n } else if (indexA !== -1) {\n return -1;\n } else if (indexB !== -1) {\n return 1;\n }\n return 0;\n });\n};\n\nexport const transformAttributesToTags = (\n attributes: Record = {}\n): ProductTileModel.TagAttributes[] => {\n const tags = Object.keys(attributes)\n .filter((key) => Boolean(attributes[key]))\n .map((key: AttributeTypes) => {\n const label = mapClientOnlyAttributes(key, attributes[key]);\n return ProductTileModel.TagAttributes[label];\n })\n .filter(Boolean);\n\n return orderTags(tags);\n};\n\nconst addClientOnlyAttributes = (attributes: AttributeType): Label[] =>\n Object.keys(attributes).reduce(\n (acc, label) => [\n ...acc,\n mapClientOnlyAttributes(label as AttributeTypes, attributes[label]),\n ],\n []\n );\n\nexport const getRenderableAttributes = compose(\n getLabelAttrs,\n addClientOnlyAttributes\n) as (attributes?: AttributeType) => Partial;\n","import styled from 'styled-components';\n\nexport const TooltipContent = styled.div`\n min-width: 15rem;\n padding: 0.5rem;\n`;\n","import {\n breakpoints,\n Button,\n styled,\n ProductTile,\n theme,\n} from '@notino/react-styleguide';\n\nexport const StyledProductTile = styled(ProductTile)`\n margin-top: 1rem;\n`;\n\nexport const VoucherCode = styled.strong`\n color: ${theme.palette.basic};\n`;\nexport const ButtonWrapper = styled.div`\n margin-top: 1rem;\n display: flex;\n justify-content: center;\n\n & > button,\n form button {\n text-transform: none !important;\n font-weight: normal !important;\n width: auto !important;\n }\n`;\n\nexport const StyledButtonWatchdog = styled(Button)`\n margin-top: 1rem;\n line-height: 1.4;\n padding: 0.4375rem 1rem;\n text-transform: none;\n font-weight: normal;\n white-space: pre-wrap;\n @media (min-width: ${breakpoints.sm}) {\n width: auto;\n }\n`;\n\nexport const CouponIcon = styled.img`\n width: 2.5rem;\n height: 0.5rem;\n z-index: 2;\n`;\n","import * as React from 'react';\nimport { FormattedMessage } from 'react-intl';\n\nimport { ButtonModel } from '@notino/react-styleguide';\nimport { GetItemDataByIdQuery } from '@notino/shared/definitions/types';\n\nimport { AddToCartButton } from '@sharedComponents/AddToCartButton/AddToCartButton';\nimport { Watchdog } from '@sharedComponents/Watchdog';\nimport { watchdogMessages } from '@sharedComponents/Watchdog/watchdogMessages';\nimport { noop } from '@utils/noop';\n\nimport { ButtonWrapper, StyledButtonWatchdog } from '../styled';\n\ninterface IBuyButtonProps {\n buttonElementClassName: string;\n product: GetItemDataByIdQuery['vpProductById'];\n showAddToCartModal: boolean;\n disableRedirect: boolean;\n onProductAdded: (\n product: GetItemDataByIdQuery['vpProductById']\n ) => void | Promise;\n onProductAddFailed?: (\n message: string,\n product: GetItemDataByIdQuery['vpProductById']\n ) => void | Promise;\n onClosingCartModal?: () => void;\n}\n\nexport const BuyButton: React.FC = ({\n buttonElementClassName,\n product,\n showAddToCartModal,\n disableRedirect,\n onProductAdded,\n onClosingCartModal,\n onProductAddFailed = noop,\n}) => {\n return (\n <>\n {product.canBuy && (\n onProductAdded(product)}\n onProductAddFailed={(message) => onProductAddFailed(message, product)}\n onClosingModal={onClosingCartModal}\n />\n )}\n \n \n \n \n \n >\n );\n};\n","import { defineMessages } from 'react-intl';\nexport const messages = defineMessages({\n inStock: {\n id: 'product.item.in.stock',\n defaultMessage: 'skladem',\n },\n descriptionText: {\n id: 'product.item.black.friday.description.text',\n defaultMessage: 's kódem',\n },\n discountAmount: {\n id: 'product.item.discount.highlighted',\n defaultMessage: '-{amount} %',\n },\n});\n","import * as React from 'react';\nimport { FormattedMessage } from 'react-intl';\n\nimport { ProductTile, ProductTileModel } from '@notino/react-styleguide';\nimport { masterUrl } from '@notino/shared/client-utils';\nimport { IStock } from '@notino/shared/definitions/custom-definitions';\n\nimport { VoucherCode } from '@components/Universals/ProductItem/styled';\nimport { useSettings } from '@containers/ProductDetailContainer/hooks/useSettings';\nimport { useIsDualPriceForCroatia } from '@containers/ProductDetailContainer/ProductDetail/hooks/useIsDualPriceForCroatia';\nimport { dispatchTrackingEvent } from '@context/tracking/utils';\nimport { ProductEventWither } from '@helpers/googleTagManager';\nimport { noop } from '@utils/noop';\n\nimport { useGetImageAttributes } from '../../CdnImage/useImageAttributes';\nimport { useProductSpaRedirect } from '../../ClientRedirect/useHandleClientRedirect';\nimport { useFormatPrice } from '../../PriceLabel/useFormattedPrice';\nimport { usePriceConfig } from '../../PriceLabel/usePriceConfig';\nimport { useWishlistActionsFactory } from '../WishlistModifier/useWishlistActions';\n\nimport { BuyButton } from './components';\nimport { messages } from './messages';\nimport { IProductItemProps } from './model';\nimport { useGetTagAttributes } from './useTagAttributes';\nimport { useGetProductIcons } from './utils';\n\n// this character ensures that there is same spacing as if text was present\n// found here https://jkorpela.fi/chars/spaces.html\nconst SPECIAL_IDEOGRAPHIC_SPACE = ' ';\n\ntype UseGetProductItemProps = Pick<\n IProductItemProps,\n | 'useMasterUrl'\n | 'setProductWishlisted'\n | 'GTMEventLabel'\n | 'inSectionOfType'\n | 'sendGTM'\n | 'noPriceIfUnavailable'\n | 'noTags'\n | 'showWishlistButton'\n | 'onProductAdded'\n | 'onProductAddFailed'\n | 'onClosingCartModal'\n | 'showBuyButton'\n | 'showAddToCartModal'\n | 'buttonElementClassName'\n | 'canSpaRedirect'\n | 'wishlistActionLocation'\n>;\n\ntype GetProductProps = Pick<\n IProductItemProps,\n | 'product'\n | 'wishlisted'\n | 'order'\n | 'appendComponent'\n | 'description'\n | 'position'\n>;\n\nexport const useGetProductItemProps = ({\n useMasterUrl = false,\n setProductWishlisted,\n GTMEventLabel,\n inSectionOfType = '',\n sendGTM = false,\n noPriceIfUnavailable,\n noTags,\n showWishlistButton,\n onProductAdded = noop,\n onProductAddFailed = noop,\n onClosingCartModal = noop,\n showBuyButton = false,\n showAddToCartModal = true,\n buttonElementClassName,\n canSpaRedirect = true,\n wishlistActionLocation,\n}: UseGetProductItemProps) => {\n const { data: { currentLocale, currentCurrency } = {} } = usePriceConfig();\n const { Settings: { showUnitPrices } = {} } = useSettings();\n\n const formatPrice = useFormatPrice();\n const {\n isDualPriceForCroatia,\n getLeftSideFormattedPrice,\n getRightSideFormattedPrice,\n leftSideCurrency,\n rightSideCurrency,\n } = useIsDualPriceForCroatia();\n const getTagAttributes = useGetTagAttributes();\n const getProductIcons = useGetProductIcons();\n const getImageAttributes = useGetImageAttributes();\n\n const withProductSpaRedirect = useProductSpaRedirect(\n canSpaRedirect,\n useMasterUrl\n );\n\n const { handleWishlistClick, isWishlistAllowed } = useWishlistActionsFactory({\n onCompleted: setProductWishlisted,\n });\n\n return ({\n product,\n wishlisted = false,\n order,\n appendComponent,\n description,\n position = 0,\n }: GetProductProps): ProductTileModel.IProductTileContentProps => {\n if (!currentLocale || !currentCurrency || !product) {\n return null;\n }\n\n const formattedPriceObj = formatPrice(product.price);\n const formattedUnitPriceObj = formatPrice(product.unitPrice);\n const tagAttributes = getTagAttributes(product.attributes);\n\n const productIcons = getProductIcons(product.attributes);\n\n const imageAttributes = getImageAttributes(\n product.imageUrl,\n 'list',\n `${product.brand?.name} ${product.name}`\n );\n\n const handleTracking = () => {\n if (sendGTM && currentCurrency) {\n dispatchTrackingEvent({\n event: 'select_item',\n product: [\n ProductEventWither()\n .withVpProduct(product)\n .withAdditionalData({\n list_position: position,\n list_name: GTMEventLabel,\n })\n .build(),\n ],\n _clear: true,\n });\n }\n };\n\n const withSpaRedirect = withProductSpaRedirect(product);\n const handleClick = withSpaRedirect(handleTracking);\n\n const productInStock = product.stockAvailability.code !== IStock.outOfStock;\n const showPrice = !noPriceIfUnavailable || product.canBuy;\n\n const voucherDiscount = product.attributes.VoucherDiscount;\n\n function getVoucherDiscountDecoration():\n | 'summerBlackFriday'\n | 'blackFriday'\n | undefined {\n if (voucherDiscount?.tags.includes('summerblackfriday')) {\n return 'summerBlackFriday';\n }\n if (voucherDiscount?.tags?.includes('blackfriday')) {\n return 'blackFriday';\n }\n return undefined;\n }\n\n return {\n state: ProductTileModel.State.product,\n tileProps: {\n 'data-testid': 'product-item',\n 'data-cypress': 'productItem-container',\n },\n product: {\n id: product.id,\n description: description,\n name: product.name,\n subName: product.subName,\n brandName: product.brand?.name ?? SPECIAL_IDEOGRAPHIC_SPACE,\n variantName: product.annotation,\n },\n\n unitPrice: showPrice &&\n showUnitPrices &&\n product.unitPrice && {\n price: {\n price: String(\n formattedUnitPriceObj?.formattedPrice || product.unitPrice?.value\n ),\n currencySymbol: formattedUnitPriceObj?.currencySymbol,\n currencyFormat: formattedUnitPriceObj?.currentLocale?.priceFormat,\n },\n unit: product.unitPrice.unit,\n unitAmount: String(product.unitPrice.perAmount),\n },\n ratings: Boolean(product.reviewOverview?.rating) && {\n inSectionOfType,\n score: product.reviewOverview.rating,\n },\n productIcons,\n tags: productInStock &&\n tagAttributes && {\n attributes: tagAttributes,\n visible: !noTags,\n },\n aboveBuyButton: (\n <>\n {Boolean(product.attributes.BlackFridayOfferDiscount) && (\n \n }\n discountDescription={\n \n }\n />\n )}\n\n {Boolean(voucherDiscount) && (\n \n }\n discountDescription={\n \n \n \n {voucherDiscount.code}\n \n }\n />\n )}\n >\n ),\n appendComponent: (\n <>\n {appendComponent}\n {showBuyButton && (\n \n )}\n >\n ),\n href: useMasterUrl ? masterUrl(product.url) : product.url,\n imageProps: imageAttributes,\n onClick: handleClick,\n order,\n wishlist: {\n wishlisted: wishlisted || product.wishlisted,\n onClick: () =>\n handleWishlistClick({\n productId: product.id,\n wishlisted: wishlisted || product?.wishlisted,\n actionLocation: wishlistActionLocation,\n listName: GTMEventLabel,\n listPosition: position,\n }),\n notActive: !isWishlistAllowed && showWishlistButton,\n },\n price:\n showPrice &&\n (isDualPriceForCroatia\n ? {\n price: getRightSideFormattedPrice(product.price.value),\n currencySymbol: rightSideCurrency,\n currencyFormat: 'v s',\n }\n : {\n price: String(\n formattedPriceObj?.formattedPrice || product?.price.value\n ),\n currencySymbol:\n formattedPriceObj?.currencySymbol || currentCurrency.symbol,\n currencyFormat:\n formattedPriceObj?.currentLocale?.priceFormat ||\n currentLocale.priceFormat,\n }),\n dualPrice: showPrice &&\n isDualPriceForCroatia && {\n price: getLeftSideFormattedPrice(product.price.value),\n currencySymbol: leftSideCurrency,\n currencyFormat: 'v s',\n },\n };\n };\n};\n\nexport const useProductItemProps = ({\n product,\n wishlisted,\n description,\n position,\n appendComponent,\n order,\n ...rest\n}: IProductItemProps) => {\n const getProductItemProps = useGetProductItemProps(rest);\n\n return getProductItemProps({\n product,\n wishlisted,\n description,\n position,\n appendComponent,\n order,\n });\n};\n","import { useIntl } from 'react-intl';\n\nimport { ProductTileModel } from '@notino/react-styleguide';\n\nimport { messages } from '@containers/ProductDetailContainer/ProductDetail/ProductBaseInfo/VoucherDiscount/VoucherDiscountInfo/messages';\nimport { useFeatureFlags } from '@context/launch-darkly/LDProvider';\nimport { useApolloClientHeaders } from '@hooks/useApolloClientHeaders';\n\ninterface Attributes {\n BlackFridayOfferDiscount?: boolean;\n blackFriday?: boolean;\n christmasOffer?: boolean;\n VoucherDiscount?: {\n productDetailOnly?: boolean;\n theme: string | null;\n tags: string[];\n };\n}\n\nfunction transformProductIcons(\n attributes: Attributes,\n newDermoIcon: boolean,\n formatMessage,\n isFranceShop: boolean\n): ProductTileModel.IProductIcon[] {\n return Object.keys(attributes)\n .map((key) => {\n if (key === 'LorealLabel') {\n return { icon: ProductTileModel.SpecialSales.lorealLabel };\n } else if (['BlackFridayOfferDiscount', 'blackFriday'].includes(key)) {\n return {\n icon: ProductTileModel.SpecialSales.blackFriday,\n label: formatMessage(messages.blackFridayLabel),\n };\n } else if (key === 'christmasOffer') {\n return { icon: ProductTileModel.SpecialSales.christmasOffer };\n } else if (key === 'VoucherDiscount') {\n const voucherDiscount = attributes.VoucherDiscount;\n if (voucherDiscount?.tags.includes('summerblackfriday')) {\n return {\n icon: ProductTileModel.SpecialSales.summerBlackFriday,\n label: formatMessage(messages.sbfLabel),\n title: formatMessage(messages.sbfLabel),\n highlightedWordIndex: isFranceShop ? 2 : 0,\n };\n } else if (voucherDiscount?.tags?.includes('blackfriday')) {\n return {\n icon: ProductTileModel.SpecialSales.blackFriday,\n label: formatMessage(messages.sbfLabel),\n };\n }\n } else if (key === 'Dermo') {\n return {\n icon: newDermoIcon\n ? ProductTileModel.SpecialSales.recommendedByDermatologists\n : ProductTileModel.SpecialSales.dermo,\n };\n }\n return null;\n })\n .filter(Boolean);\n}\n\nexport const useGetProductIcons = () => {\n const { formatMessage } = useIntl();\n const { newDermoIcon } = useFeatureFlags();\n const isFranceShop = useApolloClientHeaders().shopId === 'notino.fr';\n\n return (\n attributes: Record\n ): ProductTileModel.IProductIcon[] => {\n if (!attributes) return [];\n\n return transformProductIcons(\n attributes,\n newDermoIcon,\n formatMessage,\n isFranceShop\n );\n };\n};\n","/*\n * ProductBaseInfo messages\n */\nimport { defineMessages } from 'react-intl';\nexport default defineMessages({\n attributeNew: {\n id: 'attribute.new',\n defaultMessage: 'Nové',\n },\n attributeAction: {\n id: 'attribute.action',\n defaultMessage: 'Akce',\n },\n attributeClearance: {\n id: 'attribute.clearance',\n defaultMessage: 'Výprodej',\n },\n attributeFreeDelivery: {\n id: 'attribute.freedelivery',\n defaultMessage: 'Doprava zdarma',\n },\n attributeDamage: {\n id: 'attribute.damage',\n defaultMessage: 'Speciální sleva',\n },\n attributeDamaged: {\n id: 'attribute.damaged',\n defaultMessage: 'Poškozený obal',\n },\n attributeReturned: {\n id: 'attribute.returned',\n defaultMessage: 'Vráceno',\n },\n});\n","import { useIntl } from 'react-intl';\n\nimport { ProductTileModel } from '@notino/react-styleguide';\n\nimport messages from '@components/Tags/messages';\nimport { labelAttributes } from '@components/Tags/model';\nimport { transformAttributesToTags } from '@components/Tags/utils';\n\nexport function useGetTagAttributes() {\n const { formatMessage } = useIntl();\n\n return (\n attributes: Record,\n options: {\n multiple?: boolean;\n customFilter?: (tagAttributes: ProductTileModel.TagAttributes) => boolean;\n } = {}\n ) => {\n const { multiple = false, customFilter } = options;\n\n if (!attributes) {\n return null;\n }\n\n let renderableAttributes: ProductTileModel.TagAttributes[] =\n transformAttributesToTags(attributes);\n\n if (customFilter) {\n renderableAttributes = renderableAttributes.filter(customFilter);\n }\n\n const attrs = renderableAttributes.slice(0, multiple ? Infinity : 1);\n\n return attrs.map((label) => ({\n attribute: label,\n label: formatMessage(messages[`attribute${label}`]),\n ...labelAttributes[label],\n }));\n };\n}\n\nexport function useTagAttributes(\n attributes: Record,\n options: {\n multiple?: boolean;\n customFilter?: (tagAttributes: ProductTileModel.TagAttributes) => boolean;\n } = {}\n) {\n const getTagAttributes = useGetTagAttributes();\n return getTagAttributes(attributes, options);\n}\n","import loadable from '@loadable/component';\n\nexport const Routine = loadable(() => import('../Routine'));\n","import { withTryToInvokeTrackingEvent } from '@utils/exponea-logging';\n\nexport const trackExponeaWishlistUpdate = withTryToInvokeTrackingEvent(\n 'WishlistUpdate',\n (payload: Parameters[0]) => {\n void window.trackingEvents.WishlistUpdate(payload);\n }\n);\n","import { useQuery } from '@apollo/client';\n\nimport { IsWishlistEnabledQuery } from '@notino/shared/definitions/types';\n\nimport isWishlistEnabledQuery from '../queries/isWishlistEnabled.graphql';\n\nexport const useIsWishlistEnabled = () => {\n const { data, loading, error } = useQuery(\n isWishlistEnabledQuery\n );\n\n return !loading && !error && data?.Settings?.isWishlistAllowed;\n};\n","import * as React from 'react';\n\nimport { useMutation } from '@apollo/client';\n\nimport {\n AddToWishlistMutation,\n AddToWishlistMutationVariables,\n RemoveFromWishlistMutation,\n RemoveFromWishlistMutationVariables,\n} from '@notino/shared/definitions/types';\n\nimport getItemDataByIdQuery from '@components/Universals/ProductItem/queries/itemDataById.graphql';\nimport variantFragment from '@containers/ProductDetailContainer/queries/fragVariant.graphql';\nimport { useTrackingContext } from '@context/tracking/TrackingContext';\n\nimport addToWishlistMutation from '../queries/addToWishlist.graphql';\nimport removeFromWishlistMutation from '../queries/removeFromWishlist.graphql';\nimport { trackExponeaWishlistUpdate } from '../tracking/exponea';\nimport {\n handleTrackWishlistOperation,\n WishlistActionLocation,\n} from '../tracking/gtm';\nimport { updateWishlistedCache } from '../updateWishlistedCache';\n\nimport { useIsWishlistEnabled } from './useIsWishlistEnabled';\n\nexport const publishUpdateWishlistCountEvent = () => {\n if (window.wishlistService) {\n window.wishlistService.count();\n } else if (document) {\n // publish a custom event only when the wihlistService is unavailable because\n // when it is available the event is published inside the wishlistService count() method\n const event = new CustomEvent('header:wishlist:overview:update');\n document.dispatchEvent(event);\n }\n};\n\ninterface IUseWishlistActions {\n productId: string;\n wishlisted: boolean;\n onCompleted?: (productId: string, wishlisted: boolean) => void;\n}\n\nexport const useWishlistActionsFactory = ({\n onCompleted,\n}: Pick) => {\n const { getTimeFromInit } = useTrackingContext();\n\n const isWishlistAllowed = useIsWishlistEnabled();\n\n const [add, { loading: addLoading }] = useMutation<\n AddToWishlistMutation,\n AddToWishlistMutationVariables\n >(addToWishlistMutation);\n\n const [remove, { loading: removeLoading }] = useMutation<\n RemoveFromWishlistMutation,\n RemoveFromWishlistMutationVariables\n >(removeFromWishlistMutation);\n\n const loading = addLoading || removeLoading;\n\n const handleWishlistClick = React.useCallback(\n async ({\n productId,\n wishlisted,\n actionLocation,\n trackingPromoLabels = [],\n listName,\n listPosition,\n }: {\n productId: string;\n wishlisted: boolean;\n actionLocation: WishlistActionLocation;\n trackingPromoLabels?: string[];\n listName?: string;\n listPosition?: number;\n }) => {\n const addedToWishlist = !wishlisted;\n const operation = addedToWishlist ? add : remove;\n const { data } = await operation({\n variables: { productId },\n update: (cache) => {\n updateWishlistedCache({\n cache,\n wishlisted: addedToWishlist,\n productId,\n });\n\n const itemDataById = cache.readQuery({\n query: getItemDataByIdQuery,\n variables: { id: productId.toString() },\n })?.vpProductById;\n\n const variantData = cache.readFragment({\n fragment: variantFragment,\n id: `Variant:${productId}`,\n });\n\n const product = itemDataById || variantData;\n\n if (product) {\n handleTrackWishlistOperation({\n addedToWishlist: addedToWishlist,\n product: { ...product, wishlisted: addedToWishlist },\n timing: getTimeFromInit(),\n actionLocation,\n additionalPromoLabels: trackingPromoLabels,\n additionalGtm: {\n list_name: listName,\n list_position: listPosition,\n },\n });\n }\n },\n });\n\n const wishlistProductIds =\n 'WishlistAddProduct' in data\n ? data.WishlistAddProduct.products\n : data.WishlistRemoveProduct.products;\n\n trackExponeaWishlistUpdate({\n action: addedToWishlist ? 'add' : 'remove',\n productId: Number(productId),\n wishlistProductIds: wishlistProductIds.map(Number),\n });\n\n publishUpdateWishlistCountEvent();\n onCompleted?.(productId, addedToWishlist);\n },\n [add, onCompleted, remove, getTimeFromInit]\n );\n\n return React.useMemo(\n () => ({\n handleWishlistClick,\n isWishlistAllowed,\n loading,\n }),\n [handleWishlistClick, isWishlistAllowed, loading]\n );\n};\n\nexport const useWishlistActions = ({\n productId,\n wishlisted,\n onCompleted,\n}: IUseWishlistActions) => {\n const { handleWishlistClick, isWishlistAllowed, loading } =\n useWishlistActionsFactory({ onCompleted });\n\n return React.useMemo(\n () => ({\n handleWishlistClick: (\n actionLocation: WishlistActionLocation,\n trackingPromoLabels: string[] = []\n ) => {\n return handleWishlistClick({\n productId,\n wishlisted,\n actionLocation,\n trackingPromoLabels,\n });\n },\n isWishlistAllowed,\n loading,\n }),\n [handleWishlistClick, isWishlistAllowed, loading, productId, wishlisted]\n );\n};\n","import { GetItemDataByIdQuery as IgetItemDataById } from '@notino/shared/definitions/types';\n\nimport getItemDataByIdQuery from '../ProductItem/queries/itemDataById.graphql';\n\nimport { ICallbackParameters } from './model';\n\nexport const updateWishlistedCache = ({\n cache,\n wishlisted,\n productId,\n}: ICallbackParameters & { productId: string }) => {\n cache.updateQuery(\n {\n query: getItemDataByIdQuery,\n variables: { id: productId.toString() },\n },\n (prev) => ({\n vpProductById: {\n ...prev?.vpProductById,\n wishlisted,\n },\n })\n );\n};\n","import { GetProductsByIdBatchedQuery } from '@notino/shared/definitions/types';\nimport { Product } from '@notino/web-tracking/dist/types/package/ga4/events/products';\n\nimport { dispatchTrackingEvent } from '@context/tracking/utils';\nimport { ProductEventWither } from '@helpers/googleTagManager';\n\nexport type WishlistActionLocation = 'product_detail' | 'wishlist' | 'list';\n\ninterface HandleTrackWishlistOperationParams {\n addedToWishlist: boolean;\n product: GetProductsByIdBatchedQuery['vpProductByIds'][number];\n timing: number;\n actionLocation: WishlistActionLocation;\n additionalPromoLabels?: string[];\n additionalGtm?: Partial;\n}\n\nexport const handleTrackWishlistOperation = ({\n addedToWishlist,\n product,\n timing,\n actionLocation,\n additionalGtm = {},\n additionalPromoLabels = [],\n}: HandleTrackWishlistOperationParams) => {\n const additionalData: Partial = Object.entries(additionalGtm).reduce(\n (acc, [key, value]) =>\n value === undefined ? acc : { ...acc, [key]: value },\n {}\n );\n\n if (addedToWishlist) {\n dispatchTrackingEvent({\n event: 'add_to_wishlist',\n wishlist: {\n action: 'product_added_to_wishlist',\n interaction: 'click_add',\n type: actionLocation,\n timing,\n products: [\n ProductEventWither()\n .withVpProduct(product)\n .withAdditionalPromoLabels(additionalPromoLabels)\n .withAdditionalData(additionalData)\n .build(),\n ],\n },\n _clear: true,\n });\n } else {\n dispatchTrackingEvent({\n event: 'remove_from_wishlist',\n wishlist: {\n action: 'product_removed_from_wishlist',\n interaction: 'click_remove',\n type: actionLocation,\n timing,\n products: [\n ProductEventWither()\n .withVpProduct(product)\n .withAdditionalData(additionalData)\n .build(),\n ],\n },\n _clear: true,\n });\n }\n};\n","interface IMissData {\n missId: number;\n startTime: string;\n year: number;\n}\n\nexport const createCarousel = (\n id: string,\n data: { ids: string[] }\n): Carousel => ({\n id,\n type: ComponentType.Carousel,\n data,\n});\n\nexport const createMiss = (id: string, data: IMissData): Miss => ({\n id,\n type: ComponentType.Miss,\n data,\n});\n\nexport const createUniversalPage = (\n type: ComponentType,\n id: string,\n data: { ids: string[] }\n): UniversalPage => ({\n id,\n type,\n data,\n});\n\nexport enum ComponentType {\n Carousel = 'carousel',\n Wishlist = 'wishlist',\n Miss = 'miss',\n AdventCalendar = 'adventCalendar',\n GiftWrapper = 'giftwrapper',\n TryAndBuy = 'tryAndBuy',\n ShadeFinder = 'shadeFinder',\n}\n\ninterface UniversalPage {\n type: ComponentType;\n id: string;\n data: { ids: string[] };\n}\n\nexport type Components = Carousel | Miss | UniversalPage;\n\nexport interface Carousel {\n type: ComponentType.Carousel;\n id: string;\n data: { ids: string[] };\n}\n\nexport interface Miss {\n type: ComponentType.Miss;\n id: string;\n data: IMissData;\n}\n\nexport const createComponents = (url: string): Components[] => {\n const qs = decodeURIComponent(url).replace('/render', '');\n const reqComponents = new URLSearchParams(qs).get('components');\n\n const data: Array<{\n type: ComponentType;\n id: string;\n data: { ids: string[] };\n }> = JSON.parse(reqComponents);\n\n return data\n .filter((component) =>\n Object.values(ComponentType).includes(component.type)\n )\n .map((component) => {\n switch (component.type) {\n case ComponentType.Carousel: {\n return createCarousel(component.id, component.data);\n }\n case ComponentType.Miss: {\n return createMiss(\n component.id,\n component.data as unknown as IMissData\n );\n }\n // as all unsupported types are filtered, we can default to create universal page\n default: {\n return createUniversalPage(\n component.type,\n component.id,\n component.data\n );\n }\n }\n });\n};\n","import styled, { css } from 'styled-components';\n\nconst ContentWrapper = styled.div<{ newPdDesignEnabled?: boolean }>`\n max-width: 64.6875rem;\n margin: 0 auto;\n padding: 0;\n width: 100%;\n\n ${({ newPdDesignEnabled }) =>\n newPdDesignEnabled &&\n css`\n max-width: 80rem;\n @media (min-width: 105rem) {\n max-width: 100rem;\n }\n `}\n`;\n\nexport default ContentWrapper;\n","import styled from 'styled-components';\n\nimport { breakpoints, css } from '@notino/react-styleguide';\n\nconst TabWrapper = styled.div<{ newPdDesignEnabled: boolean }>`\n ${({ newPdDesignEnabled }) =>\n !newPdDesignEnabled &&\n css`\n margin: 0 -0.9375rem;\n `}\n\n @media (min-width: ${breakpoints.md}) {\n margin: 0;\n }\n`;\n\nexport default TabWrapper;\n","import styled from 'styled-components';\n\nconst ViewPort = styled.main`\n padding-bottom: 3.125rem;\n`;\n\nexport default ViewPort;\n","export const zipRegexes = {\n 'notino.cz': {\n pattern: /\\d{3} \\d{2}$/,\n mask: '999 99',\n },\n 'notino.sk': {\n pattern: /\\d{3} \\d{2}$/,\n mask: '999 99',\n },\n 'notino.pl': {\n pattern: /\\d{2}-\\d{3}$/,\n mask: '99-999',\n },\n 'notino.de': {\n pattern: /^\\d{5}$/,\n mask: '99999',\n },\n 'notino.at': {\n pattern: /^\\d{4}$/,\n mask: '9999',\n },\n 'notino.hu': {\n pattern: /^\\d{4}$/,\n mask: '9999',\n },\n 'notino.es': {\n pattern: /^\\d{5}$/,\n mask: '99999',\n },\n 'notino.pt': {\n pattern: /^\\d{4}$/,\n mask: '9999',\n },\n 'notino.co.uk': {\n pattern:\n /^(([A-Z]{1,2}[0-9][A-Z0-9]?|ASCN|STHL|TDCU|BBND|[BFS]IQQ|PCRN|TKCA) ?[0-9][A-Z]{2}|BFPO ?[0-9]{1,4}|(KY[0-9]|MSR|VG|AI)[ -]?[0-9]{4}|[A-Z]{2} ?[0-9]{2}|GE ?CX|GIR ?0A{2}|SAN ?TA1)$/,\n // as UK post code is too complex we do not provide mask\n mask: null,\n },\n 'notino.fr': {\n pattern: /^\\d{5}$/,\n mask: '99999',\n },\n 'notino.it': {\n pattern: /^\\d{5}$/,\n mask: '99999',\n },\n 'notino.nl': {\n pattern: /^[1-9][0-9]{3} ?(?!sa|sd|ss)[a-z|A-Z]{2}$/i,\n mask: '9999 aa',\n },\n 'notino.be': {\n pattern: /^\\d{4}$/,\n mask: '9999',\n },\n 'notino.hr': {\n pattern: /^\\d{5}$/,\n mask: '99999',\n },\n 'notino.si': {\n pattern: /^(\\d{4})$/,\n mask: '9999',\n },\n 'notino.dk': {\n pattern: /^\\d{4}$/,\n mask: '9999',\n },\n 'notino.fi': {\n pattern: /^\\d{5}$/,\n mask: '99999',\n },\n 'notino.gr': {\n pattern: /\\d{3} \\d{2}$/,\n mask: '999 99',\n },\n 'notino.bg': {\n pattern: /^\\d{4}$/,\n mask: '9999',\n },\n 'notino.ua': {\n pattern: /^\\d{5}$/,\n mask: '99999',\n },\n 'notino.se': {\n pattern: /\\d{3} \\d{2}$/,\n mask: '999 99',\n },\n 'notino.ro': {\n pattern: /^\\d{6}$/,\n mask: '999999',\n },\n 'notino.ch': {\n pattern: /^\\d{4}$/,\n mask: '9999',\n },\n 'notino.ru': {\n pattern: /^\\d{6}$/,\n mask: '999999',\n },\n};\nexport const LOGIN_URL = '/myaccount.asp';\n","import loadable from '@loadable/component';\n\nexport const AdminPanel = loadable(() => import('../AdminPanel'));\n","import loadable from '@loadable/component';\n\nexport default loadable(() => import('../AdventCalendar'));\n","export interface IRoute {\n component: string;\n path?: string;\n exact?: boolean;\n rolesOnly?: Roles[];\n}\n\ninterface IUserInfo {\n id: string;\n firstName: string;\n lastName: string;\n lastName2: string;\n email: string;\n}\n\n// watch out here, user needs to be single level object for the purposes of error reporting (see src/app.ts)\nexport interface IUser {\n role: string;\n id?: string;\n token?: string;\n cartId?: string;\n info?: IUserInfo;\n}\n\nexport interface ISettings {\n useNewAddToCart: boolean;\n imageDomainPath: string;\n addToCartSettings: {\n buttonAllowed: string;\n buttonAllowedForAll: boolean;\n modalAllowed: string;\n isNewShoppingCartEndpointEnabled: boolean;\n };\n engravingSettings: {\n allowed: string;\n };\n routineSettings: {\n allowed: string;\n };\n routes: IRoute[];\n isWishlistAllowed: boolean;\n isTryItFirstEnabled: boolean;\n deliveriesDisplayMode?: string;\n}\n\nexport interface ICurrency {\n id: number;\n code: string;\n symbol: string;\n}\n\nexport interface ILocale {\n id: number;\n tag: string;\n currency: string;\n priceDecimalPlaces: number;\n priceFormat: string; // values: 'vs', 'v s', 'sv', 's v' where 'v' stands for value, 's' for symbol\n}\n\nexport enum Roles {\n ANONYMOUS = 'Anonymous',\n CUSTOMER = 'Customer',\n SUPERUSER = 'Superuser',\n ADMIN = 'Admin',\n}\n\nexport enum EShop {\n NOTINO_CZ = 'notino.cz',\n NOTINO_SK = 'notino.sk',\n NOTINO_PL = 'notino.pl',\n NOTINO_DE = 'notino.de',\n NOTINO_AT = 'notino.at',\n NOTINO_HU = 'notino.hu',\n NOTINO_ES = 'notino.es',\n NOTINO_PT = 'notino.pt',\n NOTINO_COM = 'notino.com',\n NOTINO_CO_UK = 'notino.co.uk',\n NOTINO_FR = 'notino.fr',\n NOTINO_IT = 'notino.it',\n NOTINO_NL = 'notino.nl',\n NOTINO_BE = 'notino.be',\n NOTINO_HR = 'notino.hr',\n NOTINO_SE = 'notino.se',\n NOTINO_DK = 'notino.dk',\n NOTINO_FI = 'notino.fi',\n NOTINO_GR = 'notino.gr',\n NOTINO_RO = 'notino.ro',\n NOTINO_SI = 'notino.si',\n NOTINO_UA = 'notino.ua',\n NOTINO_BG = 'notino.bg',\n}\n\nexport const RoleGroups = {\n KNOWN: [Roles.ADMIN, Roles.CUSTOMER, Roles.SUPERUSER],\n};\n\nexport type Validator = (\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n value: any,\n allValues?: Record\n) => string | { key: string; values: Record };\n\nexport interface IValidationDictionary {\n [key: string]: IValidation;\n}\n\nexport interface IValidation {\n required?: boolean;\n maxLength?: number;\n regex?: {\n pattern: string;\n modifier?: string;\n errMsg?: string;\n };\n}\n\nexport type IRegexValidatorFactory = (\n pattern: string,\n modifier?: string,\n error?: string\n) => Validator;\n\nexport enum RequestState {\n DEFAULT = 'default',\n DONE = 'done',\n IN_PROGRESS = 'in_progress',\n RETRYING = 'retrying',\n FAILED = 'failed',\n}\n\nexport type RequestUser = RequestContext & {\n xUserToken?: string;\n jwtToken?: string;\n userName?: string;\n};\n\n/**\n * Add shopId model to global types\n */\ndeclare global {\n namespace Express {\n // tslint:disable-next-line:interface-name\n interface Request {\n user?: RequestUser;\n }\n }\n\n // tslint:disable-next-line:interface-name\n interface RequestContext {\n shopId: EShop;\n role: Roles;\n groupId?: string;\n userId?: string;\n email?: string;\n givenName?: string;\n userName?: string;\n cartId?: string;\n comparisonIds?: string;\n authorization?: string;\n grd: string;\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n jwtPayload?: any;\n }\n\n // tslint:disable-next-line:interface-name\n interface GraphQLRequestContext {\n shopId?: EShop;\n }\n}\n","import { withTryToInvokeTrackingEvent } from '@utils/exponea-logging';\n\nexport const runExponeaFunctions = () => {\n if (typeof window.runExponeaFunctions === 'function') {\n window.runExponeaFunctions();\n } else {\n window.addEventListener('load', runExponeaAndRemoveListener);\n }\n};\n\nconst runExponeaAndRemoveListener = () => {\n if (typeof window.runExponeaFunctions === 'function') {\n window.runExponeaFunctions();\n }\n window.removeEventListener('load', runExponeaAndRemoveListener);\n};\n\nexport const runTrackingEventViewItem = withTryToInvokeTrackingEvent(\n 'ViewItem',\n (payload: Parameters[0]) => {\n void window.trackingEvents.ViewItem(payload);\n }\n);\n","import { Validator } from '@containers/App/model';\n\nimport { IRegexValidatorFactory, IValidation } from './model';\n\nconst composeValidators = (validators) => (value) =>\n validators.reduce((error, validator) => error || validator(value), undefined);\n\nexport const required: Validator = (value) => (value ? undefined : 'Required');\n\nconst maxLengthFactory = (maxLength: number, error = 'MaxLength') => {\n return (value) => {\n return value?.length > maxLength ? error : undefined;\n };\n};\n\nconst regexFactory: IRegexValidatorFactory = (\n pattern,\n modifier,\n error = 'Invalid'\n) => {\n const regex = new RegExp(pattern, modifier);\n return (value) => {\n const testValue = value ? value : '';\n return regex.test(testValue) ? undefined : error;\n };\n};\n\nconst mapValidator = (validation: IValidation): Validator => {\n const validators = [];\n if (validation.required) {\n validators.push(required);\n }\n if (validation.maxLength) {\n validators.push(maxLengthFactory(validation.maxLength));\n }\n if (validation.regex) {\n validators.push(\n regexFactory(\n validation.regex.pattern,\n validation.regex.modifier,\n validation.regex.errMsg\n )\n );\n }\n if (validators.length === 1) {\n return validators[0];\n }\n return composeValidators(validators);\n};\n\nexport const validatorFactory = >(\n validations: T\n): Record =>\n Object.keys(validations).reduce((result, key) => {\n result[key as keyof T] = mapValidator(validations[key]);\n return result;\n }, {} as Record);\n","import loadable from '@loadable/component';\n\nexport default loadable(() => import('../Home'));\n","import { PathTemplate } from '@notino/shared/definitions/types';\n\nimport { ComponentType } from '@components/WebView/utils';\n\n// needed for wishlist -> pd spa redirect\nconst pdSkeletonPrefixes = ['pd.about', 'pd.code', 'pd.availability'];\n\nexport const prefixes = {\n [PathTemplate.ProductDetailSimple]: [\n 'pd',\n 'attribute',\n 'universals',\n 'product',\n 'brand',\n 'reviews',\n 'rating',\n 'order',\n 'relatedProducts',\n 'global',\n 'engraving',\n 'try.it.first',\n 'watchdog',\n 'modiface',\n ],\n [PathTemplate.Error]: ['pd'],\n [PathTemplate.Tryandbuy]: ['tnb'],\n [PathTemplate.Wishlist]: [\n 'wishlist',\n 'universals',\n 'attribute',\n 'reviews',\n 'product',\n 'pd.summer.black.friday',\n 'pd.black.friday',\n 'watchdog',\n 'global',\n ...pdSkeletonPrefixes,\n ],\n // We use component type for development purposes\n [ComponentType.AdventCalendar]: ['adventcalendar'],\n [ComponentType.Miss]: ['miss'],\n [ComponentType.GiftWrapper]: ['gifts'],\n [ComponentType.TryAndBuy]: ['tryandbuy', 'tnb', 'global'],\n};\n","import loadable from '@loadable/component';\n\nexport default loadable(() => import('../Miss'));\n","import styled, { css } from 'styled-components';\n\nimport { breakpoints } from '@notino/react-styleguide';\n\nexport const Margin = styled.div<{ newDesign: boolean }>`\n ${({ newDesign }) =>\n newDesign &&\n css`\n margin-bottom: -2rem;\n @media (min-width: ${breakpoints.lg}) {\n margin-top: -1rem;\n margin-bottom: 4rem;\n }\n `}\n`;\n","import * as React from 'react';\nimport { useInView } from 'react-intersection-observer';\n\nimport { useQuery } from '@apollo/client';\nimport { map } from 'lodash';\n\nimport { IArticle } from '@notino/react-styleguide';\nimport {\n GetProductViewQuery,\n GetBlogArticlesQuery,\n GetBlogArticlesQueryVariables,\n} from '@notino/shared/definitions/types';\n\nimport messages from '@containers/ProductDetailContainer/messages';\nimport { useFeatureFlags } from '@context/launch-darkly/LDProvider';\nimport { useTrackingContext } from '@context/tracking/TrackingContext';\nimport { dispatchTrackingEvent } from '@context/tracking/utils';\nimport loadable from '@loadable/component';\n\nimport { useNewPdDesignEnabled } from '../hooks/useNewPdDesignEnabled';\nimport { SectionContainer } from '../SectionContainer';\n\nimport { Margin } from './index.styles';\nimport getBlogArticlesQuery from './queries/getBlogArticles.graphql';\n\nconst LoadableArticlesSlider = loadable(\n () => import('./LoadableArticlesSlider')\n);\n\ntype Props = Pick<\n GetProductViewQuery['productDetailByMasterId'][number],\n 'categorization'\n> & {\n brandId: number;\n masterProductCode: string;\n allProductCodes: string[];\n};\n\nexport const BlogArticles = ({\n categorization: { kinds, categories, subCategories, types },\n brandId,\n masterProductCode,\n allProductCodes,\n}: Props) => {\n const { newBlogEndpoint } = useFeatureFlags();\n const { getTimeFromInit } = useTrackingContext();\n const newDesign = useNewPdDesignEnabled();\n\n const [ref, isInView] = useInView({\n triggerOnce: true,\n rootMargin: '50px',\n });\n\n const { data, error } = useQuery<\n GetBlogArticlesQuery,\n GetBlogArticlesQueryVariables\n >(getBlogArticlesQuery, {\n skip: !isInView,\n variables: {\n variables: {\n kinds: map(kinds, 'id'),\n types: map(types, 'id'),\n categories: map(categories, 'id'),\n subCategories: map(subCategories, 'id'),\n brandId: brandId,\n masterProductCode: masterProductCode,\n useNewEndpoint: newBlogEndpoint,\n productCodes: allProductCodes,\n },\n },\n });\n\n const trackClick = (article: IArticle) => {\n dispatchTrackingEvent({\n event: 'article_click',\n _clear: true,\n blog: {\n timing: getTimeFromInit(),\n name: 'blog_tile',\n action: 'click_on_blog_tile',\n interaction: 'click',\n type: 'blog_tile',\n link_path: article.url,\n unified_link: articles.find((x) => x.url === article.url)?.hreflangId,\n location: 'product_detail',\n position: `${articles.map((a) => a.url).indexOf(article.url) + 1}`,\n },\n });\n };\n\n const articles = data?.getArticles?.articles || [];\n\n return (\n \n {!error && articles.length > 0 && (\n \n \n ({\n ...a,\n image: { alt: a.image.alt, src: a.image.url },\n }))}\n onClick={(_, article) => trackClick(article)}\n />\n \n \n )}\n
\n );\n};\n","import{c as e}from\"./createSvgIcon-89a7bcb8.js\";import\"react\";import\"./index.js\";import\"./_rollupPluginBabelHelpers-fc5c8eb1.js\";import\"./ThemeContext.js\";import\"styled-components\";var r=function(r){return e(\"M32,0A32,32,0,1,0,64,32,32,32,0,0,0,32,0ZM46.39,47.74a9,9,0,0,1-9.25-2.8,33.05,33.05,0,0,1-2.55-3.28L24.81,28.09V47.74H17.61V16.26a9.06,9.06,0,0,1,9.25,2.8,33.05,33.05,0,0,1,2.55,3.28l9.78,13.57V16.26h7.19Z\",r)};export{r as HomeIcon};\n//# sourceMappingURL=HomeIcon.js.map\n","import{a as e,_ as n}from\"./_rollupPluginBabelHelpers-fc5c8eb1.js\";import*as i from\"react\";import{Colors as t}from\"./Colors.js\";import{ChevronRightIcon as r}from\"./ChevronRightIcon.js\";import{HomeIcon as o}from\"./HomeIcon.js\";import{SkeletonElement as a}from\"./SkeletonElement.js\";import l from\"styled-components\";import{breakpoints as m}from\"./breakpoints.js\";import\"./index.js\";import\"./ThemeContext.js\";import\"./createSvgIcon-89a7bcb8.js\";import\"./theme.js\";var c=l.div.withConfig({displayName:\"styled__BreadcrumbWrapper\",componentId:\"sc-caahyp-0\"})([\"display:flex;align-items:center;flex-direction:\",\";overflow:auto;-ms-overflow-style:none;scrollbar-width:none;&::-webkit-scrollbar{display:none;}\"],(function(e){return e.alignToRight?\"row-reverse\":\"row\"})),d=l.a.withConfig({displayName:\"styled__HomeLink\",componentId:\"sc-caahyp-1\"})([\"text-decoration:none;vertical-align:inherit;position:relative;cursor:pointer;display:none;@media (min-width:\",\"){display:flex;align-items:center;margin-right:0.4rem;}\"],m.md),s=l.span.withConfig({displayName:\"styled__LinkSeparator\",componentId:\"sc-caahyp-2\"})([\"margin:0 0.4rem;color:\",\";display:\",\";@media (min-width:\",\"){display:\",\";margin:0 0.3rem;}\"],(function(e){return e.theme.palette.neutral}),(function(e){var n=e.displayMobile,i=e.hide;return n&&!i?\"inline\":\"none\"}),m.md,(function(e){return e.hide?\"none\":\"inline\"})),p=l.a.withConfig({displayName:\"styled__BreadcrumbLink\",componentId:\"sc-caahyp-3\"})([\"cursor:pointer;text-decoration:none;white-space:nowrap;color:\",\";&:hover{text-decoration:underline;}@media (min-width:\",\"){margin:0 0.4rem;}\"],(function(e){return e.theme.palette.neutralDarker}),m.md),h=l.span.withConfig({displayName:\"styled__Text\",componentId:\"sc-caahyp-4\"})([\"white-space:nowrap;color:\",\";@media (min-width:\",\"){margin:0 0.4rem;}\"],(function(e){return e.theme.palette.neutralDarker}),m.md),u=l.div.withConfig({displayName:\"styled__LinksWrapper\",componentId:\"sc-caahyp-5\"})([\"display:flex;align-items:center;justify-content:flex-start;@media (min-width:\",\"){display:block;}\"],m.md),f=[\"items\",\"homeLinkTitle\",\"alignToRight\",\"separator\",\"renderLink\",\"handleNavigation\",\"onItemClick\"],k=[\"onClick\",\"name\",\"link\",\"index\"],g={slash:\"/\",chevron:i.createElement(r,{width:\"0.7em\",height:\"0.7em\",color:t.neutralDarker})},y=function r(a){var l=a.items,m=a.homeLinkTitle,p=a.alignToRight,h=a.separator,k=void 0===h?\"slash\":h,y=a.renderLink,w=void 0===y?function(e){return i.createElement(r.Link,e)}:y,v=a.handleNavigation,x=a.onItemClick,C=e(a,f),b=function(e,n){var i=n.index,t=n.link,r=n.name;null==x||x({index:i,link:t,name:r}),v&&(e.preventDefault(),v({link:t,name:r}))};return(null==l?void 0:l.length)>0&&i.createElement(c,n({alignToRight:p},C),i.createElement(d,{\"data-order\":0,href:\"/\",title:m,onClick:function(e){return b(e,{index:0,link:\"/\",name:m})}},i.createElement(o,{color:t.neutralDarker,width:\"1em\",height:\"1em\"})),i.createElement(s,{hide:!1,displayMobile:!1,role:\"presentation\"},g[k]),i.createElement(u,null,l.map((function(e,n){var t=e.link,r=e.name;return i.createElement(i.Fragment,{key:t},i.createElement(s,{displayMobile:!0,hide:0===n,role:\"presentation\"},g[k]),w({onClick:b,index:n+1,link:t,name:r}))}))))};y.Skeleton=function(){return i.createElement(c,null,i.createElement(a,{height:1,width:15}))},y.Link=function(t){var r=t.onClick,o=t.name,a=t.link,l=t.index,m=e(t,k);return a?i.createElement(p,n({onClick:function(e){return r(e,{index:l,link:a.replace(window.location.origin,\"\"),name:o})},href:a},m),o):i.createElement(h,m,o)};export{y as Breadcrumb};\n//# sourceMappingURL=Breadcrumb.js.map\n","import { ListItem } from 'schema-dts';\n\nimport { TrackingAttributes } from '@context/tracking/types';\nimport { dispatchTrackingEvent } from '@context/tracking/utils';\n\nimport { IBreadcrumb } from './model';\n\nconst prepareListItem = (item: IBreadcrumb, index: number): ListItem => ({\n '@type': 'ListItem',\n position: index + 1,\n item: {\n '@id': item.link,\n name: item.name,\n },\n});\n\nexport const prepareItemListElement = (items: IBreadcrumb[]) =>\n items.map(prepareListItem);\n\ntype BreadcrumbClickTrackingEvent = TrackingAttributes<{\n index: 0 | 1 | 2 | 3 | 4;\n name: string;\n link: string;\n text: string[];\n}>;\n\nexport const trackBreadcrumbClick = ({\n name,\n link,\n index,\n variant,\n timing,\n text,\n}: BreadcrumbClickTrackingEvent) => {\n dispatchTrackingEvent({\n event: 'breadcrumbs_click',\n breadcrumbs: {\n timing,\n type: `level_${index}`,\n name,\n path_query: link,\n interaction: 'click',\n variant,\n action: 'breadcrumbs_used',\n text,\n },\n _clear: true,\n });\n};\n","import * as React from 'react';\nimport { useIntl } from 'react-intl';\nimport { JsonLd } from 'react-schemaorg';\n\nimport { BreadcrumbList } from 'schema-dts';\n\nimport messages from '@containers/ProductDetailContainer/messages';\n\nimport { IBreadcrumb } from './model';\nimport { prepareItemListElement } from './utils';\n\ninterface IBreadcrumbsJsonLdProps {\n items: IBreadcrumb[];\n}\n\nexport const BreadcrumbsJsonLd = ({ items }: IBreadcrumbsJsonLdProps) => {\n const { formatMessage } = useIntl();\n\n return (\n \n item={{\n '@context': 'https://schema.org',\n '@type': 'BreadcrumbList',\n itemListElement: prepareItemListElement([\n { link: '/', name: formatMessage(messages.mainPage) },\n ...items,\n ]),\n }}\n />\n );\n};\n","import * as React from 'react';\nimport { useIntl } from 'react-intl';\n\nimport { Breadcrumb as BreadcrumbSG } from '@notino/react-styleguide';\n\nimport { useTrackingContext } from '@context/tracking/TrackingContext';\n\nimport messages from '../../messages';\nimport { useNewPdDesignEnabled } from '../hooks/useNewPdDesignEnabled';\n\nimport { BreadcrumbsJsonLd } from './BreadcrumbsJsonLd';\nimport { IBreadcrumb } from './model';\nimport { BreadcrumbWrapper } from './styled';\nimport { trackBreadcrumbClick } from './utils';\n\ninterface IBreadcrumbProps {\n items: IBreadcrumb[];\n alignToRight?: boolean;\n renderJsonLd?: boolean;\n}\n\nexport const Breadcrumb = ({\n items,\n alignToRight = true,\n renderJsonLd = true,\n}: IBreadcrumbProps) => {\n const { getTimeFromInit, getVariant } = useTrackingContext();\n const { formatMessage } = useIntl();\n const newPdDesign = useNewPdDesignEnabled();\n\n const handleTrackClick = ({ index, link, name }) => {\n trackBreadcrumbClick({\n index,\n link,\n name,\n timing: getTimeFromInit(),\n variant: getVariant(),\n text: items.map((i) => i.name),\n });\n };\n\n return (\n items?.length > 0 && (\n \n 4}\n items={items}\n homeLinkTitle={formatMessage(messages.mainPage)}\n />\n {renderJsonLd && }\n \n )\n );\n};\n","import styled, { css } from 'styled-components';\n\nimport { breakpoints, theme } from '@notino/react-styleguide';\n\nexport const BreadcrumbWrapper = styled.div<{ newPdDesign?: boolean }>`\n max-width: 64.6875rem;\n font-size: 0.75rem;\n padding: 1rem 0 0 0;\n display: block;\n\n ${({ newPdDesign }) =>\n newPdDesign\n ? css`\n ${theme.typography.labelRegular400}\n font-size: 0.75rem;\n padding: 1rem 0 1.5rem;\n\n @media (min-width: ${breakpoints.lg}) {\n padding: 1.25rem 0 1.5rem;\n }\n `\n : css`\n @media (min-width: ${breakpoints.md}) {\n padding: 1rem 0 1rem;\n }\n `}\n`;\n","import styled, { css } from 'styled-components';\n\nimport { breakpoints, Heading, theme } from '@notino/react-styleguide';\nexport const StyledHeader = styled.div<{\n newDesign: boolean;\n}>`\n ${({ newDesign }) =>\n !newDesign\n ? css`\n @media (min-width: ${breakpoints.md}) {\n width: 58.3333%;\n }\n `\n : css`\n @media (min-width: ${breakpoints.md}) {\n width: 100%;\n }\n `} }\n`;\n\nexport const H1 = styled(Heading.H1)<{ newDesign: boolean }>`\n font-size: 1.125rem !important;\n line-height: 2rem !important;\n text-align: left;\n\n @media (min-width: ${breakpoints.sm}) {\n font-size: 1.5rem !important;\n }\n\n ${({ newDesign }) =>\n newDesign\n ? css`\n margin: 0 !important;\n `\n : css`\n margin: 1rem 0 0 !important;\n @media (min-width: ${breakpoints.md}) {\n margin: 0 !important;\n }\n `}\n`;\n\nexport const BrandExtraLine = styled.a<{\n newDesign: boolean;\n}>`\n margin-right: 0.3rem;\n\n &:hover {\n text-decoration: underline;\n }\n\n ${({ newDesign }) =>\n newDesign\n ? css`\n margin-right: 0.25rem;\n color: ${theme.palette.neutralDarker};\n font-size: 1rem;\n `\n : css`\n color: ${theme.palette.basic};\n font-size: 1.125rem;\n @media (min-width: ${breakpoints.sm}) {\n font-size: 1.5rem;\n }\n @media (min-width: ${breakpoints.md}) {\n font-size: 1rem;\n }\n `}\n`;\n\nexport const ProductNameWrapper = styled.span<{\n newDesign: boolean;\n}>`\n display: flex;\n flex-direction: row;\n flex-wrap: wrap;\n\n ${({ newDesign }) =>\n !newDesign\n ? css`\n @media (min-width: ${breakpoints.md}) {\n flex-direction: column;\n }\n `\n : css`\n align-items: center;\n\n @media (min-width: ${breakpoints.md}) {\n flex-direction: row;\n }\n `}\n`;\n\nexport const Span = styled.span<{ newDesign: boolean }>`\n font-weight: bold;\n\n ${({ newDesign }) =>\n newDesign\n ? css`\n display: block;\n font-size: 1rem;\n margin-top: 0rem;\n line-height: 1.5rem;\n `\n : css`\n display: inline;\n font-size: 1.125rem;\n\n @media (min-width: ${breakpoints.sm}) {\n font-size: 1.5rem;\n }\n `}\n`;\n\nexport const Description = styled.span<{ newDesign: boolean }>`\n ${({ newDesign }) =>\n newDesign\n ? css`\n ${theme.typography.bodyRegular};\n color: ${theme.palette.neutralDarker};\n margin-bottom: 0.5rem;\n display: block;\n\n @media (min-width: ${breakpoints.md}) {\n margin-top: 0.25rem;\n }\n `\n : css`\n font-size: 0.875rem;\n font-weight: 300;\n margin-top: 0.5rem;\n margin-bottom: 0.5rem;\n line-height: normal;\n display: block;\n\n @media (min-width: ${breakpoints.sm}) {\n font-size: 1.1rem;\n }\n `}\n`;\n\nexport const SubName = styled.span`\n display: block;\n font-size: 0.875rem;\n font-weight: 300;\n\n @media (min-width: ${breakpoints.sm}) {\n line-height: 2rem;\n font-size: 1.1rem;\n }\n`;\n\nexport const ReviewsAnchor = styled.a`\n cursor: pointer;\n text-decoration: none;\n`;\n\nexport const BazaarvoiceRatingPlaceholder = styled.div`\n position: absolute;\n top: 0;\n z-index: 0;\n`;\n\nexport const BazaarvoiceRatingsDiv = styled.div`\n position: absolute;\n z-index: 1;\n top: 0;\n\n background: ${theme.palette.basicInverse};\n\n font-size: 0.75rem !important;\n`;\n\nexport const Wrapper = styled.div`\n height: 1.125rem;\n position: relative;\n`;\n","import * as React from 'react';\n\nimport { Colors, Ratings } from '@notino/react-styleguide';\n\nimport {\n BazaarvoiceRatingPlaceholder,\n BazaarvoiceRatingsDiv,\n Wrapper,\n} from './styled';\n\ninterface BazaarvoiceRatingsProps {\n productCode: string;\n}\n\nexport const BazaarvoiceRatings = ({\n productCode,\n}: BazaarvoiceRatingsProps) => {\n return (\n \n \n \n \n\n \n \n );\n};\n","import { TrackingAttributes } from '@context/tracking/types';\nimport { dispatchTrackingEvent } from '@context/tracking/utils';\n\nexport const trackBrandClick = ({\n timing,\n name,\n}: Omit, 'variant'>) => {\n dispatchTrackingEvent({\n event: 'element_click',\n element: {\n timing,\n name,\n action: 'click_on_element',\n interaction: 'click',\n type: 'brand',\n },\n _clear: true,\n });\n};\n\nexport const trackRatingClick = ({\n timing,\n}: Omit) => {\n dispatchTrackingEvent({\n event: 'element_click',\n element: {\n timing,\n name: 'stars',\n action: 'click_on_element',\n interaction: 'click',\n type: 'product_detail',\n },\n _clear: true,\n });\n};\n\nexport const withSpaceAfterWord = (word: string) => `${word} `;\n","import * as React from 'react';\n\nimport { useQuery } from '@apollo/client';\n\nimport { Ratings, Colors } from '@notino/react-styleguide';\nimport {\n GetReviewOverviewQuery,\n GetReviewOverviewQueryVariables,\n} from '@notino/shared/definitions/types';\n\nimport { useTrackingContext } from '@context/tracking/TrackingContext';\nimport { TabLink } from '@sharedComponents/TabSwitcher';\n\nimport getReviewOverviewQuery from '../../queries/reviewOverview.graphql';\nimport { TabIds } from '../model';\n\nimport { ReviewsAnchor } from './styled';\nimport { trackRatingClick } from './utils';\n\ninterface IPlaceholderProps {\n masterId: string;\n reviewsId?: string;\n}\n\nexport const ProductRating = ({\n masterId,\n reviewsId = 'productBaseInfo',\n}: IPlaceholderProps) => {\n const { getTimeFromInit } = useTrackingContext();\n const { loading, error, data } = useQuery<\n GetReviewOverviewQuery,\n GetReviewOverviewQueryVariables\n >(getReviewOverviewQuery, {\n ssr: true,\n errorPolicy: 'all',\n variables: {\n masterId,\n },\n });\n\n const reviewOverview = data?.productDetailByMasterId?.[0]?.reviewOverview;\n\n const isReviewOverviewLoaded = !loading && !error && reviewOverview;\n\n const rating = isReviewOverviewLoaded ? reviewOverview.rating : 4.0;\n const totalVotes = isReviewOverviewLoaded ? reviewOverview.count : 1;\n\n return (\n \n {({ setActiveTab }) => {\n const handleReviewClick = () => {\n trackRatingClick({ timing: getTimeFromInit() });\n setActiveTab();\n };\n\n return (\n \n \n \n );\n }}\n \n );\n};\n","import * as React from 'react';\n\nimport ErrorBoundary from '@components/ErrorBoundary';\nimport { useFeatureFlags } from '@context/launch-darkly/LDProvider';\nimport { useTrackingContext } from '@context/tracking/TrackingContext';\n\nimport { useNewPdDesignEnabled } from '../hooks/useNewPdDesignEnabled';\n\nimport { BazaarvoiceRatings } from './BazaarvoiceRatings';\nimport { IHeaderProps } from './model';\nimport { ProductRating } from './ProductRating';\nimport {\n StyledHeader,\n H1,\n Span,\n Description,\n SubName,\n BrandExtraLine,\n ProductNameWrapper,\n} from './styled';\nimport { trackBrandClick, withSpaceAfterWord } from './utils';\n\nexport const Header = ({\n desc,\n collection,\n subName,\n brandInfo,\n productId,\n productCode,\n children,\n reviewsId,\n hideReviews = false,\n}: React.PropsWithChildren) => {\n const newPdDesign = useNewPdDesignEnabled();\n const { bazaarvoiceIntegrationPd } = useFeatureFlags();\n const { getTimeFromInit } = useTrackingContext();\n\n const handleTrackClick = () =>\n trackBrandClick({\n timing: getTimeFromInit(),\n name: brandInfo.name,\n });\n\n const showOwnRatings = !hideReviews && !bazaarvoiceIntegrationPd;\n const showBazaarvoiceRatings = !hideReviews && !!bazaarvoiceIntegrationPd;\n\n return (\n \n \n \n );\n};\n","import loadable from '@loadable/component';\n\nexport default loadable(() => import('../ModiFaceModal'));\n","export interface IColorVariant {\n colors: string[];\n id: string | number;\n}\n\nexport interface IModiFaceVariant {\n [key: string]: unknown;\n}\n\nexport interface IModifaceEffectVariants {\n [key: string]: IModiFaceVariant[];\n}\n\nexport enum EMessage {\n allowWebcam = 'allowWebcam',\n webcamError = 'webcamError',\n genericError = 'genericError',\n faceNotDetected = 'faceNotDetected',\n fileSizeTooBig = 'fileSizeTooBig',\n unsupportedFormat = 'unsupportedFormat',\n photoDimensionsTooSmall = 'photoDimensionsTooSmall',\n noMessage = '',\n}\n\nexport enum ModeType {\n NONE,\n CAMERA,\n PHOTO,\n}\n","export const isRenderingOnServer = () => typeof window === 'undefined';\n","import { isRenderingOnServer } from '@utils/isRenderingOnServer';\n\nif (\n !isRenderingOnServer() &&\n navigator.mediaDevices &&\n navigator.mediaDevices.enumerateDevices\n) {\n // Firefox 38+ seems having support of enumerateDevices\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n navigator.enumerateDevices = (callback: (param: any) => void) => {\n const enumerateDevices = navigator.mediaDevices.enumerateDevices();\n // eslint-disable-next-line @typescript-eslint/no-misused-promises\n if (enumerateDevices && enumerateDevices.then) {\n navigator.mediaDevices\n .enumerateDevices()\n .then(callback)\n .catch(() => {\n callback([]);\n });\n } else {\n callback([]);\n }\n };\n}\n\nexport const hasDeviceWebcam = (callback: (hasWebcam: boolean) => void) => {\n if (\n (typeof MediaStreamTrack === 'undefined' ||\n !('getSources' in MediaStreamTrack)) &&\n (!navigator.mediaDevices || !navigator.mediaDevices.enumerateDevices)\n ) {\n callback(false);\n return;\n }\n\n if (\n !navigator.enumerateDevices &&\n window.MediaStreamTrack &&\n window.MediaStreamTrack.getSources\n ) {\n navigator.enumerateDevices = window.MediaStreamTrack.getSources.bind(\n window.MediaStreamTrack\n );\n }\n\n if (!navigator.enumerateDevices && navigator.enumerateDevices) {\n navigator.enumerateDevices = navigator.enumerateDevices.bind(navigator);\n }\n\n if (!navigator.enumerateDevices) {\n callback(false);\n return;\n }\n\n navigator.enumerateDevices((devices) => {\n const hasWebCam = devices.some(\n (device: { kind: string }) =>\n device.kind === 'videoinput' || device.kind === 'video'\n );\n callback(hasWebCam);\n });\n};\n","import { EMessage } from '../model';\n\nexport const triggerWebcamPermission = (\n onSuccess: () => void,\n onFail: (message: EMessage) => void\n) => {\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n const navigator = window.navigator as any;\n\n navigator.getUserMedia =\n navigator.getUserMedia ||\n navigator.webkitGetUserMedia ||\n navigator.mozGetUserMedia ||\n navigator.msGetUserMedia;\n\n if (navigator.getUserMedia) {\n navigator.getUserMedia(\n { video: { width: 640, height: 450 } },\n onSuccess,\n () => {\n onFail(EMessage.webcamError);\n }\n );\n } else if (navigator.mediaDevices) {\n // Safari fix\n navigator.mediaDevices.getUserMedia({ video: true }).then(\n (stream: MediaStream) => {\n stream.getVideoTracks()[0].stop();\n onSuccess();\n },\n () => {\n onFail(EMessage.webcamError);\n }\n );\n } else {\n onFail(EMessage.genericError);\n }\n};\n","import { GetProductViewQuery } from '@notino/shared/definitions/types';\n\nimport { TrackingAttributes } from '@context/tracking/types';\nimport { dispatchTrackingEvent } from '@context/tracking/utils';\nimport { ProductEventWither } from '@helpers/googleTagManager';\n\nexport const trackModifaceOpen = ({\n timing,\n product,\n}: Omit<\n TrackingAttributes<{\n product: GetProductViewQuery['productDetailByMasterId'][number];\n }>,\n 'variant'\n>) => {\n dispatchTrackingEvent({\n event: 'modiface_open',\n modiface: {\n timing,\n products: [ProductEventWither().withProduct(product).forceBuild()],\n action: 'modiface_opened',\n interaction: 'click',\n },\n _clear: true,\n });\n};\n\nexport const trackModifaceLoaded = ({\n timing,\n product,\n}: Omit<\n TrackingAttributes<{\n product: GetProductViewQuery['productDetailByMasterId'][number];\n }>,\n 'variant'\n>) => {\n dispatchTrackingEvent({\n event: 'modiface_loaded',\n modiface: {\n timing,\n products: [ProductEventWither().withProduct(product).forceBuild()],\n action: 'modiface_loaded',\n interaction: 'automatic',\n },\n _clear: true,\n });\n};\n\nexport const trackModifaceFailed = ({\n timing,\n product,\n error,\n}: Omit<\n TrackingAttributes<{\n product: GetProductViewQuery['productDetailByMasterId'][number];\n error: string;\n }>,\n 'variant'\n>) => {\n dispatchTrackingEvent({\n event: 'modiface_error',\n modiface: {\n timing,\n products: [ProductEventWither().withProduct(product).forceBuild()],\n action: 'modiface_error',\n interaction: 'automatic',\n type: 'load',\n error_message: error,\n },\n _clear: true,\n });\n};\n\nexport const trackModifaceSelectClick = ({\n timing,\n product,\n selectedVariant,\n viewedVariants,\n}: Omit<\n TrackingAttributes<{\n product: GetProductViewQuery['productDetailByMasterId'][number];\n selectedVariant: GetProductViewQuery['productDetailByMasterId'][number]['variants'][number];\n viewedVariants: number;\n }>,\n 'variant'\n>) => {\n dispatchTrackingEvent({\n event: 'modiface_select_product',\n modiface: {\n timing,\n products: [\n ProductEventWither()\n .withProduct(product)\n .withVariant(selectedVariant)\n .build(),\n ],\n action: 'modiface_product_selected',\n interaction: 'click',\n viewed_variants: viewedVariants,\n },\n _clear: true,\n });\n};\n\nexport const trackModifaceClosed = ({\n timing,\n product,\n selectedVariant,\n viewedVariants,\n}: Omit<\n TrackingAttributes<{\n product: GetProductViewQuery['productDetailByMasterId'][number];\n selectedVariant: GetProductViewQuery['productDetailByMasterId'][number]['variants'][number];\n viewedVariants: number;\n }>,\n 'variant'\n>) => {\n dispatchTrackingEvent({\n event: 'modiface_close',\n modiface: {\n timing,\n products: [\n ProductEventWither()\n .withProduct(product)\n .withVariant(selectedVariant)\n .build(),\n ],\n interaction: 'close_x',\n action: 'modiface_closed',\n viewed_variants: viewedVariants,\n },\n _clear: true,\n });\n};\n","import styled, { css } from 'styled-components';\n\nimport {\n breakpoints,\n IBasicStyledProps,\n InfoCircleIcon,\n theme,\n} from '@notino/react-styleguide';\n\nexport const Wrapper = styled.div<{ newDesign: boolean }>`\n line-height: 1.3125rem;\n margin: 1rem 0;\n font-size: 0.875rem;\n\n ${({ newDesign }) =>\n newDesign &&\n css`\n margin: 0.75rem 0 1rem;\n @media (min-width: ${breakpoints.lg}) {\n margin: 1rem 0;\n }\n `}\n`;\n\nexport const CodeBlock = styled.span`\n color: ${(props) => props.theme.palette.neutralDarker};\n`;\n\nexport const CodeName = styled.span`\n display: none;\n\n @media (min-width: ${breakpoints.sm}) {\n display: inline;\n }\n`;\n\nexport const Separator = styled.span<{ newDesign: boolean }>`\n color: ${({ newDesign }) =>\n newDesign ? theme.palette.neutral : theme.palette.neutralDarker};\n`;\n\nexport interface IAvailabilityProps extends IBasicStyledProps {\n highlight: boolean;\n newDesign: boolean;\n}\n\nexport const Availability = styled.span`\n color: ${(props: IAvailabilityProps) =>\n props.highlight\n ? props.theme.palette.primary\n : props.theme.palette.successDark};\n\n ${({ newDesign, highlight }) =>\n newDesign\n ? css`\n font-weight: ${highlight ? 500 : 'normal'};\n `\n : css`\n font-weight: ${highlight ? 'bold' : 'normal'};\n `}\n`;\n\nexport interface IAdditionalDescriptionStyledProps extends IBasicStyledProps {\n damage?: boolean;\n highlight?: boolean;\n}\n// prettier-ignore\nexport const AdditionalDescriptionStyled = styled.p`\n color: ${(props: IAdditionalDescriptionStyledProps) =>\n props.damage ? props.theme.palette.alert : props.theme.palette.basic};\n font-weight: ${(props: IAdditionalDescriptionStyledProps) =>\n props.highlight ? 'bold' : 'normal'};\n margin: 0;\n`;\n\nexport const AdditionalText = styled.div`\n margin: 0;\n`;\n\nexport const StyledInfoIcon = styled(InfoCircleIcon)`\n margin-bottom: 4px;\n`;\n\nexport const TooltipContent = styled.div`\n max-width: max-content;\n width: 18rem;\n padding: 0.5rem;\n`;\n","import loadable from '@loadable/component';\n\nexport default loadable(() => import('./index'));\n","import styled, { css } from 'styled-components';\n\nimport { breakpoints, theme } from '@notino/react-styleguide';\n\nimport { Wrapper, Currency } from '@components/PriceLabel/components';\nexport const Container = styled.div<{\n newDesign: boolean;\n}>`\n margin-top: 0;\n\n ${({ newDesign }) =>\n newDesign\n ? css`\n @media (min-width: ${breakpoints.lg}) {\n margin-top: 1rem;\n }\n `\n : css`\n @media (min-width: ${breakpoints.md}) {\n margin-top: 1.25rem;\n }\n `}\n`;\n\nexport const FlexWrapper = styled.div<{ newDesign: boolean }>`\n display: flex;\n justify-content: space-between;\n align-items: flex-end;\n\n ${({ newDesign }) =>\n newDesign\n ? css`\n margin-top: 1.5rem;\n height: 2rem;\n `\n : css`\n min-height: 2.1875rem;\n\n @media (min-width: ${breakpoints.sm}) {\n min-height: 3.25rem;\n }\n `}\n`;\n\nexport const LineWrapper = styled.div<{ newDesign: boolean }>`\n display: flex;\n justify-content: space-between;\n align-items: center;\n\n ${({ newDesign }) =>\n newDesign\n ? css`\n margin: 0.75rem 0;\n @media (min-width: ${breakpoints.lg}) {\n margin-top: 0.5rem;\n }\n `\n : css`\n min-height: 0.9375rem;\n margin-bottom: 0.5625rem;\n `}\n`;\n\nexport const Line = styled.hr<{ dashedLine: boolean }>`\n flex: 1;\n ${({ dashedLine }) =>\n dashedLine\n ? `border-top: 1px dashed ${theme.palette.neutralLight};`\n : `border-top: 1px solid ${theme.palette.neutralDark};`};\n height: 0.0625rem;\n margin: 0;\n border-bottom: none;\n`;\n\nexport const Name = styled.div<{ newDesign: boolean }>`\n padding: 0 0.5rem 0.05rem 0;\n ${({ newDesign }) =>\n newDesign\n ? css`\n ${theme.typography.labelLarge400}\n `\n : css`\n font-weight: 400;\n font-size: 1.25rem;\n\n @media (min-width: ${breakpoints.sm}) {\n font-size: 1.3rem;\n padding: 0 0.5rem 0.2rem 0;\n }\n @media (min-width: ${breakpoints.md}) {\n font-size: 1.3rem;\n padding: 0 0.5rem 0.3rem 0;\n }\n `}\n`;\n\nexport const PriceWrapper = styled(Wrapper)`\n ${theme.typography.titleDisplay}\n`;\n\nexport const CurrencyWrapper = styled(Currency)`\n ${theme.typography.titleLarge}\n`;\n\nexport const DualPriceWrapper = styled.div`\n display: flex;\n flex-wrap: wrap;\n justify-content: flex-end;\n`;\n\nexport const DualPrice = styled.div<{ newDesign: boolean }>`\n font-size: 1.55rem;\n font-weight: 700;\n ${({ newDesign }) => newDesign && theme.typography.titleLarge}\n`;\n\nexport const DualPriceSplitter = styled.div<{ newDesign: boolean }>`\n padding: 0 0.3rem;\n font-size: 1.55rem;\n font-weight: 700;\n ${({ newDesign }) => newDesign && theme.typography.titleLarge}\n`;\n","import loadable from '@loadable/component';\n\nexport default loadable(() => import('./ShadefinderButton'));\n","import { defineMessages } from 'react-intl';\n\nexport const messages = defineMessages({\n tryItFirst: {\n id: 'try.it.first',\n defaultMessage: 'Nejdřív vyzkoušejte, pak rozbalujte',\n },\n tryItFirstDescription: {\n id: 'try.it.first.description',\n defaultMessage:\n 'Pošleme vám i vzorek produktu a až poté se rozhodnete, zda si velké balení necháte.',\n },\n tryItFirstAbout: {\n id: 'try.it.first.about',\n defaultMessage: 'Více o službě Try it First',\n },\n});\n","import { defineMessages } from 'react-intl';\n\nexport const messages = defineMessages({\n discountAmount: {\n id: 'pd.black.friday.discount.highlighted',\n defaultMessage: 'Sleva 20% navíc ',\n },\n code: {\n id: 'pd.black.friday.code',\n defaultMessage: 'SUMMER',\n },\n label: {\n id: 'pd.black.friday.label',\n defaultMessage: 'Black Friday',\n },\n blackFridayLabel: {\n id: 'pd.black.friday.original.label',\n defaultMessage: 'Black Friday',\n },\n sbfLabel: {\n id: 'pd.summer.black.friday.label',\n defaultMessage: 'Summer Black Friday',\n },\n});\n","import { defineMessages } from 'react-intl';\n\nexport const messages = defineMessages({\n code: {\n id: 'pd.discount.code',\n defaultMessage: 'notino',\n },\n codeInCart: {\n id: 'pd.discount.code.in.cart',\n defaultMessage: 'Kód zadejte v košíku',\n },\n conditionText: {\n id: 'pd.discount.condition.text',\n defaultMessage: 'Nevztahuje se na zboží v akci a ve výprodeji',\n },\n conditionalTooltipText: {\n id: 'pd.discount.conditionalTooltip.text',\n defaultMessage:\n 'Kód zadejte prosím v košíku. Voucher se nevztahuje na zboží v akci a ve výprodeji.',\n },\n conditionalTooltipSaleIncludedText: {\n id: 'pd.discount.conditionalTooltip.saleIncluded.text',\n defaultMessage: 'Kód zadejte prosím v košíku.',\n },\n descriptionText: {\n id: 'pd.discount.description.text',\n defaultMessage: '{highlighted} s kódem {code}',\n },\n discountAmountPercentage: {\n id: 'product.item.discount.highlighted',\n defaultMessage: '-{amount} %',\n },\n discountAmountAbsolute: {\n id: 'product.item.discount.highlighted.absolute',\n defaultMessage: '{amountPrice} sleva',\n },\n copyCode: {\n id: 'pd.discount.copyToClipboard.tooltip.copyCode',\n defaultMessage: 'Zkopírovat kód ',\n },\n codeCopied: {\n id: 'pd.discount.copyToClipboard.tooltip.codeCopied',\n defaultMessage: 'Kód zkopírovan!',\n },\n});\n","import styled from 'styled-components';\n\nimport { ReactFCC } from '@notino/react-styleguide';\n\nexport const Wrapper = styled.span`\n font-size: inherit;\n line-height: inherit;\n`;\n\nexport const CurrencyWrapper = styled(Wrapper)<{\n isSpaced: boolean;\n isPrefix: boolean;\n}>`\n &:before {\n content: ${(props) => (props.isSpaced && !props.isPrefix ? '\" \"' : '')};\n }\n &:after {\n content: ${(props) => (props.isSpaced && props.isPrefix ? '\" \"' : '')};\n }\n` as ReactFCC;\n\nexport const LineWrapper = styled.div`\n margin-bottom: 0.75rem;\n`;\n\nexport const Slash = styled.span`\n padding: 0 0.25rem;\n`;\n","import*as e from\"react\";import n from\"styled-components\";import{theme as t}from\"./theme.js\";var o=n.div.withConfig({displayName:\"PercentageDiscountIcon__Wrapper\",componentId:\"sc-1w34n0m-0\"})([\"width:2.5rem;height:2.5rem;background:\",\";color:\",\";font-weight:bold;font-size:0.75rem;display:flex;align-items:center;justify-content:center;\"],t.palette.basic,t.palette.basicInverse),r=n.span.withConfig({displayName:\"PercentageDiscountIcon__Minus\",componentId:\"sc-1w34n0m-1\"})([\"margin-right:1px;\"]),i=n.span.withConfig({displayName:\"PercentageDiscountIcon__Symbol\",componentId:\"sc-1w34n0m-2\"})([\"font-weight:normal;margin-left:3px;\"]),a=function(n){var t=n.percentage;return e.createElement(o,null,e.createElement(r,null,\"-\"),t,e.createElement(i,null,\"%\"))};export{a as PercentageDiscountIcon};\n//# sourceMappingURL=PercentageDiscountIcon.js.map\n","import{_ as e}from\"./_rollupPluginBabelHelpers-fc5c8eb1.js\";import*as t from\"react\";import{Icon as r}from\"./index.js\";import\"./ThemeContext.js\";import\"styled-components\";var o=function(o){return t.createElement(r,e({width:\"20\",height:\"20\",viewBox:\"0 0 20 20\"},o),t.createElement(\"path\",{d:\"M4.5 18C4.0875 18 3.73437 17.8531 3.44062 17.5594C3.14687 17.2656 3 16.9125 3 16.5V5H4.5V16.5H14V18H4.5ZM7.5 15C7.0875 15 6.73437 14.8531 6.44062 14.5594C6.14687 14.2656 6 13.9125 6 13.5V3.5C6 3.0875 6.14687 2.73438 6.44062 2.44063C6.73437 2.14688 7.0875 2 7.5 2H15.5C15.9125 2 16.2656 2.14688 16.5594 2.44063C16.8531 2.73438 17 3.0875 17 3.5V13.5C17 13.9125 16.8531 14.2656 16.5594 14.5594C16.2656 14.8531 15.9125 15 15.5 15H7.5ZM7.5 13.5H15.5V3.5H7.5V13.5Z\",fill:\"currentColor\"}))};export{o as CopyToClipboardIcon};\n//# sourceMappingURL=CopyToClipboardIcon.js.map\n","import styled, { css } from 'styled-components';\n\nimport { breakpoints } from '@notino/react-styleguide';\n\nimport theme from '@themes/default';\n\nexport const VoucherDiscountInfoWrapper = styled.div<{\n newDesign: boolean;\n isUnderSelectBox?: boolean;\n}>`\n height: 4rem;\n display: flex;\n align-items: center;\n justify-content: space-between;\n background-color: ${theme.palette.primary}08;\n padding: 0.75rem;\n margin: ${(props) =>\n props.isUnderSelectBox ? '0 0 0.75rem 0' : '-1rem 0 0.75rem 0'};\n\n ${({ newDesign }) =>\n newDesign\n ? css`\n @media (min-width: ${breakpoints.md}) {\n margin: 0.5rem 0 0.75rem 0;\n }\n `\n : css`\n margin: -1rem 0 0.75rem 0;\n\n @media (min-width: ${breakpoints.md}) {\n margin: -1.15rem 0 0.75rem 0;\n }\n `}\n`;\n\nexport const DiscountInfoDescription = styled.div`\n text-align: left;\n`;\n\nexport const DiscountAmmountWrapper = styled.span<{\n newDesign: boolean;\n color?: string;\n}>`\n color: ${({ color = theme.palette.primary }) => color};\n\n ${({ newDesign }) =>\n newDesign\n ? theme.typography.labelRegular\n : css`\n font-weight: bold;\n `}\n`;\n\nexport const CodeWrapper = styled.span<{ newDesign: boolean }>`\n text-transfrom: uppercase;\n color: ${theme.palette.basic};\n\n ${({ newDesign }) =>\n newDesign\n ? theme.typography.labelRegular\n : css`\n font-weight: bold;\n `}\n`;\n\nexport const TermsWrapper = styled.div<{\n newDesign: boolean;\n}>`\n font-size: 0.75rem;\n color: ${theme.palette.neutralDark};\n font-weight: normal;\n display: flex;\n justify-content: flex-start;\n align-items: flex-start;\n\n @media (min-width: ${breakpoints.sm}) {\n align-items: center;\n }\n\n ${({ newDesign }) =>\n !newDesign &&\n css`\n @media (min-width: ${breakpoints.lg}) {\n margin-top: 0.2rem;\n }\n `}\n`;\n\nexport const TextWrapper = styled.div<{ newDesign: boolean }>`\n margin-bottom: 0.25rem;\n color: ${theme.palette.basic};\n\n ${({ newDesign }) =>\n newDesign\n ? theme.typography.labelRegular400\n : css`\n ${theme.typography.labelLarge}\n font-weight: 500;\n `}\n`;\n\nexport const VoucherDiscountIconWrapper = styled.div<{\n hideDesktop: boolean;\n}>`\n height: 2.25rem;\n display: block;\n\n margin: 0 0 0 2rem;\n\n @media (min-width: ${breakpoints.sm}) {\n display: ${({ hideDesktop }) => (hideDesktop ? 'none' : 'block')};\n margin: 0 0 0 1rem;\n }\n\n @media (min-width: ${breakpoints.md}) {\n margin: 0 0 0 2.5rem;\n }\n`;\n\nexport const InfoIconWrapper = styled.span<{ newDesign: boolean }>`\n display: flex;\n align-items: center;\n margin-top: 0.1rem;\n\n @media (min-width: ${breakpoints.sm}) {\n margin-top: 0;\n }\n`;\n\nexport const InstructionTextWrapper = styled.span<{ newDesign: boolean }>`\n padding-right: 0.3rem;\n\n ${({ newDesign }) =>\n newDesign\n ? css`\n ${theme.typography.labelSmall400}\n color: ${theme.palette.neutralDarker}\n `\n : css`\n ${theme.typography.labelRegular400}\n `}\n`;\n","import{a as e}from\"./_rollupPluginBabelHelpers-fc5c8eb1.js\";import*as i from\"react\";import n from\"styled-components\";var t=[\"children\",\"size\",\"onClick\"],r=n.span.withConfig({displayName:\"InteractiveArea__Wrapper\",componentId:\"sc-qyhg1f-0\"})([\"position:relative;display:inline-block;\"]),a=n.span.withConfig({displayName:\"InteractiveArea__Interactive\",componentId:\"sc-qyhg1f-1\"})([\"cursor:pointer;position:absolute;z-index:1;height:\",\";width:\",\";top:50%;left:50%;transform:translate(-50%,-50%);display:inline-block;\"],(function(e){return e.size||\"2.5rem\"}),(function(e){return e.size||\"2.5rem\"})),o=function(n){var o=n.children,s=n.size,c=n.onClick,l=e(n,t);return i.createElement(r,l,o,i.createElement(a,{className:\"interactive-area\",size:s,onClick:c}))};export{o as InteractiveArea};\n//# sourceMappingURL=InteractiveArea.js.map\n","import styled from 'styled-components';\n\nimport { InteractiveArea } from '@notino/react-styleguide';\n\nimport { TooltipContent } from '@components/Tooltip/Content';\n\nexport const ProductDiscountWrapper = styled.div`\n margin: -0.5rem 0 1rem 0;\n`;\n\nexport const StyledInteractiveArea = styled(InteractiveArea)`\n vertical-align: middle;\n top: -3px;\n .interactive-area {\n transform: translate(-50%, -18px);\n height: 1.75rem;\n }\n`;\n\nexport const TextAndTooltipWrapper = styled.div`\n display: flex;\n gap: 0.25rem;\n align-items: center;\n`;\n\nexport const CopyTooltipContent = styled(TooltipContent)`\n min-width: 0;\n`;\n","import * as React from 'react';\nimport { FormattedMessage } from 'react-intl';\n\nimport {\n Tooltip,\n TooltipModel,\n InfoCircleIcon,\n Colors,\n CopyToClipboardIcon,\n} from '@notino/react-styleguide';\n\nimport { PortalTooltip } from '@components/PortalTooltip';\nimport { useNewPdDesignEnabled } from '@containers/ProductDetailContainer/ProductDetail/hooks/useNewPdDesignEnabled';\n\nimport { messages } from '../../messages';\nimport {\n TextWrapper,\n CodeWrapper,\n DiscountAmmountWrapper,\n TermsWrapper,\n InstructionTextWrapper,\n InfoIconWrapper,\n DiscountInfoDescription,\n} from '../styled';\n\nimport {\n CopyTooltipContent,\n StyledInteractiveArea,\n TextAndTooltipWrapper,\n} from './styled';\n\ninterface IProductDiscountProps {\n highlightedDiscountAmount: string | JSX.Element;\n instructionsText: JSX.Element;\n infoTooltipText: JSX.Element;\n discountCode: string;\n highlightedDiscountAmountColor?: string;\n}\n\nexport const ProductDiscount: React.FC = ({\n highlightedDiscountAmount,\n discountCode,\n instructionsText,\n infoTooltipText,\n highlightedDiscountAmountColor,\n}) => {\n const newDesign = useNewPdDesignEnabled();\n const [showIsCopied, setShowIsCopied] = React.useState(false);\n const tooltipTimeout = React.useRef(null);\n\n const handleCopy = async () => {\n await navigator.clipboard.writeText(discountCode);\n setShowIsCopied(true);\n clearTimeout(tooltipTimeout.current);\n tooltipTimeout.current = setTimeout(() => setShowIsCopied(false), 2000);\n };\n\n return (\n \n \n \n {discountCode}\n ),\n highlighted: (\n \n {highlightedDiscountAmount}\n \n ),\n }}\n />\n \n \n {showIsCopied ? (\n \n ) : (\n \n )}\n \n }\n >\n \n \n \n \n \n\n \n \n {instructionsText}\n \n\n {infoTooltipText && (\n {infoTooltipText}\n }\n >\n \n \n \n \n )}\n \n \n );\n};\n","import * as React from 'react';\n\nimport { PercentageDiscountIcon } from '@notino/react-styleguide';\n\nimport { useNewPdDesignEnabled } from '@containers/ProductDetailContainer/ProductDetail/hooks/useNewPdDesignEnabled';\n\nimport { ProductDiscount } from './ProductDiscount';\nimport {\n VoucherDiscountInfoWrapper,\n VoucherDiscountIconWrapper,\n} from './styled';\n\nexport interface IVoucherDiscountInfoProps {\n highlightedDiscountAmount: string | JSX.Element;\n instructionsText: JSX.Element;\n infoTooltipText: JSX.Element;\n discountCode: string;\n percentage: number;\n isUnderSelectBox?: boolean;\n showPercentageIcon: boolean;\n highlightedDiscountAmountColor?: string;\n}\n\nexport const VoucherDiscountInfo: React.FC = ({\n showPercentageIcon,\n percentage,\n isUnderSelectBox,\n ...props\n}) => {\n const newDesign = useNewPdDesignEnabled();\n\n return (\n \n \n\n {showPercentageIcon && (\n \n \n \n )}\n \n );\n};\n","import { styled } from '@notino/react-styleguide';\n\nexport const Link = styled.a`\n color: inherit;\n`;\n","import * as React from 'react';\n\nimport { ReactFCC, Tooltip } from '@notino/react-styleguide';\n\nimport { TooltipContent } from '@components/Tooltip/Content';\n\nimport { IConditionalVoucherDiscount } from '..';\n\nimport { Link } from './styled';\n\ninterface IConditionsTooltipProps {\n targets: IConditionalVoucherDiscount['targets'];\n}\nexport const ConditionsTooltip: ReactFCC = ({\n children,\n targets,\n}) => {\n const content = targets.map((target, idx) => (\n \n {target.name}\n {idx < targets.length - 1 && ', '}\n \n ));\n return (\n {content}}\n wrapText={true}\n alignDesktop={true}\n >\n {children}\n \n );\n};\n","import { defineMessages } from 'react-intl';\n\nexport const messages = defineMessages({\n brands1Categories0FromPrice: {\n id: 'pd.conditionalVoucherDiscount.fromPrice.brands.1.categories.0',\n defaultMessage: 'Platí od {price} na značku {brand}',\n },\n brands1Categories0FromPieces: {\n id: 'pd.conditionalVoucherDiscount.fromPieces.brands.1.categories.0',\n defaultMessage: 'Platí od {pieces} ks na značku {brand}',\n },\n brandsNCategories0FromPrice: {\n id: 'pd.conditionalVoucherDiscount.fromPrice.brands.N.categories.0',\n defaultMessage: 'Platí od {price} na tyto značky',\n },\n brandsNCategories0FromPieces: {\n id: 'pd.conditionalVoucherDiscount.fromPieces.brands.N.categories.0',\n defaultMessage: 'Platí od {pieces} ks na tyto značky',\n },\n brands0Categories1FromPrice: {\n id: 'pd.conditionalVoucherDiscount.fromPrice.brands.0.categories.1',\n defaultMessage: 'Platí od {price} na kategorii {category}',\n },\n brands0Categories1FromPieces: {\n id: 'pd.conditionalVoucherDiscount.fromPieces.brands.0.categories.1',\n defaultMessage: 'Platí od {pieces} ks na kategorii {category}',\n },\n brands0CategoriesNFromPrice: {\n id: 'pd.conditionalVoucherDiscount.fromPrice.brands.0.categories.N',\n defaultMessage:\n 'Platí od {price} na tyto kategorie',\n },\n brands0CategoriesNFromPieces: {\n id: 'pd.conditionalVoucherDiscount.fromPieces.brands.0.categories.N',\n defaultMessage:\n 'Platí od {pieces} ks na tyto kategorie',\n },\n brandsNCategoriesNFromPrice: {\n id: 'pd.conditionalVoucherDiscount.fromPrice.brands.N.categories.N',\n defaultMessage:\n 'Platí od {price} na tyto značky a kategorie',\n },\n brandsNCategoriesNFromPieces: {\n id: 'pd.conditionalVoucherDiscount.fromPieces.brands.N.categories.N',\n defaultMessage:\n 'Platí od {pieces} ks na tyto značky a kategorie',\n },\n fromPriceSelectedProducts: {\n id: 'pd.conditionalVoucherDiscount.fromPrice.selectedProducts',\n defaultMessage: 'Platí od {price} na vybrané produkty',\n },\n fromPiecesSelectedProducts: {\n id: 'pd.conditionalVoucherDiscount.fromPieces.selectedProducts',\n defaultMessage: 'Platí od {pieces} ks na vybrané produkty',\n },\n});\n","import * as React from 'react';\nimport { useIntl } from 'react-intl';\n\nimport { useFormatPrice } from '@components/PriceLabel/useFormattedPrice';\nimport { useIsDualPriceForCroatia } from '@containers/ProductDetailContainer/ProductDetail/hooks/useIsDualPriceForCroatia';\nimport { partition } from '@utils/array';\n\nimport { IConditionalVoucherDiscount } from '..';\n\nimport { ConditionsTooltip } from './ConditionsTooltip';\nimport { messages } from './messages';\nimport { Link } from './styled';\n\ntype Props = {\n discount: IConditionalVoucherDiscount;\n};\n\nexport const ConditionalInstructionsText = ({\n discount: { targets, conditionType, discountConditions },\n}: Props) => {\n const { formatMessage } = useIntl();\n const formatPrice = useFormatPrice();\n const {\n isDualPriceForCroatia,\n getLeftSideFormattedPrice,\n leftSideCurrency,\n rightSideCurrency,\n getRightSideFormattedPrice,\n } = useIsDualPriceForCroatia();\n\n const [brands, categories] = partition(targets, 'type', [\n 'Brand',\n 'Category',\n ]);\n\n const conditionMin = discountConditions[0].conditionMin;\n\n const priceOrPieces =\n conditionType === 'Piece'\n ? { pieces: conditionMin }\n : {\n price: isDualPriceForCroatia\n ? `${getLeftSideFormattedPrice(\n conditionMin\n )} ${leftSideCurrency} / ${getRightSideFormattedPrice(\n conditionMin\n )} ${rightSideCurrency}`\n : formatPrice({ value: conditionMin }).formattedPriceWithCurrency,\n };\n\n let formattedMessage;\n if (brands.length > 0 && categories.length > 0) {\n const message =\n conditionType === 'Price'\n ? messages.brandsNCategoriesNFromPrice\n : messages.brandsNCategoriesNFromPieces;\n\n formattedMessage = formatMessage(message, {\n ...priceOrPieces,\n these: (parts) => (\n {parts}\n ),\n });\n } else if (brands.length > 1) {\n const message =\n conditionType === 'Price'\n ? messages.brandsNCategories0FromPrice\n : messages.brandsNCategories0FromPieces;\n\n formattedMessage = formatMessage(message, {\n ...priceOrPieces,\n brands: (parts) => (\n {parts}\n ),\n });\n } else if (categories.length > 1) {\n const message =\n conditionType === 'Price'\n ? messages.brands0CategoriesNFromPrice\n : messages.brands0CategoriesNFromPieces;\n\n formattedMessage = formatMessage(message, {\n ...priceOrPieces,\n categories: (parts) => (\n {parts}\n ),\n });\n } else if (brands.length === 1) {\n const message =\n conditionType === 'Price'\n ? messages.brands1Categories0FromPrice\n : messages.brands1Categories0FromPieces;\n\n formattedMessage = formatMessage(message, {\n ...priceOrPieces,\n brand: (\n \n {brands[0].name}\n \n ),\n });\n } else if (categories.length === 1) {\n const message =\n conditionType === 'Price'\n ? messages.brands0Categories1FromPrice\n : messages.brands0Categories1FromPieces;\n\n formattedMessage = formatMessage(message, {\n ...priceOrPieces,\n category: (\n \n {categories[0].name}\n \n ),\n });\n } else {\n const message =\n conditionType === 'Price'\n ? messages.fromPriceSelectedProducts\n : messages.fromPiecesSelectedProducts;\n\n formattedMessage = formatMessage(message, priceOrPieces);\n }\n\n return formattedMessage;\n};\n","import * as React from 'react';\nimport { FormattedMessage } from 'react-intl';\n\nimport { theme } from '@notino/react-styleguide';\n\nimport PriceLabel from '@components/PriceLabel';\nimport { ICurrency } from '@containers/App/model';\nimport { useIsDualPriceForCroatia } from '@containers/ProductDetailContainer/ProductDetail/hooks/useIsDualPriceForCroatia';\n\nimport { messages } from '../messages';\nimport { Wrapper, CurrencyWrapper, Slash } from '../styled';\nimport { VoucherDiscountInfo } from '../VoucherDiscountInfo';\n\nimport { ConditionalInstructionsText } from './ConditionalInstructionsText';\n\nexport interface IConditionalVoucherDiscount {\n code: string;\n conditionType: 'Price' | 'Piece';\n discountType: 'Absolute' | 'Percentage';\n productsOnSaleIncluded: boolean;\n discountConditions: Array<{\n percentageDiscount: number;\n discountedPrice: number;\n discountAmount: number;\n conditionMin: number;\n }>;\n targets: Array<{\n name: string;\n type: 'Brand' | 'Category';\n url: string;\n }>;\n}\n\nexport interface IVoucherDiscountProps {\n voucherDiscount: IConditionalVoucherDiscount;\n currency: ICurrency;\n isUnderSelectBox?: boolean;\n}\n\nexport const ConditionalVoucherDiscount = ({\n voucherDiscount,\n currency,\n isUnderSelectBox,\n}: IVoucherDiscountProps) => {\n const {\n isDualPriceForCroatia,\n getLeftSideFormattedPrice,\n leftSideCurrency,\n rightSideCurrency,\n getRightSidePrice,\n } = useIsDualPriceForCroatia();\n\n if (voucherDiscount.discountConditions.length !== 1) {\n return null;\n }\n\n const {\n code,\n productsOnSaleIncluded,\n discountType,\n discountConditions: [\n { discountedPrice, discountAmount, percentageDiscount },\n ],\n } = voucherDiscount;\n\n const getPriceComponent = (price: number) => (\n <>\n {isDualPriceForCroatia && (\n \n {getLeftSideFormattedPrice(price)}{' '}\n {leftSideCurrency}\n /\n \n )}\n \n >\n );\n\n const discountTypeComponentMap: Record = {\n Percentage: getPriceComponent(discountedPrice),\n Absolute: (\n \n ),\n };\n\n return (\n \n ) : (\n \n )\n }\n instructionsText={\n \n }\n />\n );\n};\n","import * as React from 'react';\nimport { FormattedMessage } from 'react-intl';\n\nimport PriceLabel from '@components/PriceLabel';\nimport { ICurrency } from '@containers/App/model';\n\nimport { useIsDualPriceForCroatia } from '../../hooks/useIsDualPriceForCroatia';\n\nimport { messages } from './messages';\nimport { Wrapper, CurrencyWrapper, Slash } from './styled';\nimport { VoucherDiscountInfo } from './VoucherDiscountInfo';\n\nexport interface IVoucherDiscount {\n displayType: 'DiscountedPrice' | 'Percentage';\n percentage: number;\n discountedPrice: number;\n code: string;\n productsOnSaleIncluded: boolean;\n}\n\nexport interface IVoucherDiscountProps {\n voucherDiscount: IVoucherDiscount;\n currency: ICurrency;\n isUnderSelectBox?: boolean;\n}\n\nexport const VoucherDiscount = ({\n voucherDiscount,\n currency,\n isUnderSelectBox,\n}: IVoucherDiscountProps) => {\n const {\n isDualPriceForCroatia,\n getLeftSideFormattedPrice,\n leftSideCurrency,\n rightSideCurrency,\n getRightSidePrice,\n } = useIsDualPriceForCroatia();\n\n const {\n code,\n percentage,\n discountedPrice,\n displayType,\n productsOnSaleIncluded,\n } = voucherDiscount;\n\n const discountAmountComponentMap: Record = {\n Percentage: (\n \n ),\n DiscountedPrice: (\n <>\n {isDualPriceForCroatia && (\n \n {getLeftSideFormattedPrice(discountedPrice)}{' '}\n {leftSideCurrency}\n /\n \n )}\n \n >\n ),\n };\n\n return (\n \n )\n }\n instructionsText={}\n highlightedDiscountAmount={discountAmountComponentMap[displayType]}\n />\n );\n};\n","import * as React from 'react';\n\nimport { ICurrency } from '@containers/App/model';\n\nimport { Line } from '../SelectedVariant/styled';\n\nimport {\n ConditionalVoucherDiscount,\n IConditionalVoucherDiscount,\n} from './ConditionalVoucherDiscount';\nimport { LineWrapper } from './styled';\nimport { IVoucherDiscount, VoucherDiscount } from './VoucherDiscount';\n\ninterface IVoucherDiscountProps {\n voucherDiscount: IVoucherDiscount | IConditionalVoucherDiscount;\n currency: ICurrency;\n isUnderSelectBox?: boolean;\n hideLine?: boolean;\n}\n\nexport const VoucherDiscountMaster = ({\n voucherDiscount,\n hideLine,\n ...rest\n}: IVoucherDiscountProps) => {\n if (\n isConditionalDiscount(voucherDiscount) &&\n voucherDiscount.discountConditions.length !== 1\n ) {\n return null;\n }\n\n return (\n <>\n {isConditionalDiscount(voucherDiscount) ? (\n \n ) : (\n \n )}\n\n {!hideLine && (\n \n \n \n )}\n >\n );\n};\n\nconst isConditionalDiscount = (\n discount: IConditionalVoucherDiscount | IVoucherDiscount\n): discount is IConditionalVoucherDiscount => {\n return 'discountConditions' in discount;\n};\n","export interface IEngravingTryItFirstContext {\n actions: IActions;\n state: IEngravingTryItFirstState;\n}\n\nexport interface IEngravingTryItFirstState {\n withEngraving: boolean;\n withTryItFirst: boolean;\n engravingInitials: string;\n}\n\nexport interface IActions {\n changeEngravingInitials: (value: string) => void;\n toggleTryItFirst: (value: boolean) => void;\n clear: () => void;\n}\n\nexport enum ActionTypes {\n CHANGE_ENGRAVING_INITIALS = 'CHANGE_ENGRAVING_INITIALS',\n TOGGLE_TRY_IT_FIRST = 'TOGGLE_TRY_IT_FIRST',\n CLEAR = 'CLEAR',\n}\n\nexport type Action =\n | { type: ActionTypes.CHANGE_ENGRAVING_INITIALS; value: string }\n | { type: ActionTypes.TOGGLE_TRY_IT_FIRST; value: boolean }\n | { type: ActionTypes.CLEAR };\n","import { Action, ActionTypes, IEngravingTryItFirstState } from './model';\n\nexport const initialState: IEngravingTryItFirstState = {\n withEngraving: false,\n withTryItFirst: false,\n engravingInitials: '',\n};\n\nexport const reducer = (\n state: IEngravingTryItFirstState,\n action: Action\n): IEngravingTryItFirstState => {\n switch (action.type) {\n case ActionTypes.CHANGE_ENGRAVING_INITIALS:\n return {\n ...state,\n withEngraving: action.value.length > 0,\n engravingInitials: action.value,\n withTryItFirst: false,\n };\n case ActionTypes.TOGGLE_TRY_IT_FIRST:\n return {\n ...state,\n withTryItFirst: action.value,\n withEngraving: false,\n };\n case ActionTypes.CLEAR:\n return {\n ...initialState,\n };\n default:\n return state;\n }\n};\n","import { dispatchTrackingEvent } from '@context/tracking/utils';\n\nexport const trackEngravingChecked = (checked: boolean, timing: number) => {\n dispatchTrackingEvent({\n event: 'element_click',\n element: {\n name: 'engraving',\n action: checked ? 'select_engraving' : 'deselect_engraving',\n interaction: 'checkbox',\n mode: checked ? 'on' : 'off',\n timing,\n type: 'engraving',\n },\n _clear: true,\n });\n};\n","import * as React from 'react';\n\nimport { ReactFCC } from '@notino/react-styleguide';\n\nimport { IEngravingTryItFirstContext } from './model';\nimport { reducer, initialState } from './reducer';\nimport { useActions } from './useActions';\n\nconst initialContext = {\n state: initialState,\n actions: {},\n};\n\nexport const EngravingTryItFirstContext =\n React.createContext(\n initialContext as IEngravingTryItFirstContext\n );\n\nexport const EngravingTryItFirstProvider: ReactFCC = ({ children }) => {\n const [state, dispatch] = React.useReducer(reducer, initialState);\n\n const actions = useActions(dispatch);\n const value = React.useMemo(() => ({ state, actions }), [state, actions]);\n\n return (\n \n {children}\n \n );\n};\n\nexport const useEngravingTryItFirstContext = () =>\n React.useContext(EngravingTryItFirstContext);\n","import * as React from 'react';\n\nimport { useTrackingContext } from '@context/tracking/TrackingContext';\n\nimport { trackEngravingChecked } from '../../Engraving/tracking';\nimport { trackTryItFirstChanged } from '../../TryItFirst/tracking';\n\nimport { Action, ActionTypes, IActions } from './model';\n\nexport const useActions = (dispatch: React.Dispatch): IActions => {\n const { getTimeFromInit } = useTrackingContext();\n\n return React.useMemo(\n () => ({\n changeEngravingInitials: (value: string) => {\n dispatch({ type: ActionTypes.CHANGE_ENGRAVING_INITIALS, value });\n trackEngravingChecked(value.length > 0, getTimeFromInit());\n },\n toggleTryItFirst: (value: boolean) => {\n dispatch({ type: ActionTypes.TOGGLE_TRY_IT_FIRST, value });\n trackTryItFirstChanged(value, getTimeFromInit());\n },\n clear: () => {\n dispatch({ type: ActionTypes.CLEAR });\n },\n }),\n [dispatch, getTimeFromInit]\n );\n};\n","import { dispatchTrackingEvent } from '@context/tracking/utils';\n\nexport const trackTryItFirstChanged = (checked: boolean, timing: number) => {\n dispatchTrackingEvent({\n event: 'element_click',\n element: {\n mode: checked ? 'on' : 'off',\n interaction: 'checkbox',\n action: checked ? 'select_try_it_first' : 'deselect_try_it_first',\n name: 'try_it_first',\n type: 'try_it_first',\n timing,\n },\n _clear: true,\n });\n};\n","import * as React from 'react';\n\nimport {\n GiftType,\n GetProductViewQuery,\n} from '@notino/shared/definitions/types';\n\nconst EMPTY_ARR = [];\nexport const useTryItFirstCampaigns = (\n campaigns: GetProductViewQuery['productDetailByMasterId'][number]['campaigns'] = EMPTY_ARR\n) => {\n return React.useMemo(() => {\n return campaigns.filter(\n (campaign) => campaign && campaign.giftType === GiftType.TryItFirst\n );\n }, [campaigns]);\n};\n","import styled, { css } from 'styled-components';\n\nimport { breakpoints, Button } from '@notino/react-styleguide';\n\nexport const Container = styled.div<{ newDesign: boolean }>`\n width: 100%;\n\n ${({ newDesign }) =>\n newDesign &&\n css`\n @media (min-width: ${breakpoints.lg}) {\n padding-bottom: 2rem;\n }\n `}\n`;\n\nexport const ButtonsWrapper = styled.div<{ canBuy?: boolean }>`\n display: flex;\n flex-direction: column;\n\n ${({ canBuy }) =>\n canBuy &&\n css`\n @media (min-width: ${breakpoints.lg}) {\n flex-direction: row;\n }\n `}\n`;\n\nexport const LeftIconWrapper = styled.div`\n align-self: flex-start;\n padding-right: 1rem;\n width: 2.25rem;\n line-height: 0;\n`;\n\nexport const StyledWatchdogButton = styled(Button)`\n text-transform: none !important;\n font-weight: normal !important;\n`;\n\nexport const WatchdogWrapper = styled.div`\n text-align: center;\n align-self: flex-start;\n\n @media (min-width: ${breakpoints.md}) {\n text-align: left;\n }\n`;\n\nexport const DiscountWrapper = styled.div<{ newDesign: boolean }>`\n ${({ newDesign }) =>\n newDesign &&\n css`\n margin-top: 0.75rem;\n `}\n`;\n","import * as React from 'react';\nimport { useLocation } from 'react-router';\n\nimport { GetProductViewQuery } from '@notino/shared/definitions/types';\n\nimport { selectVariant } from '../../utils';\n\nexport const useCurrentVariant = (\n variants: GetProductViewQuery['productDetailByMasterId'][number]['variants']\n) => {\n const { pathname } = useLocation();\n\n return React.useMemo(\n () => selectVariant(variants, pathname),\n [variants, pathname]\n );\n};\n","import * as React from 'react';\n\nimport { GetProductViewQuery } from '@notino/shared/definitions/types';\n\nimport { useCartServices } from './hooks/useCartServices';\nimport { useCurrentVariant } from './hooks/useCurrentVariant';\n\ninterface ProductDetailContextValue extends ReturnType {\n product: GetProductViewQuery['productDetailByMasterId'][number];\n currentVariant: GetProductViewQuery['productDetailByMasterId'][number]['variants'][number];\n}\n\ninterface Props {\n product: GetProductViewQuery['productDetailByMasterId'][number];\n}\n\nconst Context = React.createContext(null!);\n\nexport const useProductDetailContext = (throwError = true) => {\n const value = React.useContext(Context);\n if (!value && throwError) {\n throw Error(\n 'useProductDetailContext must be within ProductDetailContextProvider.'\n );\n }\n\n return value;\n};\n\nexport const ProductDetailContextProvider = ({\n children,\n product,\n}: React.PropsWithChildren) => {\n const currentVariant = useCurrentVariant(product.variants);\n const cartServices = useCartServices(product, currentVariant);\n\n const value: ProductDetailContextValue = React.useMemo(\n () => ({\n product,\n currentVariant,\n ...cartServices,\n }),\n [product, currentVariant, cartServices]\n );\n\n return {children};\n};\n","import * as React from 'react';\nimport { useMemo } from 'react';\n\nimport { useQuery } from '@apollo/client';\n\nimport {\n GiftType,\n GetProductViewQuery,\n GetServicesIncludedQuery,\n} from '@notino/shared/definitions/types';\n\nimport getServicesIncludedQuery from '@containers/ProductDetailContainer/queries/servicesIncluded.graphql';\n\nimport { getEngravingAvailability, getTryItFirstAvailability } from '../utils';\n\nexport const useCartServices = (\n product: GetProductViewQuery['productDetailByMasterId'][number],\n variant: GetProductViewQuery['productDetailByMasterId'][number]['variants'][number]\n) => {\n const productsIdsURI = React.useMemo(\n () => product.variants.map(({ id }) => `productIds=${id}`).join('&'),\n [product.variants]\n );\n\n const additionalServices = useQuery(\n getServicesIncludedQuery,\n {\n skip: !variant.canBuy,\n ssr: false,\n variables: {\n productsIds: productsIdsURI,\n },\n }\n );\n\n const engravingAvailable = React.useMemo(\n () =>\n additionalServices &&\n additionalServices.data?.CartServicesIncluded &&\n getEngravingAvailability(\n additionalServices.data.CartServicesIncluded?.engravingShow,\n variant.engraving,\n additionalServices.error\n ),\n [additionalServices, variant.engraving]\n );\n\n const tryItFirstCampaigns = React.useMemo(() => {\n const { campaigns = [] } = product;\n return campaigns.filter(\n (campaign) => campaign && campaign.giftType === GiftType.TryItFirst\n );\n }, [product]);\n\n const tryItFirstAvailable = React.useMemo(\n () =>\n additionalServices &&\n additionalServices.data?.CartServicesIncluded &&\n getTryItFirstAvailability(\n additionalServices.data.CartServicesIncluded?.tryFirstShow,\n tryItFirstCampaigns,\n variant,\n additionalServices.error\n ),\n [additionalServices, variant, tryItFirstCampaigns]\n );\n\n return useMemo(\n () => ({\n additionalServices,\n engravingAvailable,\n tryItFirstAvailable,\n }),\n [additionalServices, engravingAvailable, tryItFirstAvailable]\n );\n};\n","import{_ as e}from\"./_rollupPluginBabelHelpers-fc5c8eb1.js\";import*as r from\"react\";import{Icon as t}from\"./index.js\";import\"./ThemeContext.js\";import\"styled-components\";var o=function(o){return r.createElement(t,e({},o,{viewBox:\"0 0 124.72 124.72\"}),r.createElement(\"circle\",{fill:\"#f5f5f5\",cx:\"62.36\",cy:\"62.36\",r:\"62.36\"}),r.createElement(\"polygon\",{fill:\"currentColor\",points:\"95.82 62.36 45.41 33.26 45.41 91.47 95.82 62.36\"}))};export{o as PlayCircleIcon};\n//# sourceMappingURL=PlayCircleIcon.js.map\n","import styled, { css } from 'styled-components';\n\nimport { breakpoints, theme } from '@notino/react-styleguide';\n\nexport const SliderWrapper = styled.div`\n margin-top: 1.25rem;\n padding: 0 1.5625rem;\n height: 4.375rem;\n display: none;\n\n @media (min-width: ${breakpoints.md}) {\n display: block;\n }\n\n .slick-list {\n min-height: 4.6875rem;\n }\n`;\n\nconst commonImgStyles = css`\n max-width: 4.375rem !important;\n max-height: 4.375rem !important;\n vertical-align: bottom;\n width: auto !important;\n height: auto !important;\n overflow: hidden;\n`;\n\nconst newCommonImgStyles = css`\n width: 8.75rem !important;\n height: 8.75rem !important;\n object-fit: cover;\n overflow: hidden;\n border-radius: 0.25rem;\n`;\n\nexport const Img = styled.img<{ newDesign: boolean; isSelected: boolean }>`\n cursor: pointer;\n display: inline;\n ${({ newDesign }) => (newDesign ? newCommonImgStyles : commonImgStyles)}\n ${({ isSelected, newDesign }) =>\n !isSelected &&\n newDesign &&\n css`\n opacity: 0.5;\n `}\n`;\n\nexport const Div = styled.div<{ newDesign: boolean }>`\n min-width: 4.375rem;\n line-height: 1rem;\n position: relative;\n margin-bottom: 0.3125rem;\n\n cursor: pointer;\n text-align: center;\n\n ${({ newDesign }) =>\n newDesign\n ? css`\n mix-blend-mode: multiply;\n `\n : css`\n padding: 0 0.3125rem;\n background-color: ${theme.palette.basicInverse};\n `}\n`;\n\nexport const TopMostDiv = styled.div<{ newDesign: boolean }>`\n ${({ newDesign }) =>\n newDesign &&\n css`\n position: relative;\n border-radius: 0.25rem;\n background-color: ${theme.palette.neutralLighter};\n `}\n`;\n\nexport const Border = styled.div`\n position: absolute;\n left: 0;\n top: 0;\n bottom: 0;\n right: 0;\n border: 2px solid ${theme.palette.basic};\n border-radius: 0.25rem;\n`;\n\nexport const VideoIcon = styled.div`\n opacity: 0.8;\n position: absolute;\n top: 50%;\n left: 50%;\n transform: translate(-50%, -50%);\n`;\n","import * as React from 'react';\nimport { useInView } from 'react-intersection-observer';\n\nimport { Colors, PlayCircleIcon } from '@notino/react-styleguide';\n\nimport ErrorBoundary from '@components/ErrorBoundary';\nimport { RectanglePlaceholder } from '@components/Placeholders';\nimport { IImageProperties } from '@helpers/utils';\n\nimport { useNewPdDesignEnabled } from '../../hooks/useNewPdDesignEnabled';\n\nimport { Border, Div, Img, TopMostDiv, VideoIcon } from './styled';\n\nexport interface IImageProps extends IImageProperties {\n onClick?: React.MouseEventHandler;\n onMouseOver?: React.EventHandler>;\n isSelected: boolean;\n}\n\nexport const Image: React.FC = ({\n isVideo,\n onClick,\n onMouseOver,\n ...imageAttributes\n}) => {\n const newDesign = useNewPdDesignEnabled();\n const [ref, inView] = useInView({ triggerOnce: true });\n\n return (\n \n \n {imageAttributes.isSelected && newDesign && }\n\n \n {inView ? (\n
![]()
\n ) : (\n
\n )}\n {isVideo && (\n
\n \n \n )}\n
\n \n \n );\n};\n","import styled from 'styled-components';\n\nimport { breakpoints } from '@notino/react-styleguide';\n\nexport const Wrapper = styled.div`\n margin-top: 1.25rem;\n position: relative;\n overflow: hidden;\n display: none;\n\n @media (min-width: ${breakpoints.lg}) {\n margin-top: 1rem;\n display: block;\n }\n`;\n\nexport const ImageWrapper = styled.div`\n flex-basis: 6rem;\n`;\n\nexport const ScrollWrapper = styled.div`\n display: flex;\n gap: 1rem;\n overflow: auto;\n -ms-overflow-style: none;\n scrollbar-width: none;\n\n ::-webkit-scrollbar {\n display: none;\n }\n`;\n","import * as React from 'react';\n\nimport {\n ChevronLeftIcon,\n ChevronRightIcon,\n Colors,\n} from '@notino/react-styleguide';\n\nimport { IImageProperties } from '@helpers/utils';\nimport { useArrows } from '@hooks/useArrows';\nimport { Fade, Arrow } from '@hooks/useArrows/styled';\n\nimport { useProductDetailContext } from '../../ProductDetailContext';\n\nimport { Image } from './Image';\nimport { ImageWrapper, ScrollWrapper, Wrapper } from './ScrollThumbs.styles';\n\ninterface IProps {\n imageIndex: number;\n handleImageChange: (index: number) => void;\n handleImageClick?: (index: number) => React.MouseEventHandler;\n imagesAttrs: IImageProperties[];\n cypressDataValue?: string;\n}\n\nexport const ScrollThumbs = ({\n imageIndex,\n handleImageChange,\n handleImageClick,\n imagesAttrs,\n cypressDataValue,\n}: IProps) => {\n const { currentVariant } = useProductDetailContext();\n const { containerRef, scrollTo, showLeftArrow, showRightArrow } = useArrows([\n currentVariant.id,\n ]);\n\n return (\n \n {showLeftArrow && (\n <>\n scrollTo('left')} role=\"button\">\n \n \n \n >\n )}\n\n \n {imagesAttrs.map((imageAttr, index) => (\n \n handleImageChange(index)}\n />\n \n ))}\n \n\n {showRightArrow && (\n <>\n scrollTo('right')}\n role=\"button\"\n >\n \n \n \n >\n )}\n \n );\n};\n","import styled, { css } from 'styled-components';\n\nimport { breakpoints } from '@notino/react-styleguide';\n\nexport const SliderWrapper = styled.div`\n display: block;\n position: relative;\n height: 100%;\n\n .slick-track {\n display: flex;\n align-items: start;\n height: 100%;\n }\n\n .slick-list {\n overflow: hidden;\n height: 100%;\n }\n\n .slick-slider {\n height: 100%;\n }\n`;\n\ninterface IArrowWrapperProps {\n isLeft: boolean;\n}\n\nconst leftArrowCSS = css`\n left: 0;\n @media (min-width: ${breakpoints.lg}) {\n left: -1.5625rem;\n }\n`;\n\nconst rightArrowCSS = css`\n right: 0;\n @media (min-width: ${breakpoints.lg}) {\n right: -1.5625rem;\n }\n`;\n\nexport const ArrowWrapper = styled.div`\n position: absolute;\n top: 50%;\n transform: translate(0, -50%);\n cursor: pointer;\n color: transparent;\n border: none;\n outline: 0;\n background-color: rgb(255, 255, 255);\n z-index: 2;\n\n ${({ isLeft }) => (isLeft ? leftArrowCSS : rightArrowCSS)}\n`;\n","import * as React from 'react';\n\nimport {\n ChevronLeftIcon,\n Colors,\n ChevronRightIcon,\n IBasicStyledProps,\n} from '@notino/react-styleguide';\n\nimport { ArrowWrapper } from './styled';\n\ninterface IArrowProps extends IBasicStyledProps {\n direction: string;\n className?: string;\n onClick?: React.MouseEventHandler;\n}\n\nconst Arrow: React.FC = ({\n direction,\n onClick,\n className,\n ...basicStyledProps\n}) => {\n const color = onClick ? Colors.neutralDarker : Colors.neutralLight;\n const isLeft = direction === 'left';\n return (\n \n {isLeft ? (\n \n ) : (\n \n )}\n \n );\n};\n\nexport default Arrow;\n","import * as React from 'react';\nimport Slider, { Settings } from 'react-slick';\n\nimport { ReactFCC } from '@notino/react-styleguide';\n\nimport { SliderWrapper } from './components/styled';\n\nexport { Arrows } from './components';\n\nexport interface ISimpleSliderProps {\n conf: Settings;\n sliderRef?: React.LegacyRef;\n}\n\nconst SimpleSlider: ReactFCC = ({\n conf,\n children,\n sliderRef,\n}) => (\n \n \n {children}\n \n \n);\n\nexport default SimpleSlider;\n","import * as React from 'react';\nimport { LazyLoadTypes, Settings } from 'react-slick';\n\nimport Slider, { Arrows } from '@components/Slider';\nimport { IImageProperties } from '@helpers/utils';\n\nimport { Image } from './Image';\nimport { SliderWrapper } from './styled';\n\ninterface IThumbsImagesProps {\n imageIndex: number;\n handleImageChange: (index: number) => void;\n handleImageClick?: (index: number) => React.MouseEventHandler;\n imagesAttrs: IImageProperties[];\n config: Settings;\n cypressDataValue?: string;\n}\n\nexport const Thumbs: React.FC = React.memo(\n ({\n imagesAttrs,\n config,\n handleImageChange,\n handleImageClick,\n imageIndex,\n cypressDataValue = '',\n }) => {\n const imagesLen = imagesAttrs.length;\n\n const sliderConfig = React.useMemo(\n () => ({\n cssEase: 'ease-in-out',\n infinite: false,\n lazyLoad: 'ondemand' as LazyLoadTypes,\n prevArrow:\n imagesLen > config.slidesToShow ? : null,\n nextArrow:\n imagesLen > config.slidesToShow ? : null,\n arrows: imagesLen > config.slidesToShow,\n ...config,\n }),\n [imagesLen, config]\n );\n\n return (\n \n \n {imagesAttrs.map((imageAttr, index) => {\n const onMouseOver = () => handleImageChange(index);\n return (\n \n );\n })}\n \n \n );\n }\n);\n","import { Settings } from 'react-slick';\n\nexport const THUMBS_SLIDER_CONFIG: Settings = {\n slidesToShow: 10,\n slidesToScroll: 10,\n responsive: [\n {\n breakpoint: 992,\n settings: {\n slidesToShow: 8,\n slidesToScroll: 8,\n },\n },\n {\n breakpoint: 768,\n settings: {\n slidesToShow: 6,\n slidesToScroll: 6,\n },\n },\n ],\n};\n","import styled from 'styled-components';\n\nimport { breakpoints, Modal } from '@notino/react-styleguide';\n\nexport const Wrapper = styled.div`\n display: flex;\n justify-content: center;\n align-items: center;\n overflow: hidden;\n height: 70vh;\n`;\n\nexport const Image = styled.img`\n max-width: 100%;\n height: auto;\n max-height: 100%;\n object-fit: contain;\n`;\n\nexport const ThumbsWrapper = styled.div`\n width: 100%;\n padding: 1rem 2.5rem 1.5625rem;\n`;\n\nexport const VideoSizeContainer = styled.div`\n flex-basis: 100vh;\n`;\n\nexport const PlayerWrapper = styled.div`\n position: relative;\n padding-bottom: 56.25%; /* 16:9 */\n width: 100%;\n height: 0;\n iframe {\n position: absolute;\n width: 100%;\n height: 100%;\n left: 0;\n top: 0;\n @media (min-width: ${breakpoints.md}) {\n background: ${(props) => props.theme.palette.neutralLighter};\n }\n }\n`;\n\nexport const GalleryModal = styled(Modal)`\n position: absolute;\n height: 100%;\n`;\n","import * as React from 'react';\n\nimport { useNewPdDesignEnabled } from '@containers/ProductDetailContainer/ProductDetail/hooks/useNewPdDesignEnabled';\nimport { ScrollThumbs } from '@containers/ProductDetailContainer/ProductDetail/ProductImageGallery/Thumbs/ScrollThumbs';\nimport { IImageProperties } from '@helpers/utils';\n\nimport { Thumbs } from '../../containers/ProductDetailContainer/ProductDetail/ProductImageGallery/Thumbs';\nimport { youtubeIframe } from '../../containers/ProductDetailContainer/utils';\n\nimport { THUMBS_SLIDER_CONFIG } from './constants';\nimport {\n Wrapper,\n PlayerWrapper,\n ThumbsWrapper,\n VideoSizeContainer,\n Image,\n} from './styled';\n\ninterface IModalGalleryProps {\n onClose: () => void;\n imageIndex: number;\n images: IImageProperties[];\n thumbImages: IImageProperties[];\n videos?: string[];\n}\n\nexport const ModalGallery: React.FC = ({\n imageIndex = 0,\n thumbImages,\n videos,\n onClose,\n images,\n}) => {\n const newDesign = useNewPdDesignEnabled();\n const [mediaIndex, setMediaIndex] = React.useState(imageIndex);\n const mediaCount = thumbImages.length;\n\n React.useEffect(() => {\n setMediaIndex(imageIndex);\n }, [imageIndex]);\n\n React.useEffect(() => {\n const goToNext = () => {\n setMediaIndex((prevMediaIndex) =>\n Math.min(mediaCount > 1 ? mediaCount - 1 : 0, prevMediaIndex + 1)\n );\n };\n\n const goToPrev = () => {\n setMediaIndex((prevMediaIndex) => Math.max(0, prevMediaIndex - 1));\n };\n\n const handleKeyPress = (event) => {\n if (event.keyCode === 37) {\n goToPrev();\n } else if (event.keyCode === 39) {\n goToNext();\n } else if (event.keyCode === 27) {\n onClose();\n }\n };\n\n window.addEventListener('keydown', handleKeyPress);\n return () => {\n window.removeEventListener('keydown', handleKeyPress);\n };\n }, [mediaCount, onClose]);\n\n const getVideoId = React.useCallback(\n (index: number): string => {\n const videoIndex = index - images.length;\n return videoIndex < 0 ? '' : videos[videoIndex];\n },\n [images, videos]\n );\n\n const image = images[mediaIndex];\n const videoId = getVideoId(mediaIndex);\n\n return (\n <>\n \n {videoId ? (\n \n \n \n ) : (\n \n )}\n \n\n {thumbImages.length > 1 && !newDesign && (\n \n \n \n )}\n\n {thumbImages.length > 1 && newDesign && (\n \n \n \n )}\n >\n );\n};\n","import { Settings } from 'react-slick';\n\nexport const THUMBS_SLIDER_CONFIG: Settings = {\n slidesToShow: 4,\n slidesToScroll: 4,\n responsive: [\n {\n breakpoint: 992,\n settings: {\n slidesToShow: 3,\n slidesToScroll: 3,\n },\n },\n ],\n};\n","function clamp(v, min, max) {\n return Math.max(min, Math.min(v, max));\n}\nconst V = {\n toVector(v, fallback) {\n if (v === undefined) v = fallback;\n return Array.isArray(v) ? v : [v, v];\n },\n add(v1, v2) {\n return [v1[0] + v2[0], v1[1] + v2[1]];\n },\n sub(v1, v2) {\n return [v1[0] - v2[0], v1[1] - v2[1]];\n },\n addTo(v1, v2) {\n v1[0] += v2[0];\n v1[1] += v2[1];\n },\n subTo(v1, v2) {\n v1[0] -= v2[0];\n v1[1] -= v2[1];\n }\n};\nfunction rubberband(distance, dimension, constant) {\n if (dimension === 0 || Math.abs(dimension) === Infinity) return Math.pow(distance, constant * 5);\n return distance * dimension * constant / (dimension + constant * distance);\n}\nfunction rubberbandIfOutOfBounds(position, min, max, constant = 0.15) {\n if (constant === 0) return clamp(position, min, max);\n if (position < min) return -rubberband(min - position, max - min, constant) + min;\n if (position > max) return +rubberband(position - max, max - min, constant) + max;\n return position;\n}\nfunction computeRubberband(bounds, [Vx, Vy], [Rx, Ry]) {\n const [[X0, X1], [Y0, Y1]] = bounds;\n return [rubberbandIfOutOfBounds(Vx, X0, X1, Rx), rubberbandIfOutOfBounds(Vy, Y0, Y1, Ry)];\n}\n\nexport { V, computeRubberband as c, rubberbandIfOutOfBounds as r };\n","import { V, c as computeRubberband } from './maths-0ab39ae9.esm.js';\n\nfunction _toPrimitive(input, hint) {\n if (typeof input !== \"object\" || input === null) return input;\n var prim = input[Symbol.toPrimitive];\n if (prim !== undefined) {\n var res = prim.call(input, hint || \"default\");\n if (typeof res !== \"object\") return res;\n throw new TypeError(\"@@toPrimitive must return a primitive value.\");\n }\n return (hint === \"string\" ? String : Number)(input);\n}\n\nfunction _toPropertyKey(arg) {\n var key = _toPrimitive(arg, \"string\");\n return typeof key === \"symbol\" ? key : String(key);\n}\n\nfunction _defineProperty(obj, key, value) {\n key = _toPropertyKey(key);\n if (key in obj) {\n Object.defineProperty(obj, key, {\n value: value,\n enumerable: true,\n configurable: true,\n writable: true\n });\n } else {\n obj[key] = value;\n }\n return obj;\n}\n\nfunction ownKeys(object, enumerableOnly) {\n var keys = Object.keys(object);\n if (Object.getOwnPropertySymbols) {\n var symbols = Object.getOwnPropertySymbols(object);\n enumerableOnly && (symbols = symbols.filter(function (sym) {\n return Object.getOwnPropertyDescriptor(object, sym).enumerable;\n })), keys.push.apply(keys, symbols);\n }\n return keys;\n}\nfunction _objectSpread2(target) {\n for (var i = 1; i < arguments.length; i++) {\n var source = null != arguments[i] ? arguments[i] : {};\n i % 2 ? ownKeys(Object(source), !0).forEach(function (key) {\n _defineProperty(target, key, source[key]);\n }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(target, Object.getOwnPropertyDescriptors(source)) : ownKeys(Object(source)).forEach(function (key) {\n Object.defineProperty(target, key, Object.getOwnPropertyDescriptor(source, key));\n });\n }\n return target;\n}\n\nconst EVENT_TYPE_MAP = {\n pointer: {\n start: 'down',\n change: 'move',\n end: 'up'\n },\n mouse: {\n start: 'down',\n change: 'move',\n end: 'up'\n },\n touch: {\n start: 'start',\n change: 'move',\n end: 'end'\n },\n gesture: {\n start: 'start',\n change: 'change',\n end: 'end'\n }\n};\nfunction capitalize(string) {\n if (!string) return '';\n return string[0].toUpperCase() + string.slice(1);\n}\nconst actionsWithoutCaptureSupported = ['enter', 'leave'];\nfunction hasCapture(capture = false, actionKey) {\n return capture && !actionsWithoutCaptureSupported.includes(actionKey);\n}\nfunction toHandlerProp(device, action = '', capture = false) {\n const deviceProps = EVENT_TYPE_MAP[device];\n const actionKey = deviceProps ? deviceProps[action] || action : action;\n return 'on' + capitalize(device) + capitalize(actionKey) + (hasCapture(capture, actionKey) ? 'Capture' : '');\n}\nconst pointerCaptureEvents = ['gotpointercapture', 'lostpointercapture'];\nfunction parseProp(prop) {\n let eventKey = prop.substring(2).toLowerCase();\n const passive = !!~eventKey.indexOf('passive');\n if (passive) eventKey = eventKey.replace('passive', '');\n const captureKey = pointerCaptureEvents.includes(eventKey) ? 'capturecapture' : 'capture';\n const capture = !!~eventKey.indexOf(captureKey);\n if (capture) eventKey = eventKey.replace('capture', '');\n return {\n device: eventKey,\n capture,\n passive\n };\n}\nfunction toDomEventType(device, action = '') {\n const deviceProps = EVENT_TYPE_MAP[device];\n const actionKey = deviceProps ? deviceProps[action] || action : action;\n return device + actionKey;\n}\nfunction isTouch(event) {\n return 'touches' in event;\n}\nfunction getPointerType(event) {\n if (isTouch(event)) return 'touch';\n if ('pointerType' in event) return event.pointerType;\n return 'mouse';\n}\nfunction getCurrentTargetTouchList(event) {\n return Array.from(event.touches).filter(e => {\n var _event$currentTarget, _event$currentTarget$;\n return e.target === event.currentTarget || ((_event$currentTarget = event.currentTarget) === null || _event$currentTarget === void 0 ? void 0 : (_event$currentTarget$ = _event$currentTarget.contains) === null || _event$currentTarget$ === void 0 ? void 0 : _event$currentTarget$.call(_event$currentTarget, e.target));\n });\n}\nfunction getTouchList(event) {\n return event.type === 'touchend' || event.type === 'touchcancel' ? event.changedTouches : event.targetTouches;\n}\nfunction getValueEvent(event) {\n return isTouch(event) ? getTouchList(event)[0] : event;\n}\nfunction distanceAngle(P1, P2) {\n try {\n const dx = P2.clientX - P1.clientX;\n const dy = P2.clientY - P1.clientY;\n const cx = (P2.clientX + P1.clientX) / 2;\n const cy = (P2.clientY + P1.clientY) / 2;\n const distance = Math.hypot(dx, dy);\n const angle = -(Math.atan2(dx, dy) * 180) / Math.PI;\n const origin = [cx, cy];\n return {\n angle,\n distance,\n origin\n };\n } catch (_unused) {}\n return null;\n}\nfunction touchIds(event) {\n return getCurrentTargetTouchList(event).map(touch => touch.identifier);\n}\nfunction touchDistanceAngle(event, ids) {\n const [P1, P2] = Array.from(event.touches).filter(touch => ids.includes(touch.identifier));\n return distanceAngle(P1, P2);\n}\nfunction pointerId(event) {\n const valueEvent = getValueEvent(event);\n return isTouch(event) ? valueEvent.identifier : valueEvent.pointerId;\n}\nfunction pointerValues(event) {\n const valueEvent = getValueEvent(event);\n return [valueEvent.clientX, valueEvent.clientY];\n}\nconst LINE_HEIGHT = 40;\nconst PAGE_HEIGHT = 800;\nfunction wheelValues(event) {\n let {\n deltaX,\n deltaY,\n deltaMode\n } = event;\n if (deltaMode === 1) {\n deltaX *= LINE_HEIGHT;\n deltaY *= LINE_HEIGHT;\n } else if (deltaMode === 2) {\n deltaX *= PAGE_HEIGHT;\n deltaY *= PAGE_HEIGHT;\n }\n return [deltaX, deltaY];\n}\nfunction scrollValues(event) {\n var _ref, _ref2;\n const {\n scrollX,\n scrollY,\n scrollLeft,\n scrollTop\n } = event.currentTarget;\n return [(_ref = scrollX !== null && scrollX !== void 0 ? scrollX : scrollLeft) !== null && _ref !== void 0 ? _ref : 0, (_ref2 = scrollY !== null && scrollY !== void 0 ? scrollY : scrollTop) !== null && _ref2 !== void 0 ? _ref2 : 0];\n}\nfunction getEventDetails(event) {\n const payload = {};\n if ('buttons' in event) payload.buttons = event.buttons;\n if ('shiftKey' in event) {\n const {\n shiftKey,\n altKey,\n metaKey,\n ctrlKey\n } = event;\n Object.assign(payload, {\n shiftKey,\n altKey,\n metaKey,\n ctrlKey\n });\n }\n return payload;\n}\n\nfunction call(v, ...args) {\n if (typeof v === 'function') {\n return v(...args);\n } else {\n return v;\n }\n}\nfunction noop() {}\nfunction chain(...fns) {\n if (fns.length === 0) return noop;\n if (fns.length === 1) return fns[0];\n return function () {\n let result;\n for (const fn of fns) {\n result = fn.apply(this, arguments) || result;\n }\n return result;\n };\n}\nfunction assignDefault(value, fallback) {\n return Object.assign({}, fallback, value || {});\n}\n\nconst BEFORE_LAST_KINEMATICS_DELAY = 32;\nclass Engine {\n constructor(ctrl, args, key) {\n this.ctrl = ctrl;\n this.args = args;\n this.key = key;\n if (!this.state) {\n this.state = {};\n this.computeValues([0, 0]);\n this.computeInitial();\n if (this.init) this.init();\n this.reset();\n }\n }\n get state() {\n return this.ctrl.state[this.key];\n }\n set state(state) {\n this.ctrl.state[this.key] = state;\n }\n get shared() {\n return this.ctrl.state.shared;\n }\n get eventStore() {\n return this.ctrl.gestureEventStores[this.key];\n }\n get timeoutStore() {\n return this.ctrl.gestureTimeoutStores[this.key];\n }\n get config() {\n return this.ctrl.config[this.key];\n }\n get sharedConfig() {\n return this.ctrl.config.shared;\n }\n get handler() {\n return this.ctrl.handlers[this.key];\n }\n reset() {\n const {\n state,\n shared,\n ingKey,\n args\n } = this;\n shared[ingKey] = state._active = state.active = state._blocked = state._force = false;\n state._step = [false, false];\n state.intentional = false;\n state._movement = [0, 0];\n state._distance = [0, 0];\n state._direction = [0, 0];\n state._delta = [0, 0];\n state._bounds = [[-Infinity, Infinity], [-Infinity, Infinity]];\n state.args = args;\n state.axis = undefined;\n state.memo = undefined;\n state.elapsedTime = state.timeDelta = 0;\n state.direction = [0, 0];\n state.distance = [0, 0];\n state.overflow = [0, 0];\n state._movementBound = [false, false];\n state.velocity = [0, 0];\n state.movement = [0, 0];\n state.delta = [0, 0];\n state.timeStamp = 0;\n }\n start(event) {\n const state = this.state;\n const config = this.config;\n if (!state._active) {\n this.reset();\n this.computeInitial();\n state._active = true;\n state.target = event.target;\n state.currentTarget = event.currentTarget;\n state.lastOffset = config.from ? call(config.from, state) : state.offset;\n state.offset = state.lastOffset;\n state.startTime = state.timeStamp = event.timeStamp;\n }\n }\n computeValues(values) {\n const state = this.state;\n state._values = values;\n state.values = this.config.transform(values);\n }\n computeInitial() {\n const state = this.state;\n state._initial = state._values;\n state.initial = state.values;\n }\n compute(event) {\n const {\n state,\n config,\n shared\n } = this;\n state.args = this.args;\n let dt = 0;\n if (event) {\n state.event = event;\n if (config.preventDefault && event.cancelable) state.event.preventDefault();\n state.type = event.type;\n shared.touches = this.ctrl.pointerIds.size || this.ctrl.touchIds.size;\n shared.locked = !!document.pointerLockElement;\n Object.assign(shared, getEventDetails(event));\n shared.down = shared.pressed = shared.buttons % 2 === 1 || shared.touches > 0;\n dt = event.timeStamp - state.timeStamp;\n state.timeStamp = event.timeStamp;\n state.elapsedTime = state.timeStamp - state.startTime;\n }\n if (state._active) {\n const _absoluteDelta = state._delta.map(Math.abs);\n V.addTo(state._distance, _absoluteDelta);\n }\n if (this.axisIntent) this.axisIntent(event);\n const [_m0, _m1] = state._movement;\n const [t0, t1] = config.threshold;\n const {\n _step,\n values\n } = state;\n if (config.hasCustomTransform) {\n if (_step[0] === false) _step[0] = Math.abs(_m0) >= t0 && values[0];\n if (_step[1] === false) _step[1] = Math.abs(_m1) >= t1 && values[1];\n } else {\n if (_step[0] === false) _step[0] = Math.abs(_m0) >= t0 && Math.sign(_m0) * t0;\n if (_step[1] === false) _step[1] = Math.abs(_m1) >= t1 && Math.sign(_m1) * t1;\n }\n state.intentional = _step[0] !== false || _step[1] !== false;\n if (!state.intentional) return;\n const movement = [0, 0];\n if (config.hasCustomTransform) {\n const [v0, v1] = values;\n movement[0] = _step[0] !== false ? v0 - _step[0] : 0;\n movement[1] = _step[1] !== false ? v1 - _step[1] : 0;\n } else {\n movement[0] = _step[0] !== false ? _m0 - _step[0] : 0;\n movement[1] = _step[1] !== false ? _m1 - _step[1] : 0;\n }\n if (this.restrictToAxis && !state._blocked) this.restrictToAxis(movement);\n const previousOffset = state.offset;\n const gestureIsActive = state._active && !state._blocked || state.active;\n if (gestureIsActive) {\n state.first = state._active && !state.active;\n state.last = !state._active && state.active;\n state.active = shared[this.ingKey] = state._active;\n if (event) {\n if (state.first) {\n if ('bounds' in config) state._bounds = call(config.bounds, state);\n if (this.setup) this.setup();\n }\n state.movement = movement;\n this.computeOffset();\n }\n }\n const [ox, oy] = state.offset;\n const [[x0, x1], [y0, y1]] = state._bounds;\n state.overflow = [ox < x0 ? -1 : ox > x1 ? 1 : 0, oy < y0 ? -1 : oy > y1 ? 1 : 0];\n state._movementBound[0] = state.overflow[0] ? state._movementBound[0] === false ? state._movement[0] : state._movementBound[0] : false;\n state._movementBound[1] = state.overflow[1] ? state._movementBound[1] === false ? state._movement[1] : state._movementBound[1] : false;\n const rubberband = state._active ? config.rubberband || [0, 0] : [0, 0];\n state.offset = computeRubberband(state._bounds, state.offset, rubberband);\n state.delta = V.sub(state.offset, previousOffset);\n this.computeMovement();\n if (gestureIsActive && (!state.last || dt > BEFORE_LAST_KINEMATICS_DELAY)) {\n state.delta = V.sub(state.offset, previousOffset);\n const absoluteDelta = state.delta.map(Math.abs);\n V.addTo(state.distance, absoluteDelta);\n state.direction = state.delta.map(Math.sign);\n state._direction = state._delta.map(Math.sign);\n if (!state.first && dt > 0) {\n state.velocity = [absoluteDelta[0] / dt, absoluteDelta[1] / dt];\n state.timeDelta = dt;\n }\n }\n }\n emit() {\n const state = this.state;\n const shared = this.shared;\n const config = this.config;\n if (!state._active) this.clean();\n if ((state._blocked || !state.intentional) && !state._force && !config.triggerAllEvents) return;\n const memo = this.handler(_objectSpread2(_objectSpread2(_objectSpread2({}, shared), state), {}, {\n [this.aliasKey]: state.values\n }));\n if (memo !== undefined) state.memo = memo;\n }\n clean() {\n this.eventStore.clean();\n this.timeoutStore.clean();\n }\n}\n\nfunction selectAxis([dx, dy], threshold) {\n const absDx = Math.abs(dx);\n const absDy = Math.abs(dy);\n if (absDx > absDy && absDx > threshold) {\n return 'x';\n }\n if (absDy > absDx && absDy > threshold) {\n return 'y';\n }\n return undefined;\n}\nclass CoordinatesEngine extends Engine {\n constructor(...args) {\n super(...args);\n _defineProperty(this, \"aliasKey\", 'xy');\n }\n reset() {\n super.reset();\n this.state.axis = undefined;\n }\n init() {\n this.state.offset = [0, 0];\n this.state.lastOffset = [0, 0];\n }\n computeOffset() {\n this.state.offset = V.add(this.state.lastOffset, this.state.movement);\n }\n computeMovement() {\n this.state.movement = V.sub(this.state.offset, this.state.lastOffset);\n }\n axisIntent(event) {\n const state = this.state;\n const config = this.config;\n if (!state.axis && event) {\n const threshold = typeof config.axisThreshold === 'object' ? config.axisThreshold[getPointerType(event)] : config.axisThreshold;\n state.axis = selectAxis(state._movement, threshold);\n }\n state._blocked = (config.lockDirection || !!config.axis) && !state.axis || !!config.axis && config.axis !== state.axis;\n }\n restrictToAxis(v) {\n if (this.config.axis || this.config.lockDirection) {\n switch (this.state.axis) {\n case 'x':\n v[1] = 0;\n break;\n case 'y':\n v[0] = 0;\n break;\n }\n }\n }\n}\n\nconst identity = v => v;\nconst DEFAULT_RUBBERBAND = 0.15;\nconst commonConfigResolver = {\n enabled(value = true) {\n return value;\n },\n eventOptions(value, _k, config) {\n return _objectSpread2(_objectSpread2({}, config.shared.eventOptions), value);\n },\n preventDefault(value = false) {\n return value;\n },\n triggerAllEvents(value = false) {\n return value;\n },\n rubberband(value = 0) {\n switch (value) {\n case true:\n return [DEFAULT_RUBBERBAND, DEFAULT_RUBBERBAND];\n case false:\n return [0, 0];\n default:\n return V.toVector(value);\n }\n },\n from(value) {\n if (typeof value === 'function') return value;\n if (value != null) return V.toVector(value);\n },\n transform(value, _k, config) {\n const transform = value || config.shared.transform;\n this.hasCustomTransform = !!transform;\n if (process.env.NODE_ENV === 'development') {\n const originalTransform = transform || identity;\n return v => {\n const r = originalTransform(v);\n if (!isFinite(r[0]) || !isFinite(r[1])) {\n console.warn(`[@use-gesture]: config.transform() must produce a valid result, but it was: [${r[0]},${[1]}]`);\n }\n return r;\n };\n }\n return transform || identity;\n },\n threshold(value) {\n return V.toVector(value, 0);\n }\n};\nif (process.env.NODE_ENV === 'development') {\n Object.assign(commonConfigResolver, {\n domTarget(value) {\n if (value !== undefined) {\n throw Error(`[@use-gesture]: \\`domTarget\\` option has been renamed to \\`target\\`.`);\n }\n return NaN;\n },\n lockDirection(value) {\n if (value !== undefined) {\n throw Error(`[@use-gesture]: \\`lockDirection\\` option has been merged with \\`axis\\`. Use it as in \\`{ axis: 'lock' }\\``);\n }\n return NaN;\n },\n initial(value) {\n if (value !== undefined) {\n throw Error(`[@use-gesture]: \\`initial\\` option has been renamed to \\`from\\`.`);\n }\n return NaN;\n }\n });\n}\n\nconst DEFAULT_AXIS_THRESHOLD = 0;\nconst coordinatesConfigResolver = _objectSpread2(_objectSpread2({}, commonConfigResolver), {}, {\n axis(_v, _k, {\n axis\n }) {\n this.lockDirection = axis === 'lock';\n if (!this.lockDirection) return axis;\n },\n axisThreshold(value = DEFAULT_AXIS_THRESHOLD) {\n return value;\n },\n bounds(value = {}) {\n if (typeof value === 'function') {\n return state => coordinatesConfigResolver.bounds(value(state));\n }\n if ('current' in value) {\n return () => value.current;\n }\n if (typeof HTMLElement === 'function' && value instanceof HTMLElement) {\n return value;\n }\n const {\n left = -Infinity,\n right = Infinity,\n top = -Infinity,\n bottom = Infinity\n } = value;\n return [[left, right], [top, bottom]];\n }\n});\n\nconst KEYS_DELTA_MAP = {\n ArrowRight: (displacement, factor = 1) => [displacement * factor, 0],\n ArrowLeft: (displacement, factor = 1) => [-1 * displacement * factor, 0],\n ArrowUp: (displacement, factor = 1) => [0, -1 * displacement * factor],\n ArrowDown: (displacement, factor = 1) => [0, displacement * factor]\n};\nclass DragEngine extends CoordinatesEngine {\n constructor(...args) {\n super(...args);\n _defineProperty(this, \"ingKey\", 'dragging');\n }\n reset() {\n super.reset();\n const state = this.state;\n state._pointerId = undefined;\n state._pointerActive = false;\n state._keyboardActive = false;\n state._preventScroll = false;\n state._delayed = false;\n state.swipe = [0, 0];\n state.tap = false;\n state.canceled = false;\n state.cancel = this.cancel.bind(this);\n }\n setup() {\n const state = this.state;\n if (state._bounds instanceof HTMLElement) {\n const boundRect = state._bounds.getBoundingClientRect();\n const targetRect = state.currentTarget.getBoundingClientRect();\n const _bounds = {\n left: boundRect.left - targetRect.left + state.offset[0],\n right: boundRect.right - targetRect.right + state.offset[0],\n top: boundRect.top - targetRect.top + state.offset[1],\n bottom: boundRect.bottom - targetRect.bottom + state.offset[1]\n };\n state._bounds = coordinatesConfigResolver.bounds(_bounds);\n }\n }\n cancel() {\n const state = this.state;\n if (state.canceled) return;\n state.canceled = true;\n state._active = false;\n setTimeout(() => {\n this.compute();\n this.emit();\n }, 0);\n }\n setActive() {\n this.state._active = this.state._pointerActive || this.state._keyboardActive;\n }\n clean() {\n this.pointerClean();\n this.state._pointerActive = false;\n this.state._keyboardActive = false;\n super.clean();\n }\n pointerDown(event) {\n const config = this.config;\n const state = this.state;\n if (event.buttons != null && (Array.isArray(config.pointerButtons) ? !config.pointerButtons.includes(event.buttons) : config.pointerButtons !== -1 && config.pointerButtons !== event.buttons)) return;\n const ctrlIds = this.ctrl.setEventIds(event);\n if (config.pointerCapture) {\n event.target.setPointerCapture(event.pointerId);\n }\n if (ctrlIds && ctrlIds.size > 1 && state._pointerActive) return;\n this.start(event);\n this.setupPointer(event);\n state._pointerId = pointerId(event);\n state._pointerActive = true;\n this.computeValues(pointerValues(event));\n this.computeInitial();\n if (config.preventScrollAxis && getPointerType(event) !== 'mouse') {\n state._active = false;\n this.setupScrollPrevention(event);\n } else if (config.delay > 0) {\n this.setupDelayTrigger(event);\n if (config.triggerAllEvents) {\n this.compute(event);\n this.emit();\n }\n } else {\n this.startPointerDrag(event);\n }\n }\n startPointerDrag(event) {\n const state = this.state;\n state._active = true;\n state._preventScroll = true;\n state._delayed = false;\n this.compute(event);\n this.emit();\n }\n pointerMove(event) {\n const state = this.state;\n const config = this.config;\n if (!state._pointerActive) return;\n if (state.type === event.type && event.timeStamp === state.timeStamp) return;\n const id = pointerId(event);\n if (state._pointerId !== undefined && id !== state._pointerId) return;\n const _values = pointerValues(event);\n if (document.pointerLockElement === event.target) {\n state._delta = [event.movementX, event.movementY];\n } else {\n state._delta = V.sub(_values, state._values);\n this.computeValues(_values);\n }\n V.addTo(state._movement, state._delta);\n this.compute(event);\n if (state._delayed && state.intentional) {\n this.timeoutStore.remove('dragDelay');\n state.active = false;\n this.startPointerDrag(event);\n return;\n }\n if (config.preventScrollAxis && !state._preventScroll) {\n if (state.axis) {\n if (state.axis === config.preventScrollAxis || config.preventScrollAxis === 'xy') {\n state._active = false;\n this.clean();\n return;\n } else {\n this.timeoutStore.remove('startPointerDrag');\n this.startPointerDrag(event);\n return;\n }\n } else {\n return;\n }\n }\n this.emit();\n }\n pointerUp(event) {\n this.ctrl.setEventIds(event);\n try {\n if (this.config.pointerCapture && event.target.hasPointerCapture(event.pointerId)) {\n ;\n event.target.releasePointerCapture(event.pointerId);\n }\n } catch (_unused) {\n if (process.env.NODE_ENV === 'development') {\n console.warn(`[@use-gesture]: If you see this message, it's likely that you're using an outdated version of \\`@react-three/fiber\\`. \\n\\nPlease upgrade to the latest version.`);\n }\n }\n const state = this.state;\n const config = this.config;\n if (!state._active || !state._pointerActive) return;\n const id = pointerId(event);\n if (state._pointerId !== undefined && id !== state._pointerId) return;\n this.state._pointerActive = false;\n this.setActive();\n this.compute(event);\n const [dx, dy] = state._distance;\n state.tap = dx <= config.tapsThreshold && dy <= config.tapsThreshold;\n if (state.tap && config.filterTaps) {\n state._force = true;\n } else {\n const [_dx, _dy] = state._delta;\n const [_mx, _my] = state._movement;\n const [svx, svy] = config.swipe.velocity;\n const [sx, sy] = config.swipe.distance;\n const sdt = config.swipe.duration;\n if (state.elapsedTime < sdt) {\n const _vx = Math.abs(_dx / state.timeDelta);\n const _vy = Math.abs(_dy / state.timeDelta);\n if (_vx > svx && Math.abs(_mx) > sx) state.swipe[0] = Math.sign(_dx);\n if (_vy > svy && Math.abs(_my) > sy) state.swipe[1] = Math.sign(_dy);\n }\n }\n this.emit();\n }\n pointerClick(event) {\n if (!this.state.tap && event.detail > 0) {\n event.preventDefault();\n event.stopPropagation();\n }\n }\n setupPointer(event) {\n const config = this.config;\n const device = config.device;\n if (process.env.NODE_ENV === 'development') {\n try {\n if (device === 'pointer' && config.preventScrollDelay === undefined) {\n const currentTarget = 'uv' in event ? event.sourceEvent.currentTarget : event.currentTarget;\n const style = window.getComputedStyle(currentTarget);\n if (style.touchAction === 'auto') {\n console.warn(`[@use-gesture]: The drag target has its \\`touch-action\\` style property set to \\`auto\\`. It is recommended to add \\`touch-action: 'none'\\` so that the drag gesture behaves correctly on touch-enabled devices. For more information read this: https://use-gesture.netlify.app/docs/extras/#touch-action.\\n\\nThis message will only show in development mode. It won't appear in production. If this is intended, you can ignore it.`, currentTarget);\n }\n }\n } catch (_unused2) {}\n }\n if (config.pointerLock) {\n event.currentTarget.requestPointerLock();\n }\n if (!config.pointerCapture) {\n this.eventStore.add(this.sharedConfig.window, device, 'change', this.pointerMove.bind(this));\n this.eventStore.add(this.sharedConfig.window, device, 'end', this.pointerUp.bind(this));\n this.eventStore.add(this.sharedConfig.window, device, 'cancel', this.pointerUp.bind(this));\n }\n }\n pointerClean() {\n if (this.config.pointerLock && document.pointerLockElement === this.state.currentTarget) {\n document.exitPointerLock();\n }\n }\n preventScroll(event) {\n if (this.state._preventScroll && event.cancelable) {\n event.preventDefault();\n }\n }\n setupScrollPrevention(event) {\n this.state._preventScroll = false;\n persistEvent(event);\n const remove = this.eventStore.add(this.sharedConfig.window, 'touch', 'change', this.preventScroll.bind(this), {\n passive: false\n });\n this.eventStore.add(this.sharedConfig.window, 'touch', 'end', remove);\n this.eventStore.add(this.sharedConfig.window, 'touch', 'cancel', remove);\n this.timeoutStore.add('startPointerDrag', this.startPointerDrag.bind(this), this.config.preventScrollDelay, event);\n }\n setupDelayTrigger(event) {\n this.state._delayed = true;\n this.timeoutStore.add('dragDelay', () => {\n this.state._step = [0, 0];\n this.startPointerDrag(event);\n }, this.config.delay);\n }\n keyDown(event) {\n const deltaFn = KEYS_DELTA_MAP[event.key];\n if (deltaFn) {\n const state = this.state;\n const factor = event.shiftKey ? 10 : event.altKey ? 0.1 : 1;\n this.start(event);\n state._delta = deltaFn(this.config.keyboardDisplacement, factor);\n state._keyboardActive = true;\n V.addTo(state._movement, state._delta);\n this.compute(event);\n this.emit();\n }\n }\n keyUp(event) {\n if (!(event.key in KEYS_DELTA_MAP)) return;\n this.state._keyboardActive = false;\n this.setActive();\n this.compute(event);\n this.emit();\n }\n bind(bindFunction) {\n const device = this.config.device;\n bindFunction(device, 'start', this.pointerDown.bind(this));\n if (this.config.pointerCapture) {\n bindFunction(device, 'change', this.pointerMove.bind(this));\n bindFunction(device, 'end', this.pointerUp.bind(this));\n bindFunction(device, 'cancel', this.pointerUp.bind(this));\n bindFunction('lostPointerCapture', '', this.pointerUp.bind(this));\n }\n if (this.config.keys) {\n bindFunction('key', 'down', this.keyDown.bind(this));\n bindFunction('key', 'up', this.keyUp.bind(this));\n }\n if (this.config.filterTaps) {\n bindFunction('click', '', this.pointerClick.bind(this), {\n capture: true,\n passive: false\n });\n }\n }\n}\nfunction persistEvent(event) {\n 'persist' in event && typeof event.persist === 'function' && event.persist();\n}\n\nconst isBrowser = typeof window !== 'undefined' && window.document && window.document.createElement;\nfunction supportsTouchEvents() {\n return isBrowser && 'ontouchstart' in window;\n}\nfunction isTouchScreen() {\n return supportsTouchEvents() || isBrowser && window.navigator.maxTouchPoints > 1;\n}\nfunction supportsPointerEvents() {\n return isBrowser && 'onpointerdown' in window;\n}\nfunction supportsPointerLock() {\n return isBrowser && 'exitPointerLock' in window.document;\n}\nfunction supportsGestureEvents() {\n try {\n return 'constructor' in GestureEvent;\n } catch (e) {\n return false;\n }\n}\nconst SUPPORT = {\n isBrowser,\n gesture: supportsGestureEvents(),\n touch: isTouchScreen(),\n touchscreen: isTouchScreen(),\n pointer: supportsPointerEvents(),\n pointerLock: supportsPointerLock()\n};\n\nconst DEFAULT_PREVENT_SCROLL_DELAY = 250;\nconst DEFAULT_DRAG_DELAY = 180;\nconst DEFAULT_SWIPE_VELOCITY = 0.5;\nconst DEFAULT_SWIPE_DISTANCE = 50;\nconst DEFAULT_SWIPE_DURATION = 250;\nconst DEFAULT_KEYBOARD_DISPLACEMENT = 10;\nconst DEFAULT_DRAG_AXIS_THRESHOLD = {\n mouse: 0,\n touch: 0,\n pen: 8\n};\nconst dragConfigResolver = _objectSpread2(_objectSpread2({}, coordinatesConfigResolver), {}, {\n device(_v, _k, {\n pointer: {\n touch = false,\n lock = false,\n mouse = false\n } = {}\n }) {\n this.pointerLock = lock && SUPPORT.pointerLock;\n if (SUPPORT.touch && touch) return 'touch';\n if (this.pointerLock) return 'mouse';\n if (SUPPORT.pointer && !mouse) return 'pointer';\n if (SUPPORT.touch) return 'touch';\n return 'mouse';\n },\n preventScrollAxis(value, _k, {\n preventScroll\n }) {\n this.preventScrollDelay = typeof preventScroll === 'number' ? preventScroll : preventScroll || preventScroll === undefined && value ? DEFAULT_PREVENT_SCROLL_DELAY : undefined;\n if (!SUPPORT.touchscreen || preventScroll === false) return undefined;\n return value ? value : preventScroll !== undefined ? 'y' : undefined;\n },\n pointerCapture(_v, _k, {\n pointer: {\n capture = true,\n buttons = 1,\n keys = true\n } = {}\n }) {\n this.pointerButtons = buttons;\n this.keys = keys;\n return !this.pointerLock && this.device === 'pointer' && capture;\n },\n threshold(value, _k, {\n filterTaps = false,\n tapsThreshold = 3,\n axis = undefined\n }) {\n const threshold = V.toVector(value, filterTaps ? tapsThreshold : axis ? 1 : 0);\n this.filterTaps = filterTaps;\n this.tapsThreshold = tapsThreshold;\n return threshold;\n },\n swipe({\n velocity = DEFAULT_SWIPE_VELOCITY,\n distance = DEFAULT_SWIPE_DISTANCE,\n duration = DEFAULT_SWIPE_DURATION\n } = {}) {\n return {\n velocity: this.transform(V.toVector(velocity)),\n distance: this.transform(V.toVector(distance)),\n duration\n };\n },\n delay(value = 0) {\n switch (value) {\n case true:\n return DEFAULT_DRAG_DELAY;\n case false:\n return 0;\n default:\n return value;\n }\n },\n axisThreshold(value) {\n if (!value) return DEFAULT_DRAG_AXIS_THRESHOLD;\n return _objectSpread2(_objectSpread2({}, DEFAULT_DRAG_AXIS_THRESHOLD), value);\n },\n keyboardDisplacement(value = DEFAULT_KEYBOARD_DISPLACEMENT) {\n return value;\n }\n});\nif (process.env.NODE_ENV === 'development') {\n Object.assign(dragConfigResolver, {\n useTouch(value) {\n if (value !== undefined) {\n throw Error(`[@use-gesture]: \\`useTouch\\` option has been renamed to \\`pointer.touch\\`. Use it as in \\`{ pointer: { touch: true } }\\`.`);\n }\n return NaN;\n },\n experimental_preventWindowScrollY(value) {\n if (value !== undefined) {\n throw Error(`[@use-gesture]: \\`experimental_preventWindowScrollY\\` option has been renamed to \\`preventScroll\\`.`);\n }\n return NaN;\n },\n swipeVelocity(value) {\n if (value !== undefined) {\n throw Error(`[@use-gesture]: \\`swipeVelocity\\` option has been renamed to \\`swipe.velocity\\`. Use it as in \\`{ swipe: { velocity: 0.5 } }\\`.`);\n }\n return NaN;\n },\n swipeDistance(value) {\n if (value !== undefined) {\n throw Error(`[@use-gesture]: \\`swipeDistance\\` option has been renamed to \\`swipe.distance\\`. Use it as in \\`{ swipe: { distance: 50 } }\\`.`);\n }\n return NaN;\n },\n swipeDuration(value) {\n if (value !== undefined) {\n throw Error(`[@use-gesture]: \\`swipeDuration\\` option has been renamed to \\`swipe.duration\\`. Use it as in \\`{ swipe: { duration: 250 } }\\`.`);\n }\n return NaN;\n }\n });\n}\n\nfunction clampStateInternalMovementToBounds(state) {\n const [ox, oy] = state.overflow;\n const [dx, dy] = state._delta;\n const [dirx, diry] = state._direction;\n if (ox < 0 && dx > 0 && dirx < 0 || ox > 0 && dx < 0 && dirx > 0) {\n state._movement[0] = state._movementBound[0];\n }\n if (oy < 0 && dy > 0 && diry < 0 || oy > 0 && dy < 0 && diry > 0) {\n state._movement[1] = state._movementBound[1];\n }\n}\n\nconst SCALE_ANGLE_RATIO_INTENT_DEG = 30;\nconst PINCH_WHEEL_RATIO = 100;\nclass PinchEngine extends Engine {\n constructor(...args) {\n super(...args);\n _defineProperty(this, \"ingKey\", 'pinching');\n _defineProperty(this, \"aliasKey\", 'da');\n }\n init() {\n this.state.offset = [1, 0];\n this.state.lastOffset = [1, 0];\n this.state._pointerEvents = new Map();\n }\n reset() {\n super.reset();\n const state = this.state;\n state._touchIds = [];\n state.canceled = false;\n state.cancel = this.cancel.bind(this);\n state.turns = 0;\n }\n computeOffset() {\n const {\n type,\n movement,\n lastOffset\n } = this.state;\n if (type === 'wheel') {\n this.state.offset = V.add(movement, lastOffset);\n } else {\n this.state.offset = [(1 + movement[0]) * lastOffset[0], movement[1] + lastOffset[1]];\n }\n }\n computeMovement() {\n const {\n offset,\n lastOffset\n } = this.state;\n this.state.movement = [offset[0] / lastOffset[0], offset[1] - lastOffset[1]];\n }\n axisIntent() {\n const state = this.state;\n const [_m0, _m1] = state._movement;\n if (!state.axis) {\n const axisMovementDifference = Math.abs(_m0) * SCALE_ANGLE_RATIO_INTENT_DEG - Math.abs(_m1);\n if (axisMovementDifference < 0) state.axis = 'angle';else if (axisMovementDifference > 0) state.axis = 'scale';\n }\n }\n restrictToAxis(v) {\n if (this.config.lockDirection) {\n if (this.state.axis === 'scale') v[1] = 0;else if (this.state.axis === 'angle') v[0] = 0;\n }\n }\n cancel() {\n const state = this.state;\n if (state.canceled) return;\n setTimeout(() => {\n state.canceled = true;\n state._active = false;\n this.compute();\n this.emit();\n }, 0);\n }\n touchStart(event) {\n this.ctrl.setEventIds(event);\n const state = this.state;\n const ctrlTouchIds = this.ctrl.touchIds;\n if (state._active) {\n if (state._touchIds.every(id => ctrlTouchIds.has(id))) return;\n }\n if (ctrlTouchIds.size < 2) return;\n this.start(event);\n state._touchIds = Array.from(ctrlTouchIds).slice(0, 2);\n const payload = touchDistanceAngle(event, state._touchIds);\n if (!payload) return;\n this.pinchStart(event, payload);\n }\n pointerStart(event) {\n if (event.buttons != null && event.buttons % 2 !== 1) return;\n this.ctrl.setEventIds(event);\n event.target.setPointerCapture(event.pointerId);\n const state = this.state;\n const _pointerEvents = state._pointerEvents;\n const ctrlPointerIds = this.ctrl.pointerIds;\n if (state._active) {\n if (Array.from(_pointerEvents.keys()).every(id => ctrlPointerIds.has(id))) return;\n }\n if (_pointerEvents.size < 2) {\n _pointerEvents.set(event.pointerId, event);\n }\n if (state._pointerEvents.size < 2) return;\n this.start(event);\n const payload = distanceAngle(...Array.from(_pointerEvents.values()));\n if (!payload) return;\n this.pinchStart(event, payload);\n }\n pinchStart(event, payload) {\n const state = this.state;\n state.origin = payload.origin;\n this.computeValues([payload.distance, payload.angle]);\n this.computeInitial();\n this.compute(event);\n this.emit();\n }\n touchMove(event) {\n if (!this.state._active) return;\n const payload = touchDistanceAngle(event, this.state._touchIds);\n if (!payload) return;\n this.pinchMove(event, payload);\n }\n pointerMove(event) {\n const _pointerEvents = this.state._pointerEvents;\n if (_pointerEvents.has(event.pointerId)) {\n _pointerEvents.set(event.pointerId, event);\n }\n if (!this.state._active) return;\n const payload = distanceAngle(...Array.from(_pointerEvents.values()));\n if (!payload) return;\n this.pinchMove(event, payload);\n }\n pinchMove(event, payload) {\n const state = this.state;\n const prev_a = state._values[1];\n const delta_a = payload.angle - prev_a;\n let delta_turns = 0;\n if (Math.abs(delta_a) > 270) delta_turns += Math.sign(delta_a);\n this.computeValues([payload.distance, payload.angle - 360 * delta_turns]);\n state.origin = payload.origin;\n state.turns = delta_turns;\n state._movement = [state._values[0] / state._initial[0] - 1, state._values[1] - state._initial[1]];\n this.compute(event);\n this.emit();\n }\n touchEnd(event) {\n this.ctrl.setEventIds(event);\n if (!this.state._active) return;\n if (this.state._touchIds.some(id => !this.ctrl.touchIds.has(id))) {\n this.state._active = false;\n this.compute(event);\n this.emit();\n }\n }\n pointerEnd(event) {\n const state = this.state;\n this.ctrl.setEventIds(event);\n try {\n event.target.releasePointerCapture(event.pointerId);\n } catch (_unused) {}\n if (state._pointerEvents.has(event.pointerId)) {\n state._pointerEvents.delete(event.pointerId);\n }\n if (!state._active) return;\n if (state._pointerEvents.size < 2) {\n state._active = false;\n this.compute(event);\n this.emit();\n }\n }\n gestureStart(event) {\n if (event.cancelable) event.preventDefault();\n const state = this.state;\n if (state._active) return;\n this.start(event);\n this.computeValues([event.scale, event.rotation]);\n state.origin = [event.clientX, event.clientY];\n this.compute(event);\n this.emit();\n }\n gestureMove(event) {\n if (event.cancelable) event.preventDefault();\n if (!this.state._active) return;\n const state = this.state;\n this.computeValues([event.scale, event.rotation]);\n state.origin = [event.clientX, event.clientY];\n const _previousMovement = state._movement;\n state._movement = [event.scale - 1, event.rotation];\n state._delta = V.sub(state._movement, _previousMovement);\n this.compute(event);\n this.emit();\n }\n gestureEnd(event) {\n if (!this.state._active) return;\n this.state._active = false;\n this.compute(event);\n this.emit();\n }\n wheel(event) {\n const modifierKey = this.config.modifierKey;\n if (modifierKey && !event[modifierKey]) return;\n if (!this.state._active) this.wheelStart(event);else this.wheelChange(event);\n this.timeoutStore.add('wheelEnd', this.wheelEnd.bind(this));\n }\n wheelStart(event) {\n this.start(event);\n this.wheelChange(event);\n }\n wheelChange(event) {\n const isR3f = ('uv' in event);\n if (!isR3f) {\n if (event.cancelable) {\n event.preventDefault();\n }\n if (process.env.NODE_ENV === 'development' && !event.defaultPrevented) {\n console.warn(`[@use-gesture]: To properly support zoom on trackpads, try using the \\`target\\` option.\\n\\nThis message will only appear in development mode.`);\n }\n }\n const state = this.state;\n state._delta = [-wheelValues(event)[1] / PINCH_WHEEL_RATIO * state.offset[0], 0];\n V.addTo(state._movement, state._delta);\n clampStateInternalMovementToBounds(state);\n this.state.origin = [event.clientX, event.clientY];\n this.compute(event);\n this.emit();\n }\n wheelEnd() {\n if (!this.state._active) return;\n this.state._active = false;\n this.compute();\n this.emit();\n }\n bind(bindFunction) {\n const device = this.config.device;\n if (!!device) {\n bindFunction(device, 'start', this[device + 'Start'].bind(this));\n bindFunction(device, 'change', this[device + 'Move'].bind(this));\n bindFunction(device, 'end', this[device + 'End'].bind(this));\n bindFunction(device, 'cancel', this[device + 'End'].bind(this));\n }\n if (this.config.pinchOnWheel) {\n bindFunction('wheel', '', this.wheel.bind(this), {\n passive: false\n });\n }\n }\n}\n\nconst pinchConfigResolver = _objectSpread2(_objectSpread2({}, commonConfigResolver), {}, {\n device(_v, _k, {\n shared,\n pointer: {\n touch = false\n } = {}\n }) {\n const sharedConfig = shared;\n if (sharedConfig.target && !SUPPORT.touch && SUPPORT.gesture) return 'gesture';\n if (SUPPORT.touch && touch) return 'touch';\n if (SUPPORT.touchscreen) {\n if (SUPPORT.pointer) return 'pointer';\n if (SUPPORT.touch) return 'touch';\n }\n },\n bounds(_v, _k, {\n scaleBounds = {},\n angleBounds = {}\n }) {\n const _scaleBounds = state => {\n const D = assignDefault(call(scaleBounds, state), {\n min: -Infinity,\n max: Infinity\n });\n return [D.min, D.max];\n };\n const _angleBounds = state => {\n const A = assignDefault(call(angleBounds, state), {\n min: -Infinity,\n max: Infinity\n });\n return [A.min, A.max];\n };\n if (typeof scaleBounds !== 'function' && typeof angleBounds !== 'function') return [_scaleBounds(), _angleBounds()];\n return state => [_scaleBounds(state), _angleBounds(state)];\n },\n threshold(value, _k, config) {\n this.lockDirection = config.axis === 'lock';\n const threshold = V.toVector(value, this.lockDirection ? [0.1, 3] : 0);\n return threshold;\n },\n modifierKey(value) {\n if (value === undefined) return 'ctrlKey';\n return value;\n },\n pinchOnWheel(value = true) {\n return value;\n }\n});\n\nclass MoveEngine extends CoordinatesEngine {\n constructor(...args) {\n super(...args);\n _defineProperty(this, \"ingKey\", 'moving');\n }\n move(event) {\n if (this.config.mouseOnly && event.pointerType !== 'mouse') return;\n if (!this.state._active) this.moveStart(event);else this.moveChange(event);\n this.timeoutStore.add('moveEnd', this.moveEnd.bind(this));\n }\n moveStart(event) {\n this.start(event);\n this.computeValues(pointerValues(event));\n this.compute(event);\n this.computeInitial();\n this.emit();\n }\n moveChange(event) {\n if (!this.state._active) return;\n const values = pointerValues(event);\n const state = this.state;\n state._delta = V.sub(values, state._values);\n V.addTo(state._movement, state._delta);\n this.computeValues(values);\n this.compute(event);\n this.emit();\n }\n moveEnd(event) {\n if (!this.state._active) return;\n this.state._active = false;\n this.compute(event);\n this.emit();\n }\n bind(bindFunction) {\n bindFunction('pointer', 'change', this.move.bind(this));\n bindFunction('pointer', 'leave', this.moveEnd.bind(this));\n }\n}\n\nconst moveConfigResolver = _objectSpread2(_objectSpread2({}, coordinatesConfigResolver), {}, {\n mouseOnly: (value = true) => value\n});\n\nclass ScrollEngine extends CoordinatesEngine {\n constructor(...args) {\n super(...args);\n _defineProperty(this, \"ingKey\", 'scrolling');\n }\n scroll(event) {\n if (!this.state._active) this.start(event);\n this.scrollChange(event);\n this.timeoutStore.add('scrollEnd', this.scrollEnd.bind(this));\n }\n scrollChange(event) {\n if (event.cancelable) event.preventDefault();\n const state = this.state;\n const values = scrollValues(event);\n state._delta = V.sub(values, state._values);\n V.addTo(state._movement, state._delta);\n this.computeValues(values);\n this.compute(event);\n this.emit();\n }\n scrollEnd() {\n if (!this.state._active) return;\n this.state._active = false;\n this.compute();\n this.emit();\n }\n bind(bindFunction) {\n bindFunction('scroll', '', this.scroll.bind(this));\n }\n}\n\nconst scrollConfigResolver = coordinatesConfigResolver;\n\nclass WheelEngine extends CoordinatesEngine {\n constructor(...args) {\n super(...args);\n _defineProperty(this, \"ingKey\", 'wheeling');\n }\n wheel(event) {\n if (!this.state._active) this.start(event);\n this.wheelChange(event);\n this.timeoutStore.add('wheelEnd', this.wheelEnd.bind(this));\n }\n wheelChange(event) {\n const state = this.state;\n state._delta = wheelValues(event);\n V.addTo(state._movement, state._delta);\n clampStateInternalMovementToBounds(state);\n this.compute(event);\n this.emit();\n }\n wheelEnd() {\n if (!this.state._active) return;\n this.state._active = false;\n this.compute();\n this.emit();\n }\n bind(bindFunction) {\n bindFunction('wheel', '', this.wheel.bind(this));\n }\n}\n\nconst wheelConfigResolver = coordinatesConfigResolver;\n\nclass HoverEngine extends CoordinatesEngine {\n constructor(...args) {\n super(...args);\n _defineProperty(this, \"ingKey\", 'hovering');\n }\n enter(event) {\n if (this.config.mouseOnly && event.pointerType !== 'mouse') return;\n this.start(event);\n this.computeValues(pointerValues(event));\n this.compute(event);\n this.emit();\n }\n leave(event) {\n if (this.config.mouseOnly && event.pointerType !== 'mouse') return;\n const state = this.state;\n if (!state._active) return;\n state._active = false;\n const values = pointerValues(event);\n state._movement = state._delta = V.sub(values, state._values);\n this.computeValues(values);\n this.compute(event);\n state.delta = state.movement;\n this.emit();\n }\n bind(bindFunction) {\n bindFunction('pointer', 'enter', this.enter.bind(this));\n bindFunction('pointer', 'leave', this.leave.bind(this));\n }\n}\n\nconst hoverConfigResolver = _objectSpread2(_objectSpread2({}, coordinatesConfigResolver), {}, {\n mouseOnly: (value = true) => value\n});\n\nconst EngineMap = new Map();\nconst ConfigResolverMap = new Map();\nfunction registerAction(action) {\n EngineMap.set(action.key, action.engine);\n ConfigResolverMap.set(action.key, action.resolver);\n}\nconst dragAction = {\n key: 'drag',\n engine: DragEngine,\n resolver: dragConfigResolver\n};\nconst hoverAction = {\n key: 'hover',\n engine: HoverEngine,\n resolver: hoverConfigResolver\n};\nconst moveAction = {\n key: 'move',\n engine: MoveEngine,\n resolver: moveConfigResolver\n};\nconst pinchAction = {\n key: 'pinch',\n engine: PinchEngine,\n resolver: pinchConfigResolver\n};\nconst scrollAction = {\n key: 'scroll',\n engine: ScrollEngine,\n resolver: scrollConfigResolver\n};\nconst wheelAction = {\n key: 'wheel',\n engine: WheelEngine,\n resolver: wheelConfigResolver\n};\n\nexport { ConfigResolverMap as C, EngineMap as E, SUPPORT as S, _objectSpread2 as _, _defineProperty as a, touchIds as b, chain as c, toHandlerProp as d, dragAction as e, pinchAction as f, hoverAction as h, isTouch as i, moveAction as m, parseProp as p, registerAction as r, scrollAction as s, toDomEventType as t, wheelAction as w };\n","import { S as SUPPORT, C as ConfigResolverMap, _ as _objectSpread2, a as _defineProperty, t as toDomEventType, i as isTouch, b as touchIds, E as EngineMap, c as chain, p as parseProp, d as toHandlerProp } from './actions-94b581a0.esm.js';\nimport './maths-0ab39ae9.esm.js';\n\nfunction _objectWithoutPropertiesLoose(source, excluded) {\n if (source == null) return {};\n var target = {};\n var sourceKeys = Object.keys(source);\n var key, i;\n for (i = 0; i < sourceKeys.length; i++) {\n key = sourceKeys[i];\n if (excluded.indexOf(key) >= 0) continue;\n target[key] = source[key];\n }\n return target;\n}\n\nfunction _objectWithoutProperties(source, excluded) {\n if (source == null) return {};\n var target = _objectWithoutPropertiesLoose(source, excluded);\n var key, i;\n if (Object.getOwnPropertySymbols) {\n var sourceSymbolKeys = Object.getOwnPropertySymbols(source);\n for (i = 0; i < sourceSymbolKeys.length; i++) {\n key = sourceSymbolKeys[i];\n if (excluded.indexOf(key) >= 0) continue;\n if (!Object.prototype.propertyIsEnumerable.call(source, key)) continue;\n target[key] = source[key];\n }\n }\n return target;\n}\n\nconst sharedConfigResolver = {\n target(value) {\n if (value) {\n return () => 'current' in value ? value.current : value;\n }\n return undefined;\n },\n enabled(value = true) {\n return value;\n },\n window(value = SUPPORT.isBrowser ? window : undefined) {\n return value;\n },\n eventOptions({\n passive = true,\n capture = false\n } = {}) {\n return {\n passive,\n capture\n };\n },\n transform(value) {\n return value;\n }\n};\n\nconst _excluded = [\"target\", \"eventOptions\", \"window\", \"enabled\", \"transform\"];\nfunction resolveWith(config = {}, resolvers) {\n const result = {};\n for (const [key, resolver] of Object.entries(resolvers)) {\n switch (typeof resolver) {\n case 'function':\n if (process.env.NODE_ENV === 'development') {\n const r = resolver.call(result, config[key], key, config);\n if (!Number.isNaN(r)) result[key] = r;\n } else {\n result[key] = resolver.call(result, config[key], key, config);\n }\n break;\n case 'object':\n result[key] = resolveWith(config[key], resolver);\n break;\n case 'boolean':\n if (resolver) result[key] = config[key];\n break;\n }\n }\n return result;\n}\nfunction parse(newConfig, gestureKey, _config = {}) {\n const _ref = newConfig,\n {\n target,\n eventOptions,\n window,\n enabled,\n transform\n } = _ref,\n rest = _objectWithoutProperties(_ref, _excluded);\n _config.shared = resolveWith({\n target,\n eventOptions,\n window,\n enabled,\n transform\n }, sharedConfigResolver);\n if (gestureKey) {\n const resolver = ConfigResolverMap.get(gestureKey);\n _config[gestureKey] = resolveWith(_objectSpread2({\n shared: _config.shared\n }, rest), resolver);\n } else {\n for (const key in rest) {\n const resolver = ConfigResolverMap.get(key);\n if (resolver) {\n _config[key] = resolveWith(_objectSpread2({\n shared: _config.shared\n }, rest[key]), resolver);\n } else if (process.env.NODE_ENV === 'development') {\n if (!['drag', 'pinch', 'scroll', 'wheel', 'move', 'hover'].includes(key)) {\n if (key === 'domTarget') {\n throw Error(`[@use-gesture]: \\`domTarget\\` option has been renamed to \\`target\\`.`);\n }\n console.warn(`[@use-gesture]: Unknown config key \\`${key}\\` was used. Please read the documentation for further information.`);\n }\n }\n }\n }\n return _config;\n}\n\nclass EventStore {\n constructor(ctrl, gestureKey) {\n _defineProperty(this, \"_listeners\", new Set());\n this._ctrl = ctrl;\n this._gestureKey = gestureKey;\n }\n add(element, device, action, handler, options) {\n const listeners = this._listeners;\n const type = toDomEventType(device, action);\n const _options = this._gestureKey ? this._ctrl.config[this._gestureKey].eventOptions : {};\n const eventOptions = _objectSpread2(_objectSpread2({}, _options), options);\n element.addEventListener(type, handler, eventOptions);\n const remove = () => {\n element.removeEventListener(type, handler, eventOptions);\n listeners.delete(remove);\n };\n listeners.add(remove);\n return remove;\n }\n clean() {\n this._listeners.forEach(remove => remove());\n this._listeners.clear();\n }\n}\n\nclass TimeoutStore {\n constructor() {\n _defineProperty(this, \"_timeouts\", new Map());\n }\n add(key, callback, ms = 140, ...args) {\n this.remove(key);\n this._timeouts.set(key, window.setTimeout(callback, ms, ...args));\n }\n remove(key) {\n const timeout = this._timeouts.get(key);\n if (timeout) window.clearTimeout(timeout);\n }\n clean() {\n this._timeouts.forEach(timeout => void window.clearTimeout(timeout));\n this._timeouts.clear();\n }\n}\n\nclass Controller {\n constructor(handlers) {\n _defineProperty(this, \"gestures\", new Set());\n _defineProperty(this, \"_targetEventStore\", new EventStore(this));\n _defineProperty(this, \"gestureEventStores\", {});\n _defineProperty(this, \"gestureTimeoutStores\", {});\n _defineProperty(this, \"handlers\", {});\n _defineProperty(this, \"config\", {});\n _defineProperty(this, \"pointerIds\", new Set());\n _defineProperty(this, \"touchIds\", new Set());\n _defineProperty(this, \"state\", {\n shared: {\n shiftKey: false,\n metaKey: false,\n ctrlKey: false,\n altKey: false\n }\n });\n resolveGestures(this, handlers);\n }\n setEventIds(event) {\n if (isTouch(event)) {\n this.touchIds = new Set(touchIds(event));\n return this.touchIds;\n } else if ('pointerId' in event) {\n if (event.type === 'pointerup' || event.type === 'pointercancel') this.pointerIds.delete(event.pointerId);else if (event.type === 'pointerdown') this.pointerIds.add(event.pointerId);\n return this.pointerIds;\n }\n }\n applyHandlers(handlers, nativeHandlers) {\n this.handlers = handlers;\n this.nativeHandlers = nativeHandlers;\n }\n applyConfig(config, gestureKey) {\n this.config = parse(config, gestureKey, this.config);\n }\n clean() {\n this._targetEventStore.clean();\n for (const key of this.gestures) {\n this.gestureEventStores[key].clean();\n this.gestureTimeoutStores[key].clean();\n }\n }\n effect() {\n if (this.config.shared.target) this.bind();\n return () => this._targetEventStore.clean();\n }\n bind(...args) {\n const sharedConfig = this.config.shared;\n const props = {};\n let target;\n if (sharedConfig.target) {\n target = sharedConfig.target();\n if (!target) return;\n }\n if (sharedConfig.enabled) {\n for (const gestureKey of this.gestures) {\n const gestureConfig = this.config[gestureKey];\n const bindFunction = bindToProps(props, gestureConfig.eventOptions, !!target);\n if (gestureConfig.enabled) {\n const Engine = EngineMap.get(gestureKey);\n new Engine(this, args, gestureKey).bind(bindFunction);\n }\n }\n const nativeBindFunction = bindToProps(props, sharedConfig.eventOptions, !!target);\n for (const eventKey in this.nativeHandlers) {\n nativeBindFunction(eventKey, '', event => this.nativeHandlers[eventKey](_objectSpread2(_objectSpread2({}, this.state.shared), {}, {\n event,\n args\n })), undefined, true);\n }\n }\n for (const handlerProp in props) {\n props[handlerProp] = chain(...props[handlerProp]);\n }\n if (!target) return props;\n for (const handlerProp in props) {\n const {\n device,\n capture,\n passive\n } = parseProp(handlerProp);\n this._targetEventStore.add(target, device, '', props[handlerProp], {\n capture,\n passive\n });\n }\n }\n}\nfunction setupGesture(ctrl, gestureKey) {\n ctrl.gestures.add(gestureKey);\n ctrl.gestureEventStores[gestureKey] = new EventStore(ctrl, gestureKey);\n ctrl.gestureTimeoutStores[gestureKey] = new TimeoutStore();\n}\nfunction resolveGestures(ctrl, internalHandlers) {\n if (internalHandlers.drag) setupGesture(ctrl, 'drag');\n if (internalHandlers.wheel) setupGesture(ctrl, 'wheel');\n if (internalHandlers.scroll) setupGesture(ctrl, 'scroll');\n if (internalHandlers.move) setupGesture(ctrl, 'move');\n if (internalHandlers.pinch) setupGesture(ctrl, 'pinch');\n if (internalHandlers.hover) setupGesture(ctrl, 'hover');\n}\nconst bindToProps = (props, eventOptions, withPassiveOption) => (device, action, handler, options = {}, isNative = false) => {\n var _options$capture, _options$passive;\n const capture = (_options$capture = options.capture) !== null && _options$capture !== void 0 ? _options$capture : eventOptions.capture;\n const passive = (_options$passive = options.passive) !== null && _options$passive !== void 0 ? _options$passive : eventOptions.passive;\n let handlerProp = isNative ? device : toHandlerProp(device, action, capture);\n if (withPassiveOption && passive) handlerProp += 'Passive';\n props[handlerProp] = props[handlerProp] || [];\n props[handlerProp].push(handler);\n};\n\nconst RE_NOT_NATIVE = /^on(Drag|Wheel|Scroll|Move|Pinch|Hover)/;\nfunction sortHandlers(_handlers) {\n const native = {};\n const handlers = {};\n const actions = new Set();\n for (let key in _handlers) {\n if (RE_NOT_NATIVE.test(key)) {\n actions.add(RegExp.lastMatch);\n handlers[key] = _handlers[key];\n } else {\n native[key] = _handlers[key];\n }\n }\n return [handlers, native, actions];\n}\nfunction registerGesture(actions, handlers, handlerKey, key, internalHandlers, config) {\n if (!actions.has(handlerKey)) return;\n if (!EngineMap.has(key)) {\n if (process.env.NODE_ENV === 'development') {\n console.warn(`[@use-gesture]: You've created a custom handler that that uses the \\`${key}\\` gesture but isn't properly configured.\\n\\nPlease add \\`${key}Action\\` when creating your handler.`);\n }\n return;\n }\n const startKey = handlerKey + 'Start';\n const endKey = handlerKey + 'End';\n const fn = state => {\n let memo = undefined;\n if (state.first && startKey in handlers) handlers[startKey](state);\n if (handlerKey in handlers) memo = handlers[handlerKey](state);\n if (state.last && endKey in handlers) handlers[endKey](state);\n return memo;\n };\n internalHandlers[key] = fn;\n config[key] = config[key] || {};\n}\nfunction parseMergedHandlers(mergedHandlers, mergedConfig) {\n const [handlers, nativeHandlers, actions] = sortHandlers(mergedHandlers);\n const internalHandlers = {};\n registerGesture(actions, handlers, 'onDrag', 'drag', internalHandlers, mergedConfig);\n registerGesture(actions, handlers, 'onWheel', 'wheel', internalHandlers, mergedConfig);\n registerGesture(actions, handlers, 'onScroll', 'scroll', internalHandlers, mergedConfig);\n registerGesture(actions, handlers, 'onPinch', 'pinch', internalHandlers, mergedConfig);\n registerGesture(actions, handlers, 'onMove', 'move', internalHandlers, mergedConfig);\n registerGesture(actions, handlers, 'onHover', 'hover', internalHandlers, mergedConfig);\n return {\n handlers: internalHandlers,\n config: mergedConfig,\n nativeHandlers\n };\n}\n\nexport { Controller, parseMergedHandlers };\n","import { registerAction, dragAction, pinchAction, wheelAction, scrollAction, moveAction, hoverAction } from '@use-gesture/core/actions';\nexport * from '@use-gesture/core/actions';\nimport React from 'react';\nimport { Controller, parseMergedHandlers } from '@use-gesture/core';\nexport * from '@use-gesture/core/utils';\nexport * from '@use-gesture/core/types';\n\nfunction useRecognizers(handlers, config = {}, gestureKey, nativeHandlers) {\n const ctrl = React.useMemo(() => new Controller(handlers), []);\n ctrl.applyHandlers(handlers, nativeHandlers);\n ctrl.applyConfig(config, gestureKey);\n React.useEffect(ctrl.effect.bind(ctrl));\n React.useEffect(() => {\n return ctrl.clean.bind(ctrl);\n }, []);\n if (config.target === undefined) {\n return ctrl.bind.bind(ctrl);\n }\n return undefined;\n}\n\nfunction useDrag(handler, config) {\n registerAction(dragAction);\n return useRecognizers({\n drag: handler\n }, config || {}, 'drag');\n}\n\nfunction usePinch(handler, config) {\n registerAction(pinchAction);\n return useRecognizers({\n pinch: handler\n }, config || {}, 'pinch');\n}\n\nfunction useWheel(handler, config) {\n registerAction(wheelAction);\n return useRecognizers({\n wheel: handler\n }, config || {}, 'wheel');\n}\n\nfunction useScroll(handler, config) {\n registerAction(scrollAction);\n return useRecognizers({\n scroll: handler\n }, config || {}, 'scroll');\n}\n\nfunction useMove(handler, config) {\n registerAction(moveAction);\n return useRecognizers({\n move: handler\n }, config || {}, 'move');\n}\n\nfunction useHover(handler, config) {\n registerAction(hoverAction);\n return useRecognizers({\n hover: handler\n }, config || {}, 'hover');\n}\n\nfunction createUseGesture(actions) {\n actions.forEach(registerAction);\n return function useGesture(_handlers, _config) {\n const {\n handlers,\n nativeHandlers,\n config\n } = parseMergedHandlers(_handlers, _config || {});\n return useRecognizers(handlers, config, undefined, nativeHandlers);\n };\n}\n\nfunction useGesture(handlers, config) {\n const hook = createUseGesture([dragAction, pinchAction, scrollAction, wheelAction, moveAction, hoverAction]);\n return hook(handlers, config || {});\n}\n\nexport { createUseGesture, useDrag, useGesture, useHover, useMove, usePinch, useScroll, useWheel };\n","/*! *****************************************************************************\r\nCopyright (c) Microsoft Corporation.\r\n\r\nPermission to use, copy, modify, and/or distribute this software for any\r\npurpose with or without fee is hereby granted.\r\n\r\nTHE SOFTWARE IS PROVIDED \"AS IS\" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH\r\nREGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY\r\nAND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,\r\nINDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM\r\nLOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR\r\nOTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR\r\nPERFORMANCE OF THIS SOFTWARE.\r\n***************************************************************************** */\r\n/* global Reflect, Promise */\r\n\r\nvar extendStatics = function(d, b) {\r\n extendStatics = Object.setPrototypeOf ||\r\n ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) ||\r\n function (d, b) { for (var p in b) if (Object.prototype.hasOwnProperty.call(b, p)) d[p] = b[p]; };\r\n return extendStatics(d, b);\r\n};\r\n\r\nexport function __extends(d, b) {\r\n if (typeof b !== \"function\" && b !== null)\r\n throw new TypeError(\"Class extends value \" + String(b) + \" is not a constructor or null\");\r\n extendStatics(d, b);\r\n function __() { this.constructor = d; }\r\n d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());\r\n}\r\n\r\nexport var __assign = function() {\r\n __assign = Object.assign || function __assign(t) {\r\n for (var s, i = 1, n = arguments.length; i < n; i++) {\r\n s = arguments[i];\r\n for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p)) t[p] = s[p];\r\n }\r\n return t;\r\n }\r\n return __assign.apply(this, arguments);\r\n}\r\n\r\nexport function __rest(s, e) {\r\n var t = {};\r\n for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0)\r\n t[p] = s[p];\r\n if (s != null && typeof Object.getOwnPropertySymbols === \"function\")\r\n for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) {\r\n if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i]))\r\n t[p[i]] = s[p[i]];\r\n }\r\n return t;\r\n}\r\n\r\nexport function __decorate(decorators, target, key, desc) {\r\n var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;\r\n if (typeof Reflect === \"object\" && typeof Reflect.decorate === \"function\") r = Reflect.decorate(decorators, target, key, desc);\r\n else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;\r\n return c > 3 && r && Object.defineProperty(target, key, r), r;\r\n}\r\n\r\nexport function __param(paramIndex, decorator) {\r\n return function (target, key) { decorator(target, key, paramIndex); }\r\n}\r\n\r\nexport function __metadata(metadataKey, metadataValue) {\r\n if (typeof Reflect === \"object\" && typeof Reflect.metadata === \"function\") return Reflect.metadata(metadataKey, metadataValue);\r\n}\r\n\r\nexport function __awaiter(thisArg, _arguments, P, generator) {\r\n function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }\r\n return new (P || (P = Promise))(function (resolve, reject) {\r\n function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }\r\n function rejected(value) { try { step(generator[\"throw\"](value)); } catch (e) { reject(e); } }\r\n function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }\r\n step((generator = generator.apply(thisArg, _arguments || [])).next());\r\n });\r\n}\r\n\r\nexport function __generator(thisArg, body) {\r\n var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g;\r\n return g = { next: verb(0), \"throw\": verb(1), \"return\": verb(2) }, typeof Symbol === \"function\" && (g[Symbol.iterator] = function() { return this; }), g;\r\n function verb(n) { return function (v) { return step([n, v]); }; }\r\n function step(op) {\r\n if (f) throw new TypeError(\"Generator is already executing.\");\r\n while (_) try {\r\n if (f = 1, y && (t = op[0] & 2 ? y[\"return\"] : op[0] ? y[\"throw\"] || ((t = y[\"return\"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t;\r\n if (y = 0, t) op = [op[0] & 2, t.value];\r\n switch (op[0]) {\r\n case 0: case 1: t = op; break;\r\n case 4: _.label++; return { value: op[1], done: false };\r\n case 5: _.label++; y = op[1]; op = [0]; continue;\r\n case 7: op = _.ops.pop(); _.trys.pop(); continue;\r\n default:\r\n if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; }\r\n if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; }\r\n if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; }\r\n if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; }\r\n if (t[2]) _.ops.pop();\r\n _.trys.pop(); continue;\r\n }\r\n op = body.call(thisArg, _);\r\n } catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; }\r\n if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };\r\n }\r\n}\r\n\r\nexport var __createBinding = Object.create ? (function(o, m, k, k2) {\r\n if (k2 === undefined) k2 = k;\r\n Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } });\r\n}) : (function(o, m, k, k2) {\r\n if (k2 === undefined) k2 = k;\r\n o[k2] = m[k];\r\n});\r\n\r\nexport function __exportStar(m, o) {\r\n for (var p in m) if (p !== \"default\" && !Object.prototype.hasOwnProperty.call(o, p)) __createBinding(o, m, p);\r\n}\r\n\r\nexport function __values(o) {\r\n var s = typeof Symbol === \"function\" && Symbol.iterator, m = s && o[s], i = 0;\r\n if (m) return m.call(o);\r\n if (o && typeof o.length === \"number\") return {\r\n next: function () {\r\n if (o && i >= o.length) o = void 0;\r\n return { value: o && o[i++], done: !o };\r\n }\r\n };\r\n throw new TypeError(s ? \"Object is not iterable.\" : \"Symbol.iterator is not defined.\");\r\n}\r\n\r\nexport function __read(o, n) {\r\n var m = typeof Symbol === \"function\" && o[Symbol.iterator];\r\n if (!m) return o;\r\n var i = m.call(o), r, ar = [], e;\r\n try {\r\n while ((n === void 0 || n-- > 0) && !(r = i.next()).done) ar.push(r.value);\r\n }\r\n catch (error) { e = { error: error }; }\r\n finally {\r\n try {\r\n if (r && !r.done && (m = i[\"return\"])) m.call(i);\r\n }\r\n finally { if (e) throw e.error; }\r\n }\r\n return ar;\r\n}\r\n\r\n/** @deprecated */\r\nexport function __spread() {\r\n for (var ar = [], i = 0; i < arguments.length; i++)\r\n ar = ar.concat(__read(arguments[i]));\r\n return ar;\r\n}\r\n\r\n/** @deprecated */\r\nexport function __spreadArrays() {\r\n for (var s = 0, i = 0, il = arguments.length; i < il; i++) s += arguments[i].length;\r\n for (var r = Array(s), k = 0, i = 0; i < il; i++)\r\n for (var a = arguments[i], j = 0, jl = a.length; j < jl; j++, k++)\r\n r[k] = a[j];\r\n return r;\r\n}\r\n\r\nexport function __spreadArray(to, from, pack) {\r\n if (pack || arguments.length === 2) for (var i = 0, l = from.length, ar; i < l; i++) {\r\n if (ar || !(i in from)) {\r\n if (!ar) ar = Array.prototype.slice.call(from, 0, i);\r\n ar[i] = from[i];\r\n }\r\n }\r\n return to.concat(ar || from);\r\n}\r\n\r\nexport function __await(v) {\r\n return this instanceof __await ? (this.v = v, this) : new __await(v);\r\n}\r\n\r\nexport function __asyncGenerator(thisArg, _arguments, generator) {\r\n if (!Symbol.asyncIterator) throw new TypeError(\"Symbol.asyncIterator is not defined.\");\r\n var g = generator.apply(thisArg, _arguments || []), i, q = [];\r\n return i = {}, verb(\"next\"), verb(\"throw\"), verb(\"return\"), i[Symbol.asyncIterator] = function () { return this; }, i;\r\n function verb(n) { if (g[n]) i[n] = function (v) { return new Promise(function (a, b) { q.push([n, v, a, b]) > 1 || resume(n, v); }); }; }\r\n function resume(n, v) { try { step(g[n](v)); } catch (e) { settle(q[0][3], e); } }\r\n function step(r) { r.value instanceof __await ? Promise.resolve(r.value.v).then(fulfill, reject) : settle(q[0][2], r); }\r\n function fulfill(value) { resume(\"next\", value); }\r\n function reject(value) { resume(\"throw\", value); }\r\n function settle(f, v) { if (f(v), q.shift(), q.length) resume(q[0][0], q[0][1]); }\r\n}\r\n\r\nexport function __asyncDelegator(o) {\r\n var i, p;\r\n return i = {}, verb(\"next\"), verb(\"throw\", function (e) { throw e; }), verb(\"return\"), i[Symbol.iterator] = function () { return this; }, i;\r\n function verb(n, f) { i[n] = o[n] ? function (v) { return (p = !p) ? { value: __await(o[n](v)), done: n === \"return\" } : f ? f(v) : v; } : f; }\r\n}\r\n\r\nexport function __asyncValues(o) {\r\n if (!Symbol.asyncIterator) throw new TypeError(\"Symbol.asyncIterator is not defined.\");\r\n var m = o[Symbol.asyncIterator], i;\r\n return m ? m.call(o) : (o = typeof __values === \"function\" ? __values(o) : o[Symbol.iterator](), i = {}, verb(\"next\"), verb(\"throw\"), verb(\"return\"), i[Symbol.asyncIterator] = function () { return this; }, i);\r\n function verb(n) { i[n] = o[n] && function (v) { return new Promise(function (resolve, reject) { v = o[n](v), settle(resolve, reject, v.done, v.value); }); }; }\r\n function settle(resolve, reject, d, v) { Promise.resolve(v).then(function(v) { resolve({ value: v, done: d }); }, reject); }\r\n}\r\n\r\nexport function __makeTemplateObject(cooked, raw) {\r\n if (Object.defineProperty) { Object.defineProperty(cooked, \"raw\", { value: raw }); } else { cooked.raw = raw; }\r\n return cooked;\r\n};\r\n\r\nvar __setModuleDefault = Object.create ? (function(o, v) {\r\n Object.defineProperty(o, \"default\", { enumerable: true, value: v });\r\n}) : function(o, v) {\r\n o[\"default\"] = v;\r\n};\r\n\r\nexport function __importStar(mod) {\r\n if (mod && mod.__esModule) return mod;\r\n var result = {};\r\n if (mod != null) for (var k in mod) if (k !== \"default\" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);\r\n __setModuleDefault(result, mod);\r\n return result;\r\n}\r\n\r\nexport function __importDefault(mod) {\r\n return (mod && mod.__esModule) ? mod : { default: mod };\r\n}\r\n\r\nexport function __classPrivateFieldGet(receiver, state, kind, f) {\r\n if (kind === \"a\" && !f) throw new TypeError(\"Private accessor was defined without a getter\");\r\n if (typeof state === \"function\" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError(\"Cannot read private member from an object whose class did not declare it\");\r\n return kind === \"m\" ? f : kind === \"a\" ? f.call(receiver) : f ? f.value : state.get(receiver);\r\n}\r\n\r\nexport function __classPrivateFieldSet(receiver, state, value, kind, f) {\r\n if (kind === \"m\") throw new TypeError(\"Private method is not writable\");\r\n if (kind === \"a\" && !f) throw new TypeError(\"Private accessor was defined without a setter\");\r\n if (typeof state === \"function\" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError(\"Cannot write private member to an object whose class did not declare it\");\r\n return (kind === \"a\" ? f.call(receiver, value) : f ? f.value = value : state.set(receiver, value)), value;\r\n}\r\n","export function isFunction(value) {\n return typeof value === 'function';\n}\n//# sourceMappingURL=isFunction.js.map","export function createErrorClass(createImpl) {\n var _super = function (instance) {\n Error.call(instance);\n instance.stack = new Error().stack;\n };\n var ctorFunc = createImpl(_super);\n ctorFunc.prototype = Object.create(Error.prototype);\n ctorFunc.prototype.constructor = ctorFunc;\n return ctorFunc;\n}\n//# sourceMappingURL=createErrorClass.js.map","import { createErrorClass } from './createErrorClass';\nexport var UnsubscriptionError = createErrorClass(function (_super) {\n return function UnsubscriptionErrorImpl(errors) {\n _super(this);\n this.message = errors\n ? errors.length + \" errors occurred during unsubscription:\\n\" + errors.map(function (err, i) { return i + 1 + \") \" + err.toString(); }).join('\\n ')\n : '';\n this.name = 'UnsubscriptionError';\n this.errors = errors;\n };\n});\n//# sourceMappingURL=UnsubscriptionError.js.map","export function arrRemove(arr, item) {\n if (arr) {\n var index = arr.indexOf(item);\n 0 <= index && arr.splice(index, 1);\n }\n}\n//# sourceMappingURL=arrRemove.js.map","import { __read, __spreadArray, __values } from \"tslib\";\nimport { isFunction } from './util/isFunction';\nimport { UnsubscriptionError } from './util/UnsubscriptionError';\nimport { arrRemove } from './util/arrRemove';\nvar Subscription = (function () {\n function Subscription(initialTeardown) {\n this.initialTeardown = initialTeardown;\n this.closed = false;\n this._parentage = null;\n this._finalizers = null;\n }\n Subscription.prototype.unsubscribe = function () {\n var e_1, _a, e_2, _b;\n var errors;\n if (!this.closed) {\n this.closed = true;\n var _parentage = this._parentage;\n if (_parentage) {\n this._parentage = null;\n if (Array.isArray(_parentage)) {\n try {\n for (var _parentage_1 = __values(_parentage), _parentage_1_1 = _parentage_1.next(); !_parentage_1_1.done; _parentage_1_1 = _parentage_1.next()) {\n var parent_1 = _parentage_1_1.value;\n parent_1.remove(this);\n }\n }\n catch (e_1_1) { e_1 = { error: e_1_1 }; }\n finally {\n try {\n if (_parentage_1_1 && !_parentage_1_1.done && (_a = _parentage_1.return)) _a.call(_parentage_1);\n }\n finally { if (e_1) throw e_1.error; }\n }\n }\n else {\n _parentage.remove(this);\n }\n }\n var initialFinalizer = this.initialTeardown;\n if (isFunction(initialFinalizer)) {\n try {\n initialFinalizer();\n }\n catch (e) {\n errors = e instanceof UnsubscriptionError ? e.errors : [e];\n }\n }\n var _finalizers = this._finalizers;\n if (_finalizers) {\n this._finalizers = null;\n try {\n for (var _finalizers_1 = __values(_finalizers), _finalizers_1_1 = _finalizers_1.next(); !_finalizers_1_1.done; _finalizers_1_1 = _finalizers_1.next()) {\n var finalizer = _finalizers_1_1.value;\n try {\n execFinalizer(finalizer);\n }\n catch (err) {\n errors = errors !== null && errors !== void 0 ? errors : [];\n if (err instanceof UnsubscriptionError) {\n errors = __spreadArray(__spreadArray([], __read(errors)), __read(err.errors));\n }\n else {\n errors.push(err);\n }\n }\n }\n }\n catch (e_2_1) { e_2 = { error: e_2_1 }; }\n finally {\n try {\n if (_finalizers_1_1 && !_finalizers_1_1.done && (_b = _finalizers_1.return)) _b.call(_finalizers_1);\n }\n finally { if (e_2) throw e_2.error; }\n }\n }\n if (errors) {\n throw new UnsubscriptionError(errors);\n }\n }\n };\n Subscription.prototype.add = function (teardown) {\n var _a;\n if (teardown && teardown !== this) {\n if (this.closed) {\n execFinalizer(teardown);\n }\n else {\n if (teardown instanceof Subscription) {\n if (teardown.closed || teardown._hasParent(this)) {\n return;\n }\n teardown._addParent(this);\n }\n (this._finalizers = (_a = this._finalizers) !== null && _a !== void 0 ? _a : []).push(teardown);\n }\n }\n };\n Subscription.prototype._hasParent = function (parent) {\n var _parentage = this._parentage;\n return _parentage === parent || (Array.isArray(_parentage) && _parentage.includes(parent));\n };\n Subscription.prototype._addParent = function (parent) {\n var _parentage = this._parentage;\n this._parentage = Array.isArray(_parentage) ? (_parentage.push(parent), _parentage) : _parentage ? [_parentage, parent] : parent;\n };\n Subscription.prototype._removeParent = function (parent) {\n var _parentage = this._parentage;\n if (_parentage === parent) {\n this._parentage = null;\n }\n else if (Array.isArray(_parentage)) {\n arrRemove(_parentage, parent);\n }\n };\n Subscription.prototype.remove = function (teardown) {\n var _finalizers = this._finalizers;\n _finalizers && arrRemove(_finalizers, teardown);\n if (teardown instanceof Subscription) {\n teardown._removeParent(this);\n }\n };\n Subscription.EMPTY = (function () {\n var empty = new Subscription();\n empty.closed = true;\n return empty;\n })();\n return Subscription;\n}());\nexport { Subscription };\nexport var EMPTY_SUBSCRIPTION = Subscription.EMPTY;\nexport function isSubscription(value) {\n return (value instanceof Subscription ||\n (value && 'closed' in value && isFunction(value.remove) && isFunction(value.add) && isFunction(value.unsubscribe)));\n}\nfunction execFinalizer(finalizer) {\n if (isFunction(finalizer)) {\n finalizer();\n }\n else {\n finalizer.unsubscribe();\n }\n}\n//# sourceMappingURL=Subscription.js.map","export var config = {\n onUnhandledError: null,\n onStoppedNotification: null,\n Promise: undefined,\n useDeprecatedSynchronousErrorHandling: false,\n useDeprecatedNextContext: false,\n};\n//# sourceMappingURL=config.js.map","import { __read, __spreadArray } from \"tslib\";\nexport var timeoutProvider = {\n setTimeout: function (handler, timeout) {\n var args = [];\n for (var _i = 2; _i < arguments.length; _i++) {\n args[_i - 2] = arguments[_i];\n }\n var delegate = timeoutProvider.delegate;\n if (delegate === null || delegate === void 0 ? void 0 : delegate.setTimeout) {\n return delegate.setTimeout.apply(delegate, __spreadArray([handler, timeout], __read(args)));\n }\n return setTimeout.apply(void 0, __spreadArray([handler, timeout], __read(args)));\n },\n clearTimeout: function (handle) {\n var delegate = timeoutProvider.delegate;\n return ((delegate === null || delegate === void 0 ? void 0 : delegate.clearTimeout) || clearTimeout)(handle);\n },\n delegate: undefined,\n};\n//# sourceMappingURL=timeoutProvider.js.map","export function noop() { }\n//# sourceMappingURL=noop.js.map","export var COMPLETE_NOTIFICATION = (function () { return createNotification('C', undefined, undefined); })();\nexport function errorNotification(error) {\n return createNotification('E', undefined, error);\n}\nexport function nextNotification(value) {\n return createNotification('N', value, undefined);\n}\nexport function createNotification(kind, value, error) {\n return {\n kind: kind,\n value: value,\n error: error,\n };\n}\n//# sourceMappingURL=NotificationFactories.js.map","import { config } from '../config';\nvar context = null;\nexport function errorContext(cb) {\n if (config.useDeprecatedSynchronousErrorHandling) {\n var isRoot = !context;\n if (isRoot) {\n context = { errorThrown: false, error: null };\n }\n cb();\n if (isRoot) {\n var _a = context, errorThrown = _a.errorThrown, error = _a.error;\n context = null;\n if (errorThrown) {\n throw error;\n }\n }\n }\n else {\n cb();\n }\n}\nexport function captureError(err) {\n if (config.useDeprecatedSynchronousErrorHandling && context) {\n context.errorThrown = true;\n context.error = err;\n }\n}\n//# sourceMappingURL=errorContext.js.map","import { __extends } from \"tslib\";\nimport { isFunction } from './util/isFunction';\nimport { isSubscription, Subscription } from './Subscription';\nimport { config } from './config';\nimport { reportUnhandledError } from './util/reportUnhandledError';\nimport { noop } from './util/noop';\nimport { nextNotification, errorNotification, COMPLETE_NOTIFICATION } from './NotificationFactories';\nimport { timeoutProvider } from './scheduler/timeoutProvider';\nimport { captureError } from './util/errorContext';\nvar Subscriber = (function (_super) {\n __extends(Subscriber, _super);\n function Subscriber(destination) {\n var _this = _super.call(this) || this;\n _this.isStopped = false;\n if (destination) {\n _this.destination = destination;\n if (isSubscription(destination)) {\n destination.add(_this);\n }\n }\n else {\n _this.destination = EMPTY_OBSERVER;\n }\n return _this;\n }\n Subscriber.create = function (next, error, complete) {\n return new SafeSubscriber(next, error, complete);\n };\n Subscriber.prototype.next = function (value) {\n if (this.isStopped) {\n handleStoppedNotification(nextNotification(value), this);\n }\n else {\n this._next(value);\n }\n };\n Subscriber.prototype.error = function (err) {\n if (this.isStopped) {\n handleStoppedNotification(errorNotification(err), this);\n }\n else {\n this.isStopped = true;\n this._error(err);\n }\n };\n Subscriber.prototype.complete = function () {\n if (this.isStopped) {\n handleStoppedNotification(COMPLETE_NOTIFICATION, this);\n }\n else {\n this.isStopped = true;\n this._complete();\n }\n };\n Subscriber.prototype.unsubscribe = function () {\n if (!this.closed) {\n this.isStopped = true;\n _super.prototype.unsubscribe.call(this);\n this.destination = null;\n }\n };\n Subscriber.prototype._next = function (value) {\n this.destination.next(value);\n };\n Subscriber.prototype._error = function (err) {\n try {\n this.destination.error(err);\n }\n finally {\n this.unsubscribe();\n }\n };\n Subscriber.prototype._complete = function () {\n try {\n this.destination.complete();\n }\n finally {\n this.unsubscribe();\n }\n };\n return Subscriber;\n}(Subscription));\nexport { Subscriber };\nvar _bind = Function.prototype.bind;\nfunction bind(fn, thisArg) {\n return _bind.call(fn, thisArg);\n}\nvar ConsumerObserver = (function () {\n function ConsumerObserver(partialObserver) {\n this.partialObserver = partialObserver;\n }\n ConsumerObserver.prototype.next = function (value) {\n var partialObserver = this.partialObserver;\n if (partialObserver.next) {\n try {\n partialObserver.next(value);\n }\n catch (error) {\n handleUnhandledError(error);\n }\n }\n };\n ConsumerObserver.prototype.error = function (err) {\n var partialObserver = this.partialObserver;\n if (partialObserver.error) {\n try {\n partialObserver.error(err);\n }\n catch (error) {\n handleUnhandledError(error);\n }\n }\n else {\n handleUnhandledError(err);\n }\n };\n ConsumerObserver.prototype.complete = function () {\n var partialObserver = this.partialObserver;\n if (partialObserver.complete) {\n try {\n partialObserver.complete();\n }\n catch (error) {\n handleUnhandledError(error);\n }\n }\n };\n return ConsumerObserver;\n}());\nvar SafeSubscriber = (function (_super) {\n __extends(SafeSubscriber, _super);\n function SafeSubscriber(observerOrNext, error, complete) {\n var _this = _super.call(this) || this;\n var partialObserver;\n if (isFunction(observerOrNext) || !observerOrNext) {\n partialObserver = {\n next: (observerOrNext !== null && observerOrNext !== void 0 ? observerOrNext : undefined),\n error: error !== null && error !== void 0 ? error : undefined,\n complete: complete !== null && complete !== void 0 ? complete : undefined,\n };\n }\n else {\n var context_1;\n if (_this && config.useDeprecatedNextContext) {\n context_1 = Object.create(observerOrNext);\n context_1.unsubscribe = function () { return _this.unsubscribe(); };\n partialObserver = {\n next: observerOrNext.next && bind(observerOrNext.next, context_1),\n error: observerOrNext.error && bind(observerOrNext.error, context_1),\n complete: observerOrNext.complete && bind(observerOrNext.complete, context_1),\n };\n }\n else {\n partialObserver = observerOrNext;\n }\n }\n _this.destination = new ConsumerObserver(partialObserver);\n return _this;\n }\n return SafeSubscriber;\n}(Subscriber));\nexport { SafeSubscriber };\nfunction handleUnhandledError(error) {\n if (config.useDeprecatedSynchronousErrorHandling) {\n captureError(error);\n }\n else {\n reportUnhandledError(error);\n }\n}\nfunction defaultErrorHandler(err) {\n throw err;\n}\nfunction handleStoppedNotification(notification, subscriber) {\n var onStoppedNotification = config.onStoppedNotification;\n onStoppedNotification && timeoutProvider.setTimeout(function () { return onStoppedNotification(notification, subscriber); });\n}\nexport var EMPTY_OBSERVER = {\n closed: true,\n next: noop,\n error: defaultErrorHandler,\n complete: noop,\n};\n//# sourceMappingURL=Subscriber.js.map","import { config } from '../config';\nimport { timeoutProvider } from '../scheduler/timeoutProvider';\nexport function reportUnhandledError(err) {\n timeoutProvider.setTimeout(function () {\n var onUnhandledError = config.onUnhandledError;\n if (onUnhandledError) {\n onUnhandledError(err);\n }\n else {\n throw err;\n }\n });\n}\n//# sourceMappingURL=reportUnhandledError.js.map","export var observable = (function () { return (typeof Symbol === 'function' && Symbol.observable) || '@@observable'; })();\n//# sourceMappingURL=observable.js.map","export function identity(x) {\n return x;\n}\n//# sourceMappingURL=identity.js.map","import { identity } from './identity';\nexport function pipe() {\n var fns = [];\n for (var _i = 0; _i < arguments.length; _i++) {\n fns[_i] = arguments[_i];\n }\n return pipeFromArray(fns);\n}\nexport function pipeFromArray(fns) {\n if (fns.length === 0) {\n return identity;\n }\n if (fns.length === 1) {\n return fns[0];\n }\n return function piped(input) {\n return fns.reduce(function (prev, fn) { return fn(prev); }, input);\n };\n}\n//# sourceMappingURL=pipe.js.map","import { SafeSubscriber, Subscriber } from './Subscriber';\nimport { isSubscription } from './Subscription';\nimport { observable as Symbol_observable } from './symbol/observable';\nimport { pipeFromArray } from './util/pipe';\nimport { config } from './config';\nimport { isFunction } from './util/isFunction';\nimport { errorContext } from './util/errorContext';\nvar Observable = (function () {\n function Observable(subscribe) {\n if (subscribe) {\n this._subscribe = subscribe;\n }\n }\n Observable.prototype.lift = function (operator) {\n var observable = new Observable();\n observable.source = this;\n observable.operator = operator;\n return observable;\n };\n Observable.prototype.subscribe = function (observerOrNext, error, complete) {\n var _this = this;\n var subscriber = isSubscriber(observerOrNext) ? observerOrNext : new SafeSubscriber(observerOrNext, error, complete);\n errorContext(function () {\n var _a = _this, operator = _a.operator, source = _a.source;\n subscriber.add(operator\n ?\n operator.call(subscriber, source)\n : source\n ?\n _this._subscribe(subscriber)\n :\n _this._trySubscribe(subscriber));\n });\n return subscriber;\n };\n Observable.prototype._trySubscribe = function (sink) {\n try {\n return this._subscribe(sink);\n }\n catch (err) {\n sink.error(err);\n }\n };\n Observable.prototype.forEach = function (next, promiseCtor) {\n var _this = this;\n promiseCtor = getPromiseCtor(promiseCtor);\n return new promiseCtor(function (resolve, reject) {\n var subscriber = new SafeSubscriber({\n next: function (value) {\n try {\n next(value);\n }\n catch (err) {\n reject(err);\n subscriber.unsubscribe();\n }\n },\n error: reject,\n complete: resolve,\n });\n _this.subscribe(subscriber);\n });\n };\n Observable.prototype._subscribe = function (subscriber) {\n var _a;\n return (_a = this.source) === null || _a === void 0 ? void 0 : _a.subscribe(subscriber);\n };\n Observable.prototype[Symbol_observable] = function () {\n return this;\n };\n Observable.prototype.pipe = function () {\n var operations = [];\n for (var _i = 0; _i < arguments.length; _i++) {\n operations[_i] = arguments[_i];\n }\n return pipeFromArray(operations)(this);\n };\n Observable.prototype.toPromise = function (promiseCtor) {\n var _this = this;\n promiseCtor = getPromiseCtor(promiseCtor);\n return new promiseCtor(function (resolve, reject) {\n var value;\n _this.subscribe(function (x) { return (value = x); }, function (err) { return reject(err); }, function () { return resolve(value); });\n });\n };\n Observable.create = function (subscribe) {\n return new Observable(subscribe);\n };\n return Observable;\n}());\nexport { Observable };\nfunction getPromiseCtor(promiseCtor) {\n var _a;\n return (_a = promiseCtor !== null && promiseCtor !== void 0 ? promiseCtor : config.Promise) !== null && _a !== void 0 ? _a : Promise;\n}\nfunction isObserver(value) {\n return value && isFunction(value.next) && isFunction(value.error) && isFunction(value.complete);\n}\nfunction isSubscriber(value) {\n return (value && value instanceof Subscriber) || (isObserver(value) && isSubscription(value));\n}\n//# sourceMappingURL=Observable.js.map","import { createErrorClass } from './createErrorClass';\nexport var ObjectUnsubscribedError = createErrorClass(function (_super) {\n return function ObjectUnsubscribedErrorImpl() {\n _super(this);\n this.name = 'ObjectUnsubscribedError';\n this.message = 'object unsubscribed';\n };\n});\n//# sourceMappingURL=ObjectUnsubscribedError.js.map","import { __extends, __values } from \"tslib\";\nimport { Observable } from './Observable';\nimport { Subscription, EMPTY_SUBSCRIPTION } from './Subscription';\nimport { ObjectUnsubscribedError } from './util/ObjectUnsubscribedError';\nimport { arrRemove } from './util/arrRemove';\nimport { errorContext } from './util/errorContext';\nvar Subject = (function (_super) {\n __extends(Subject, _super);\n function Subject() {\n var _this = _super.call(this) || this;\n _this.closed = false;\n _this.currentObservers = null;\n _this.observers = [];\n _this.isStopped = false;\n _this.hasError = false;\n _this.thrownError = null;\n return _this;\n }\n Subject.prototype.lift = function (operator) {\n var subject = new AnonymousSubject(this, this);\n subject.operator = operator;\n return subject;\n };\n Subject.prototype._throwIfClosed = function () {\n if (this.closed) {\n throw new ObjectUnsubscribedError();\n }\n };\n Subject.prototype.next = function (value) {\n var _this = this;\n errorContext(function () {\n var e_1, _a;\n _this._throwIfClosed();\n if (!_this.isStopped) {\n if (!_this.currentObservers) {\n _this.currentObservers = Array.from(_this.observers);\n }\n try {\n for (var _b = __values(_this.currentObservers), _c = _b.next(); !_c.done; _c = _b.next()) {\n var observer = _c.value;\n observer.next(value);\n }\n }\n catch (e_1_1) { e_1 = { error: e_1_1 }; }\n finally {\n try {\n if (_c && !_c.done && (_a = _b.return)) _a.call(_b);\n }\n finally { if (e_1) throw e_1.error; }\n }\n }\n });\n };\n Subject.prototype.error = function (err) {\n var _this = this;\n errorContext(function () {\n _this._throwIfClosed();\n if (!_this.isStopped) {\n _this.hasError = _this.isStopped = true;\n _this.thrownError = err;\n var observers = _this.observers;\n while (observers.length) {\n observers.shift().error(err);\n }\n }\n });\n };\n Subject.prototype.complete = function () {\n var _this = this;\n errorContext(function () {\n _this._throwIfClosed();\n if (!_this.isStopped) {\n _this.isStopped = true;\n var observers = _this.observers;\n while (observers.length) {\n observers.shift().complete();\n }\n }\n });\n };\n Subject.prototype.unsubscribe = function () {\n this.isStopped = this.closed = true;\n this.observers = this.currentObservers = null;\n };\n Object.defineProperty(Subject.prototype, \"observed\", {\n get: function () {\n var _a;\n return ((_a = this.observers) === null || _a === void 0 ? void 0 : _a.length) > 0;\n },\n enumerable: false,\n configurable: true\n });\n Subject.prototype._trySubscribe = function (subscriber) {\n this._throwIfClosed();\n return _super.prototype._trySubscribe.call(this, subscriber);\n };\n Subject.prototype._subscribe = function (subscriber) {\n this._throwIfClosed();\n this._checkFinalizedStatuses(subscriber);\n return this._innerSubscribe(subscriber);\n };\n Subject.prototype._innerSubscribe = function (subscriber) {\n var _this = this;\n var _a = this, hasError = _a.hasError, isStopped = _a.isStopped, observers = _a.observers;\n if (hasError || isStopped) {\n return EMPTY_SUBSCRIPTION;\n }\n this.currentObservers = null;\n observers.push(subscriber);\n return new Subscription(function () {\n _this.currentObservers = null;\n arrRemove(observers, subscriber);\n });\n };\n Subject.prototype._checkFinalizedStatuses = function (subscriber) {\n var _a = this, hasError = _a.hasError, thrownError = _a.thrownError, isStopped = _a.isStopped;\n if (hasError) {\n subscriber.error(thrownError);\n }\n else if (isStopped) {\n subscriber.complete();\n }\n };\n Subject.prototype.asObservable = function () {\n var observable = new Observable();\n observable.source = this;\n return observable;\n };\n Subject.create = function (destination, source) {\n return new AnonymousSubject(destination, source);\n };\n return Subject;\n}(Observable));\nexport { Subject };\nvar AnonymousSubject = (function (_super) {\n __extends(AnonymousSubject, _super);\n function AnonymousSubject(destination, source) {\n var _this = _super.call(this) || this;\n _this.destination = destination;\n _this.source = source;\n return _this;\n }\n AnonymousSubject.prototype.next = function (value) {\n var _a, _b;\n (_b = (_a = this.destination) === null || _a === void 0 ? void 0 : _a.next) === null || _b === void 0 ? void 0 : _b.call(_a, value);\n };\n AnonymousSubject.prototype.error = function (err) {\n var _a, _b;\n (_b = (_a = this.destination) === null || _a === void 0 ? void 0 : _a.error) === null || _b === void 0 ? void 0 : _b.call(_a, err);\n };\n AnonymousSubject.prototype.complete = function () {\n var _a, _b;\n (_b = (_a = this.destination) === null || _a === void 0 ? void 0 : _a.complete) === null || _b === void 0 ? void 0 : _b.call(_a);\n };\n AnonymousSubject.prototype._subscribe = function (subscriber) {\n var _a, _b;\n return (_b = (_a = this.source) === null || _a === void 0 ? void 0 : _a.subscribe(subscriber)) !== null && _b !== void 0 ? _b : EMPTY_SUBSCRIPTION;\n };\n return AnonymousSubject;\n}(Subject));\nexport { AnonymousSubject };\n//# sourceMappingURL=Subject.js.map","import { useEffect, useRef } from 'react'\nimport { Subject } from 'rxjs'\nimport {\n UseSpringCarouselEventsObservableProps,\n UseTransitionCarouselEventsObservableProps,\n ObservableCallbackFn,\n EmitObservableFn,\n} from '../types'\n\nexport function useCustomEventsModule() {\n const eventsObserverRef = useRef(\n new Subject<\n T extends 'use-spring'\n ? UseSpringCarouselEventsObservableProps\n : UseTransitionCarouselEventsObservableProps\n >(),\n )\n\n function useListenToCustomEvent(fn: ObservableCallbackFn) {\n useEffect(() => {\n const subscribe = eventsObserverRef.current.subscribe(fn)\n return () => subscribe.unsubscribe()\n }, [fn])\n }\n\n const emitObservable: EmitObservableFn = data => {\n eventsObserverRef.current.next(data)\n }\n\n return {\n useListenToCustomEvent,\n emitObservable,\n }\n}\n","import { useRef, useLayoutEffect, useEffect } from 'react'\n\ntype Callback = () => void | (() => void)\n\nconst useIsomorphicLayoutEffect =\n typeof window !== 'undefined' ? useLayoutEffect : useEffect\n\nfunction useIsomorphicMount(callback: Callback) {\n const isMounted = useRef(false)\n\n useIsomorphicLayoutEffect(() => {\n if (!isMounted.current) {\n const clean = callback()\n isMounted.current = true\n\n return () => {\n clean && clean()\n }\n }\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [])\n}\n\nexport { useIsomorphicLayoutEffect, useIsomorphicMount }\n","import { useRef, createContext, useContext } from 'react'\nimport { useSpring, config, animated } from 'react-spring'\nimport { useDrag } from '@use-gesture/react'\nimport { useCustomEventsModule, useFullscreenModule, useThumbsModule } from '../modules'\nimport {\n SlideToItemFnProps,\n SlideActionType,\n UseSpringFixedSlideTypeReturnProps,\n} from '../types'\nimport { useIsomorphicLayoutEffect, useIsomorphicMount } from '../utils'\nimport {\n UseSpringCarouselProps,\n ReactSpringCarouselItemWithThumbs,\n UseSpringFluidSlideTypeReturnProps,\n UseSpringCarouselBaseProps,\n UseSpringCarouselWithThumbs,\n UseSpringCarouselFluidType,\n DisableGesturesProps,\n WithLoopProps,\n UseSpringCarouselWithNoThumbs,\n EnableGesturesProps,\n WithNoLoop,\n UseSpringCarouselFixedSlideType,\n} from '../types/useSpringCarousel'\n\ntype ReturnType = T extends 'fixed'\n ? UseSpringFixedSlideTypeReturnProps\n : UseSpringFluidSlideTypeReturnProps\ntype ContextTypes = Omit, 'carouselFragment' | 'thumbsFragment'>\n\nconst Context = createContext | undefined>(undefined)\n\nconst defaultDragSpringConfig = {\n ...config.default,\n mass: 1,\n velocity: 0,\n}\n\nfunction useSpringCarousel(\n props: UseSpringCarouselBaseProps &\n UseSpringCarouselFluidType &\n (UseSpringCarouselWithThumbs | UseSpringCarouselWithNoThumbs) &\n (DisableGesturesProps | EnableGesturesProps) &\n (WithLoopProps | WithNoLoop),\n): ReturnType<'fluid'>\nfunction useSpringCarousel(\n props: UseSpringCarouselBaseProps &\n UseSpringCarouselFixedSlideType &\n (UseSpringCarouselWithThumbs | UseSpringCarouselWithNoThumbs) &\n (DisableGesturesProps | EnableGesturesProps) &\n (WithLoopProps | WithNoLoop),\n): ReturnType<'fixed'>\n\nfunction useSpringCarousel({\n items,\n withLoop = false,\n draggingSlideTreshold,\n springConfig = config.default,\n shouldResizeOnWindowResize = true,\n withThumbs = false,\n enableThumbsWrapperScroll = true,\n slideWhenThresholdIsReached = true,\n carouselSlideAxis = 'x',\n thumbsSlideAxis = 'x',\n prepareThumbsData,\n initialActiveItem = 0,\n initialStartingPosition,\n disableGestures = false,\n gutter = 0,\n startEndGutter = 0,\n touchAction,\n slideAmount,\n freeScroll = false,\n enableFreeScrollDrag,\n itemsPerSlide = 1,\n slideType = 'fixed',\n}: UseSpringCarouselProps): ReturnType<'fixed' | 'fluid'> {\n function getItems() {\n if (withLoop) {\n if (items.length === itemsPerSlide) {\n return [...items, ...items, ...items, ...items, ...items]\n }\n return [...items, ...items, ...items]\n }\n return items\n }\n\n const isFirstMount = useRef(true)\n const slideActionType = useRef('initial' as SlideActionType)\n const internalItems = getItems()\n const activeItem = useRef(initialActiveItem)\n const mainCarouselWrapperRef = useRef(null)\n const carouselTrackWrapperRef = useRef(null)\n const isDragging = useRef(false)\n const isAnimating = useRef(false)\n const windowIsHidden = useRef(false)\n const fluidTotalWrapperScrollValue = useRef(0)\n const slideEndReached = useRef(false)\n const currentWindowWidth = useRef(0)\n const initialWindowWidth = useRef(0)\n const prevSlidedValue = useRef(0)\n const prevItems = useRef(items)\n\n /**\n * Instead of raw values, we store it in\n * useRef for performances reasons during external rerenders\n */\n const draggingSlideTresholdRef = useRef(draggingSlideTreshold ?? 0)\n const itemsPerSlideRef = useRef(itemsPerSlide)\n const gutterRef = useRef(gutter)\n const startEndGutterRef = useRef(startEndGutter)\n const initialActiveItemRef = useRef(initialActiveItem)\n const initialStartingPositionRef = useRef(initialStartingPosition)\n const carouselSlideAxisRef = useRef(carouselSlideAxis)\n\n /**\n * Update inner values during external rerenders!\n */\n itemsPerSlideRef.current = itemsPerSlide\n gutterRef.current = gutter\n startEndGutterRef.current = startEndGutter\n initialActiveItemRef.current = initialActiveItem\n initialStartingPositionRef.current = initialStartingPosition\n carouselSlideAxisRef.current = carouselSlideAxis\n\n const [carouselStyles, setCarouselStyles] = useSpring(() => ({\n y: 0,\n x: 0,\n onChange: ({ value }) => {\n if (mainCarouselWrapperRef.current && freeScroll) {\n mainCarouselWrapperRef.current[\n carouselSlideAxisRef.current === 'x' ? 'scrollLeft' : 'scrollTop'\n ] = Math.abs(value[carouselSlideAxisRef.current])\n }\n },\n }))\n\n useIsomorphicLayoutEffect(() => {\n if (draggingSlideTreshold) {\n draggingSlideTresholdRef.current = draggingSlideTreshold\n } else {\n draggingSlideTresholdRef.current = Math.floor(getSlideValue() / 2 / 2)\n }\n\n resize()\n }, [\n draggingSlideTreshold,\n itemsPerSlide,\n gutter,\n startEndGutter,\n initialActiveItem,\n initialStartingPosition,\n carouselSlideAxis,\n thumbsSlideAxis,\n ])\n\n function getCarouselItem() {\n return carouselTrackWrapperRef.current?.querySelector('.use-spring-carousel-item')\n }\n function getMainCarouselWrapperWidth() {\n if (!mainCarouselWrapperRef.current) {\n throw new Error('mainCarouselWrapperRef is not available')\n }\n return mainCarouselWrapperRef.current.getBoundingClientRect()[\n carouselSlideAxisRef.current === 'x' ? 'width' : 'height'\n ]\n }\n function getCarouselItemWidth() {\n const carouselItem = getCarouselItem()\n if (!carouselItem) {\n throw Error('No carousel items available!')\n }\n return (\n carouselItem.getBoundingClientRect()[\n carouselSlideAxisRef.current === 'x' ? 'width' : 'height'\n ] + gutterRef.current\n )\n }\n function getCurrentSlidedValue() {\n return carouselStyles[carouselSlideAxisRef.current].get()\n }\n function getIfItemsNotFillTheCarousel() {\n return getCarouselItemWidth() * items.length < getMainCarouselWrapperWidth()\n }\n function getFluidWrapperScrollValue() {\n return Math.round(\n Number(\n carouselTrackWrapperRef.current?.[\n carouselSlideAxisRef.current === 'x' ? 'scrollWidth' : 'scrollHeight'\n ],\n ) -\n carouselTrackWrapperRef.current!.getBoundingClientRect()[\n carouselSlideAxisRef.current === 'x' ? 'width' : 'height'\n ],\n )\n }\n function getIsFirstItem() {\n return getCurrentActiveItem() === 0\n }\n function getSlideValue() {\n if (!carouselTrackWrapperRef.current) {\n return 0\n }\n const itemVal = getCarouselItemWidth()\n\n if (slideType === 'fluid' && typeof slideAmount === 'number') {\n if (slideAmount < itemVal) {\n throw new Error('slideAmount must be greater than the width of a single item.')\n }\n return slideAmount\n }\n return itemVal\n }\n function adjustCarouselWrapperPosition(\n ref: HTMLDivElement,\n _initialActiveItem?: number,\n ) {\n const positionProperty = carouselSlideAxisRef.current === 'x' ? 'left' : 'top'\n function getDefaultPositionValue() {\n return getCarouselItemWidth() * items.length\n }\n function setPosition(v: number) {\n if (withLoop) {\n ref.style.top = '0px'\n ref.style[positionProperty] = `-${v - startEndGutterRef.current}px`\n } else {\n ref.style.left = '0px'\n ref.style.top = '0px'\n if (_initialActiveItem && isFirstMount.current) {\n ref.style[positionProperty] = `calc(-${_initialActiveItem} * 100%)`\n }\n }\n }\n function setStartPosition() {\n setPosition(getDefaultPositionValue())\n }\n\n if (slideType === 'fixed') {\n function setCenterPosition(i: number) {\n setPosition(\n getDefaultPositionValue() -\n getSlideValue() * Math.round(((i as number) - 1) / 2),\n )\n }\n function setEndPosition(i: number) {\n setPosition(\n getDefaultPositionValue() - getSlideValue() * Math.round((i as number) - 1),\n )\n }\n\n if (itemsPerSlideRef.current > 1) {\n switch (initialStartingPositionRef.current) {\n default:\n case 'start': {\n setStartPosition()\n break\n }\n case 'center': {\n setCenterPosition(itemsPerSlideRef.current)\n break\n }\n case 'end': {\n setEndPosition(itemsPerSlideRef.current)\n break\n }\n }\n } else {\n setStartPosition()\n }\n } else {\n setStartPosition()\n }\n }\n // eslint-disable-next-line react-hooks/exhaustive-deps\n function resize() {\n currentWindowWidth.current = window.innerWidth\n\n if (slideType === 'fluid') {\n if (getIfItemsNotFillTheCarousel()) {\n setCarouselStyles.start({\n immediate: true,\n [carouselSlideAxisRef.current]: 0,\n })\n return\n }\n fluidTotalWrapperScrollValue.current = getFluidWrapperScrollValue()\n\n if (slideEndReached.current) {\n const nextValue = -fluidTotalWrapperScrollValue.current\n setCarouselStyles.start({\n immediate: true,\n [carouselSlideAxisRef.current]: nextValue,\n })\n }\n\n initialWindowWidth.current = window.innerWidth\n } else {\n setCarouselStyles.start({\n immediate: true,\n [carouselSlideAxisRef.current]: -(getSlideValue() * getCurrentActiveItem()),\n })\n }\n\n fluidTotalWrapperScrollValue.current = getFluidWrapperScrollValue()\n adjustCarouselWrapperPosition(carouselTrackWrapperRef.current!)\n }\n function handleResize() {\n if (window.innerWidth === currentWindowWidth.current || freeScroll) {\n return\n }\n resize()\n }\n // Custom modules\n const { useListenToCustomEvent, emitObservable } = useCustomEventsModule<'use-spring'>()\n const { enterFullscreen, exitFullscreen, getIsFullscreen } =\n useFullscreenModule<'use-spring'>({\n mainCarouselWrapperRef,\n emitObservable,\n handleResize,\n })\n const { thumbsFragment: _thumbsFragment, handleThumbsScroll } = useThumbsModule({\n withThumbs,\n items: items as ReactSpringCarouselItemWithThumbs[],\n thumbsSlideAxis,\n springConfig,\n prepareThumbsData,\n slideType,\n getFluidWrapperScrollValue,\n getSlideValue,\n })\n\n function getWrapperScrollDirection() {\n if (!mainCarouselWrapperRef.current) {\n throw new Error('Missing mainCarouselWrapperRef.current')\n }\n return mainCarouselWrapperRef.current[\n carouselSlideAxisRef.current === 'x' ? 'scrollLeft' : 'scrollTop'\n ]\n }\n function getIfShouldEnableFluidDrag() {\n if (typeof enableFreeScrollDrag === 'boolean') {\n return enableFreeScrollDrag\n } else if (typeof enableFreeScrollDrag === 'function') {\n return enableFreeScrollDrag()\n }\n return false\n }\n\n const bindDrag = useDrag(\n props => {\n const isDragging = props.dragging\n const movement = props.offset[carouselSlideAxisRef.current === 'x' ? 0 : 1]\n const currentMovement = props.movement[carouselSlideAxisRef.current === 'x' ? 0 : 1]\n const prevItemTreshold = currentMovement > draggingSlideTresholdRef.current\n const nextItemTreshold = currentMovement < -draggingSlideTresholdRef.current\n const direction = props.direction[carouselSlideAxisRef.current === 'x' ? 0 : 1]\n function cancelDrag() {\n props.cancel()\n }\n function setDragDirection() {\n if (direction > 0) {\n setSlideActionType('prev')\n } else {\n setSlideActionType('next')\n }\n }\n function emitDragObservable() {\n emitObservable({\n eventName: 'onDrag',\n slideActionType: getSlideActionType(),\n ...props,\n })\n }\n function resetAnimation() {\n if (slideType === 'fluid') {\n if (\n getIfItemsNotFillTheCarousel() ||\n (getIsFirstItem() && getSlideActionType() === 'prev')\n ) {\n setCarouselStyles.start({\n [carouselSlideAxisRef.current]: 0,\n config: {\n velocity: props.velocity,\n ...springConfig,\n },\n })\n } else if (slideEndReached.current && getSlideActionType() === 'next') {\n setCarouselStyles.start({\n [carouselSlideAxisRef.current]: -fluidTotalWrapperScrollValue.current,\n config: {\n velocity: props.velocity,\n ...springConfig,\n },\n })\n } else {\n setCarouselStyles.start({\n [carouselSlideAxisRef.current]: prevSlidedValue.current,\n config: {\n velocity: props.velocity,\n ...springConfig,\n },\n })\n }\n } else {\n setCarouselStyles.start({\n [carouselSlideAxisRef.current]: -(getCurrentActiveItem() * getSlideValue()),\n config: {\n velocity: props.velocity,\n ...springConfig,\n },\n })\n }\n }\n function checkBounds() {\n const nextItemWillExceed =\n Math.abs(getCurrentSlidedValue()) + 100 >= fluidTotalWrapperScrollValue.current\n\n if (nextItemWillExceed && getSlideActionType() === 'next') {\n slideEndReached.current = true\n }\n if (getSlideActionType() === 'prev') {\n slideEndReached.current = false\n }\n }\n\n if (freeScroll && getIfShouldEnableFluidDrag()) {\n if (isDragging) {\n if (!getIsDragging()) {\n setIsDragging(true)\n }\n\n setDragDirection()\n emitDragObservable()\n checkBounds()\n }\n\n setCarouselStyles.start({\n from: {\n [carouselSlideAxisRef.current]: getWrapperScrollDirection(),\n },\n to: {\n [carouselSlideAxisRef.current]: -movement,\n },\n config: {\n velocity: props.velocity,\n friction: 50,\n tension: 1400,\n },\n })\n\n if (getWrapperScrollDirection() === 0 && getSlideActionType() === 'prev') {\n cancelDrag()\n return\n }\n if (props.last) {\n if (getSlideActionType() === 'prev') {\n slideToPrevItem(props.velocity)\n } else {\n slideToNextItem(props.velocity)\n }\n setIsDragging(false)\n }\n return\n }\n\n if (isDragging) {\n if (!getIsDragging()) {\n setIsDragging(true)\n }\n\n emitDragObservable()\n setDragDirection()\n checkBounds()\n\n if (freeScroll) {\n if (getIfShouldEnableFluidDrag()) {\n if (getWrapperScrollDirection() === 0 && getSlideActionType() === 'prev') {\n cancelDrag()\n return\n } else {\n setCarouselStyles.start({\n config: {\n velocity: props.velocity,\n friction: 50,\n tension: 1400,\n },\n from: {\n [carouselSlideAxisRef.current]: getWrapperScrollDirection(),\n },\n to: {\n [carouselSlideAxisRef.current]: -movement,\n },\n })\n }\n }\n return\n } else {\n setCarouselStyles.start({\n [carouselSlideAxisRef.current]: movement,\n config: {\n velocity: props.velocity,\n friction: 50,\n tension: 1000,\n },\n })\n }\n\n if (\n (prevItemTreshold || nextItemTreshold) &&\n getIfItemsNotFillTheCarousel() &&\n slideType === 'fluid'\n ) {\n cancelDrag()\n resetAnimation()\n } else if (\n slideEndReached.current &&\n getSlideActionType() === 'next' &&\n nextItemTreshold\n ) {\n slideEndReached.current = false\n cancelDrag()\n setCarouselStyles.start({\n [carouselSlideAxisRef.current]: -fluidTotalWrapperScrollValue.current,\n })\n } else if (slideWhenThresholdIsReached) {\n if (nextItemTreshold) {\n cancelDrag()\n if (!withLoop && getIsLastItem()) {\n resetAnimation()\n } else {\n slideToNextItem(props.velocity)\n }\n return\n } else if (prevItemTreshold) {\n cancelDrag()\n if (!withLoop && getIsFirstItem()) {\n resetAnimation()\n } else {\n slideToPrevItem(props.velocity)\n }\n return\n }\n }\n }\n\n if (\n props.last &&\n !slideWhenThresholdIsReached &&\n (nextItemTreshold || prevItemTreshold) &&\n !freeScroll\n ) {\n setIsDragging(false)\n if (nextItemTreshold) {\n if (!withLoop && getIsLastItem()) {\n resetAnimation()\n } else {\n slideToNextItem(props.velocity)\n }\n } else if (prevItemTreshold) {\n if (!withLoop && getIsFirstItem()) {\n resetAnimation()\n } else {\n slideToPrevItem(props.velocity)\n }\n }\n return\n }\n\n if (props.last && !nextItemTreshold && !prevItemTreshold) {\n if (!freeScroll) {\n resetAnimation()\n emitObservable({\n eventName: 'onDrag',\n slideActionType: getSlideActionType(),\n ...props,\n })\n }\n }\n },\n {\n enabled: !disableGestures,\n axis: carouselSlideAxisRef.current,\n from: () => {\n if (freeScroll) {\n if (carouselSlideAxisRef.current === 'x') {\n return [-getWrapperScrollDirection(), 0]\n }\n return [0, -getWrapperScrollDirection()]\n }\n return [carouselStyles.x.get(), carouselStyles.y.get()]\n },\n },\n )\n\n function setSlideActionType(type: SlideActionType) {\n slideActionType.current = type\n }\n function getSlideActionType() {\n return slideActionType.current\n }\n function setActiveItem(newItem: number) {\n activeItem.current = newItem\n }\n function getCurrentActiveItem() {\n return activeItem.current\n }\n function getIsAnimating() {\n return isAnimating.current\n }\n function setIsAnimating(val: boolean) {\n isAnimating.current = val\n }\n function setIsDragging(val: boolean) {\n isDragging.current = val\n }\n function getIsDragging() {\n return isDragging.current\n }\n function getPrevItem() {\n const currentActiveItem = getCurrentActiveItem()\n if (currentActiveItem === 0) {\n return items.length - 1\n }\n return currentActiveItem - 1\n }\n function getNextItem() {\n const currentActiveItem = getCurrentActiveItem()\n if (currentActiveItem === items.length - 1) {\n return 0\n }\n return currentActiveItem + 1\n }\n function getIsNextItem(id: string) {\n const itemIndex = findItemIndex(id)\n const activeItem = getCurrentActiveItem()\n if (withLoop && activeItem === items.length - 1) {\n return itemIndex === 0\n }\n return itemIndex === activeItem + 1\n }\n function getIsPrevItem(id: string) {\n const itemIndex = findItemIndex(id)\n const activeItem = getCurrentActiveItem()\n if (withLoop && activeItem === 0) {\n return itemIndex === items.length - 1\n }\n return itemIndex === activeItem - 1\n }\n function findItemIndex(id: string) {\n return items.findIndex(item => item.id === id)\n }\n function getFromValue(from: SlideToItemFnProps['from']) {\n if (typeof from === 'number') {\n return {\n from: {\n [carouselSlideAxisRef.current]: from,\n },\n }\n }\n return {}\n }\n function getToValue(\n customTo: SlideToItemFnProps['customTo'],\n to: SlideToItemFnProps['to'],\n ) {\n if (typeof customTo === 'number') {\n return {\n [carouselSlideAxisRef.current]: customTo,\n }\n }\n if (typeof to !== 'number') {\n throw new Error(`to values is not a number!`)\n }\n return {\n [carouselSlideAxisRef.current]: -(getSlideValue() * to!),\n }\n }\n // eslint-disable-next-line react-hooks/exhaustive-deps\n function slideToItem({\n from,\n to = -1,\n customTo,\n immediate = false,\n onRest = () => {},\n velocity,\n }: SlideToItemFnProps) {\n if (!immediate) {\n setActiveItem(to)\n setIsAnimating(true)\n emitObservable({\n eventName: 'onSlideStartChange',\n slideActionType: getSlideActionType(),\n nextItem: {\n index: slideType === 'fluid' ? -1 : to,\n id: slideType === 'fluid' ? '' : items[to].id,\n },\n })\n }\n prevSlidedValue.current = getToValue(customTo, to)[carouselSlideAxis]\n setCarouselStyles.start({\n ...getFromValue(from),\n to: getToValue(customTo, to),\n ...(velocity\n ? {\n config: {\n ...defaultDragSpringConfig,\n velocity,\n friction: undefined,\n tension: undefined,\n },\n }\n : {\n config: {\n velocity: 0,\n ...springConfig,\n },\n }),\n immediate,\n onRest: val => {\n if (val.finished) {\n setIsDragging(false)\n setIsAnimating(false)\n onRest()\n if (!immediate) {\n emitObservable({\n eventName: 'onSlideChange',\n slideActionType: getSlideActionType(),\n currentItem: {\n index: slideType === 'fluid' ? -1 : getCurrentActiveItem(),\n id: slideType === 'fluid' ? '' : items[getCurrentActiveItem()].id,\n },\n })\n }\n }\n },\n })\n if (enableThumbsWrapperScroll && withThumbs && !immediate) {\n handleThumbsScroll(to, getSlideActionType())\n }\n }\n function getIsLastItem() {\n return getCurrentActiveItem() === items.length - 1\n }\n function slideToPrevItem(velocity?: number[]) {\n setSlideActionType('prev')\n slideEndReached.current = false\n\n if (slideType === 'fluid') {\n slideEndReached.current = false\n\n if (getIfItemsNotFillTheCarousel()) {\n return\n }\n const nextPrevValue = getCurrentSlidedValue() + getSlideValue() + 200\n\n if (freeScroll) {\n const nextValue = mainCarouselWrapperRef.current!.scrollLeft - getSlideValue()\n slideToItem({\n customTo: nextValue < 0 ? 0 : nextValue,\n from: mainCarouselWrapperRef.current!.scrollLeft,\n velocity,\n })\n } else if (nextPrevValue >= 0) {\n if (withLoop) {\n slideToItem({\n from: getCurrentSlidedValue() - getCarouselItemWidth() * items.length,\n velocity,\n customTo:\n getCurrentSlidedValue() -\n getCarouselItemWidth() * items.length +\n getSlideValue(),\n })\n } else {\n slideToItem({\n customTo: 0,\n velocity,\n })\n }\n } else {\n slideToItem({\n customTo: getCurrentSlidedValue() + getSlideValue(),\n velocity,\n })\n }\n } else {\n if ((!withLoop && getCurrentActiveItem() === 0) || windowIsHidden.current) {\n return\n }\n\n if (getIsFirstItem()) {\n slideToItem({\n from: getCurrentSlidedValue() - getSlideValue() * items.length,\n to: items.length - 1,\n velocity,\n })\n } else {\n slideToItem({\n to: getPrevItem(),\n velocity,\n })\n }\n }\n }\n function slideToNextItem(velocity?: number[]) {\n setSlideActionType('next')\n\n if (slideType === 'fluid') {\n if (getIfItemsNotFillTheCarousel()) {\n return\n }\n\n const nextItemWillExceed =\n Math.abs(getCurrentSlidedValue() - getSlideValue()) + 100 >=\n fluidTotalWrapperScrollValue.current\n\n if (freeScroll) {\n const nextValue = mainCarouselWrapperRef.current!.scrollLeft + getSlideValue()\n const willExceed = nextValue > fluidTotalWrapperScrollValue.current\n const val = mainCarouselWrapperRef.current!.scrollLeft + getSlideValue()\n\n slideToItem({\n velocity,\n customTo: willExceed ? fluidTotalWrapperScrollValue.current : val,\n from: mainCarouselWrapperRef.current!.scrollLeft,\n })\n } else if (\n withLoop &&\n Math.abs(getCurrentSlidedValue() - getSlideValue()) >=\n items.length * getCarouselItemWidth()\n ) {\n const currentWidth = getCarouselItemWidth() * items.length\n slideToItem({\n from: getCurrentSlidedValue() + currentWidth,\n customTo: getCurrentSlidedValue() + currentWidth - getSlideValue(),\n velocity,\n })\n } else if (slideEndReached.current) {\n return\n } else if (nextItemWillExceed) {\n slideEndReached.current = true\n slideToItem({\n customTo: -fluidTotalWrapperScrollValue.current,\n velocity,\n })\n } else {\n slideToItem({\n customTo: getCurrentSlidedValue() - getSlideValue(),\n velocity,\n })\n }\n } else {\n if (\n (!withLoop && getCurrentActiveItem() === internalItems.length - 1) ||\n windowIsHidden.current\n ) {\n return\n }\n\n const nextItemWillExceed =\n Math.abs(getCurrentSlidedValue() - getSlideValue() + 25) >\n fluidTotalWrapperScrollValue.current\n\n if (nextItemWillExceed && !getIsDragging()) {\n slideEndReached.current = true\n } else if (slideEndReached.current) {\n slideToItem({\n to: items.length - itemsPerSlideRef.current,\n velocity,\n })\n } else if (getIsLastItem()) {\n slideToItem({\n from: getCurrentSlidedValue() + getSlideValue() * items.length,\n to: 0,\n velocity,\n })\n } else {\n slideToItem({\n to: getNextItem(),\n velocity,\n })\n }\n }\n }\n // eslint-disable-next-line react-hooks/exhaustive-deps\n function _slideToItem(item: string | number) {\n let itemIndex = 0\n\n if (typeof item === 'string') {\n itemIndex = items.findIndex(_item => _item.id === item)\n } else {\n itemIndex = item\n }\n\n if (itemIndex >= items.length) {\n throw Error(\n `The item you want to slide to doesn't exist. This could be due to the fact that \n you provide a wrong id or a higher numeric index.`,\n )\n }\n\n if (\n itemIndex === getCurrentActiveItem() ||\n (items.length !== prevItems.current.length && getCurrentActiveItem() < items.length)\n ) {\n return\n }\n\n const currentItem = findItemIndex(prevItems.current[getCurrentActiveItem()].id)\n const newActiveItem = findItemIndex(items[itemIndex].id)\n\n if (newActiveItem > currentItem) {\n setSlideActionType('next')\n } else {\n setSlideActionType('prev')\n }\n\n slideToItem({\n to: itemIndex,\n })\n }\n function getItemStyles(_itemsPerSlide: number) {\n if (slideType === 'fixed') {\n return {\n ...(carouselSlideAxisRef.current === 'x'\n ? { marginRight: `${gutterRef.current}px` }\n : { marginBottom: `${gutterRef.current}px` }),\n flex: `1 0 calc(100% / ${_itemsPerSlide} - ${\n (gutterRef.current * (_itemsPerSlide - 1)) / _itemsPerSlide\n }px)`,\n }\n }\n return {\n ...(carouselSlideAxisRef.current === 'x'\n ? { marginRight: `${gutterRef.current}px` }\n : { marginBottom: `${gutterRef.current}px` }),\n }\n }\n function getAnimatedWrapperStyles() {\n const percentValue = `calc(100% - ${startEndGutterRef.current * 2}px)`\n return {\n width: carouselSlideAxisRef.current === 'x' ? percentValue : '100%',\n height: carouselSlideAxisRef.current === 'y' ? percentValue : '100%',\n }\n }\n function getOverflowStyles() {\n if (freeScroll) {\n if (carouselSlideAxisRef.current === 'x') {\n return {\n overflowX: 'auto',\n }\n }\n return {\n overflowY: 'auto',\n }\n }\n return {}\n }\n function getWheelEvent() {\n if (freeScroll) {\n return {\n onWheel() {\n carouselStyles[carouselSlideAxisRef.current].stop()\n },\n }\n }\n return {}\n }\n function getTouchAction() {\n if (disableGestures) {\n return 'unset'\n } else if (!touchAction) {\n if (carouselSlideAxisRef.current === 'x') {\n return 'pan-y'\n }\n return 'pan-x'\n }\n return touchAction\n }\n // Perform some check on first mount\n useIsomorphicMount(() => {\n if (itemsPerSlide % 2 === 0 && initialStartingPositionRef.current) {\n throw new Error(\n `initialStartingPosition can be only used if itemsPerSlide is an even value.`,\n )\n }\n if (draggingSlideTresholdRef.current < 0) {\n throw new Error('draggingSlideTreshold must be greater than 0')\n }\n if (draggingSlideTresholdRef.current > getSlideValue() / 2) {\n throw new Error(\n `draggingSlideTreshold must be equal or less than the half of the width of an item, which is ${Math.floor(\n getSlideValue() / 2,\n )}`,\n )\n }\n if (itemsPerSlideRef.current < 1) {\n throw new Error(`The itemsPerSlide prop can't be less than 1.`)\n }\n if (itemsPerSlideRef.current > items.length) {\n throw new Error(\n `The itemsPerSlide prop can't be greater than the total length of the items you provide.`,\n )\n }\n if (initialActiveItemRef.current < 0) {\n throw new Error('The initialActiveItem cannot be less than 0.')\n }\n if (initialActiveItemRef.current > items.length) {\n throw new Error(\n 'The initialActiveItem cannot be greater than the total length of the items you provide.',\n )\n }\n if (!shouldResizeOnWindowResize) {\n console.warn(\n 'You set shouldResizeOnWindowResize={false}; be aware that the carousel could behave in a strange way if you also use the fullscreen functionality or if you change the mobile orientation.',\n )\n }\n })\n useIsomorphicMount(() => {\n function handleVisibilityChange() {\n if (document.hidden) {\n windowIsHidden.current = true\n } else {\n windowIsHidden.current = false\n }\n }\n document.addEventListener('visibilitychange', handleVisibilityChange)\n return () => {\n document.removeEventListener('visibilitychange', handleVisibilityChange)\n }\n })\n useIsomorphicMount(() => {\n isFirstMount.current = false\n fluidTotalWrapperScrollValue.current = getFluidWrapperScrollValue()\n initialWindowWidth.current = window.innerWidth\n currentWindowWidth.current = window.innerWidth\n\n if (initialActiveItem > 0) {\n slideToItem({\n to: initialActiveItem,\n immediate: true,\n })\n setActiveItem(initialActiveItem)\n if (!withLoop && carouselTrackWrapperRef.current) {\n carouselTrackWrapperRef.current.style.top = '0px'\n carouselTrackWrapperRef.current.style.left = '0px'\n }\n }\n })\n useIsomorphicLayoutEffect(() => {\n if (initialActiveItem < items.length && initialActiveItem !== activeItem.current) {\n slideToItem({\n to: initialActiveItem,\n immediate: true,\n })\n setActiveItem(initialActiveItem)\n }\n }, [initialActiveItem])\n useIsomorphicLayoutEffect(() => {\n if (shouldResizeOnWindowResize) {\n window.addEventListener('resize', handleResize)\n return () => {\n window.removeEventListener('resize', handleResize)\n }\n }\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [shouldResizeOnWindowResize])\n useIsomorphicLayoutEffect(() => {\n if (carouselTrackWrapperRef.current) {\n if (carouselSlideAxisRef.current === 'x') {\n carouselTrackWrapperRef.current.style.top = '0px'\n }\n if (carouselSlideAxisRef.current === 'y') {\n carouselTrackWrapperRef.current.style.left = '0px'\n }\n }\n }, [carouselSlideAxis])\n useIsomorphicLayoutEffect(() => {\n fluidTotalWrapperScrollValue.current = getFluidWrapperScrollValue()\n const itemsAreEqual = items.length === prevItems.current.length\n\n if (!itemsAreEqual && items.length < prevItems.current.length) {\n _slideToItem(items.length - 1)\n }\n\n prevItems.current = items\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [getFluidWrapperScrollValue, items])\n\n const contextProps = {\n useListenToCustomEvent,\n getIsFullscreen,\n enterFullscreen,\n exitFullscreen,\n getIsAnimating,\n getIsDragging,\n getIsNextItem,\n getIsPrevItem,\n slideToPrevItem() {\n slideToPrevItem()\n },\n slideToNextItem() {\n slideToNextItem()\n },\n ...(slideType === 'fixed'\n ? {\n slideToItem: _slideToItem,\n getIsActiveItem: (id: string) => {\n return findItemIndex(id) === getCurrentActiveItem()\n },\n getCurrentActiveItem: () => ({\n id: items[getCurrentActiveItem()].id,\n index: getCurrentActiveItem(),\n }),\n }\n : {}),\n }\n\n const handleCarouselFragmentRef = (ref: HTMLDivElement | null) => {\n if (ref) {\n carouselTrackWrapperRef.current = ref\n adjustCarouselWrapperPosition(ref, initialActiveItemRef.current)\n }\n }\n\n function getInitialStyles() {\n const totalValue = (items.length / itemsPerSlide) * 100\n const singleItemValue = 100 / itemsPerSlide\n const cssProp = carouselSlideAxisRef.current === 'x' ? 'left' : 'y'\n const quantityToMove = Math.floor(50 / singleItemValue)\n\n if (slideType === 'fixed') {\n if (initialStartingPositionRef.current === 'center') {\n return {\n [cssProp]: `calc(-${totalValue}% + ${singleItemValue * quantityToMove}%)`,\n }\n }\n if (initialStartingPositionRef.current === 'end') {\n return {\n [cssProp]: `calc(-${totalValue}% + ${singleItemValue * (quantityToMove * 2)}%)`,\n }\n }\n }\n return {\n [cssProp]: `0px`,\n }\n }\n\n const carouselFragment = (\n \n \n
\n {internalItems.map(({ id, renderItem }, index) => {\n return (\n \n {renderItem}\n
\n )\n })}\n \n
\n \n )\n const thumbsFragment = (\n {_thumbsFragment}\n )\n\n return {\n ...contextProps,\n carouselFragment,\n thumbsFragment,\n }\n}\n\nfunction useSpringCarouselContext() {\n const context = useContext(Context)\n if (!context) {\n throw new Error(\n 'useSpringCarouselContext must be used only inside a component that is rendered inside the Carousel.',\n )\n }\n return context as ContextTypes\n}\n\nexport { useSpringCarousel, useSpringCarouselContext }\n","import { TrackingAttributes } from '@context/tracking/types';\nimport { dispatchTrackingEvent } from '@context/tracking/utils';\n\nexport const trackProductImageClick = ({\n timing,\n}: Pick) => {\n dispatchTrackingEvent({\n event: 'element_click',\n element: {\n type: 'image',\n interaction: 'open',\n name: 'pd_image',\n timing,\n action: undefined,\n mode: undefined,\n promo_labels: undefined,\n },\n _clear: true,\n });\n};\n\nexport const trackProductImageTap = ({\n timing,\n}: Pick) => {\n dispatchTrackingEvent({\n event: 'element_click',\n element: {\n type: 'image',\n interaction: 'mobile_tap',\n name: 'pd_image',\n timing,\n action: undefined,\n mode: undefined,\n promo_labels: undefined,\n },\n _clear: true,\n });\n};\n\nexport const trackSwitchingImages = ({\n timing,\n interaction,\n}: Pick & {\n interaction: 'swipe' | 'button';\n}) => {\n dispatchTrackingEvent({\n event: 'element_click',\n element: {\n type: 'image',\n interaction,\n name: 'pd_image',\n timing,\n action: undefined,\n mode: undefined,\n promo_labels: undefined,\n },\n _clear: true,\n });\n};\n","import styled, { css } from 'styled-components';\n\nimport { breakpoints, theme } from '@notino/react-styleguide';\n\nimport { Arrows } from '@components/Slider';\n\nconst contentSizeCss = css`\n max-height: 18.75rem !important;\n max-width: calc(100vw - 40px) !important;\n margin: auto;\n`;\n\nexport const ImageSliderWrapper = styled.div<{ newPdDesign: boolean }>`\n ${({ newPdDesign }) =>\n newPdDesign\n ? css`\n position: relative;\n left: -0.9375rem;\n width: 100vw;\n background-color: ${theme.palette.neutralLighter};\n height: 21.875rem;\n margin-bottom: 1.25rem;\n aspect-ratio: 1 / 1;\n @media (min-width: ${breakpoints.lg}) {\n display: none;\n }\n @media (min-width: ${breakpoints.md}) {\n height: auto;\n }\n `\n : css`\n .use-spring-carousel-track-wrapper {\n align-items: start;\n }\n @media (min-width: ${breakpoints.md}) {\n display: none;\n }\n `}\n\n overflow: hidden;\n`;\n\nexport const PlayerWrapper = styled.div<{ newPdDesign: boolean }>`\n ${({ newPdDesign }) =>\n newPdDesign\n ? css`\n display: flex;\n align-items: center;\n width: 100%;\n\n height: 100%;\n iframe {\n ${contentSizeCss}\n }\n `\n : css`\n ${contentSizeCss}\n iframe {\n ${contentSizeCss}\n }\n `}\n`;\n\nexport const ImgWrapper = styled.div<{\n newPdDesign: boolean;\n isSquare?: boolean;\n}>`\n height: 100%;\n position: relative;\n overflow: hidden;\n\n margin: auto;\n\n > div {\n min-width: 5rem; // notino placeholder logo is not visible without this\n }\n\n ${({ newPdDesign, isSquare }) =>\n newPdDesign\n ? css`\n background-color: ${theme.palette.neutralLighter};\n max-width: 100vw;\n margin: auto;\n\n ${isSquare\n ? css`\n > div:first-child {\n height: 100%;\n }\n img {\n width: 100%;\n }\n `\n : css`\n padding: 0 2.25rem;\n height: 100%;\n img {\n width: auto;\n max-width: 100% !important;\n height: auto;\n max-height: min(21.875rem, 80%) !important;\n }\n > div:first-child {\n // use first-child so we don't target modiface overlay\n max-height: 100%;\n }\n `}\n\n @media (min-width: ${breakpoints.md}) {\n height: 100%;\n }\n img {\n mix-blend-mode: multiply;\n min-height: auto;\n\n max-height: ${isSquare ? 'none' : '100%'};\n }\n `\n : css`\n height: 18.75rem;\n ${contentSizeCss};\n\n padding-left: 1.25rem;\n padding-right: 1.25rem;\n img {\n width: auto;\n max-width: 100% !important;\n height: auto;\n max-height: 100% !important;\n @media (min-width: ${breakpoints.md}) {\n height: 100%;\n width: auto;\n max-width: unset;\n }\n }\n `}\n`;\n\nexport const StyledArrow = styled(Arrows)<{ newPdDesign: boolean }>`\n position: absolute;\n top: 50%;\n transform: translateY(-50%);\n z-index: 2;\n\n ${({ direction }) =>\n direction === 'left'\n ? css`\n left: 0;\n `\n : css`\n right: 0;\n `}\n\n ${({ newPdDesign }) =>\n newPdDesign &&\n css`\n background-color: ${theme.palette.transparent};\n height: 3rem;\n width: 3rem;\n display: flex;\n align-items: center;\n justify-content: center;\n `}\n`;\n","import * as React from 'react';\nimport { useInView } from 'react-intersection-observer';\n\nimport { ImagePlaceholder } from '@notino/react-styleguide';\n\nimport { useTrackingContext } from '@context/tracking/TrackingContext';\nimport { useOnTap } from '@hooks/useOnTap';\n\nimport { useNewPdDesignEnabled } from '../../hooks/useNewPdDesignEnabled';\nimport { trackProductImageTap } from '../tracking';\n\nimport { ImgWrapper } from './styled';\n\nimport { IImageSliderProps } from '.';\n\ninterface Props {\n initialInView?: boolean;\n imgProps: IImageSliderProps['imagesAttrs'][number];\n}\n\nexport const ResizableImagePlaceholder: React.FC<\n React.PropsWithChildren\n> = ({ imgProps, children, initialInView = false }) => {\n const newPdDesign = useNewPdDesignEnabled();\n const [isSquare, setIsSquare] = React.useState(false);\n const [inViewRef, inView] = useInView({ initialInView, triggerOnce: true });\n\n const { getTimeFromInit } = useTrackingContext();\n const onTapRef = useOnTap(() =>\n trackProductImageTap({ timing: getTimeFromInit() })\n );\n\n const handleImageLoad = (elem: HTMLImageElement) => {\n setIsSquare(\n elem.naturalWidth === elem.naturalHeight &&\n !initialInView &&\n !imgProps.src.includes('-o/')\n );\n };\n\n const handleRef = (node: HTMLDivElement) => {\n onTapRef.current = node;\n inViewRef(node);\n };\n\n return (\n \n {inView && (\n \n )}\n {children}\n \n );\n};\n","import * as React from 'react';\n\nconst TAP_MAX_TIME_MS = 300;\nconst TAP_MAX_OFFSET = 20;\n\nexport const useOnTap = (\n callback: () => void\n) => {\n const ref = React.useRef(null);\n const startTime = React.useRef(0);\n const startPosition = React.useRef({ x: 0, y: 0 });\n\n const callbackRef = React.useRef(callback);\n React.useEffect(() => {\n callbackRef.current = callback;\n });\n\n React.useEffect(() => {\n const el = ref.current;\n\n const handleStart = (e: TouchEvent) => {\n const touch = e.changedTouches.item(0);\n startPosition.current = { x: touch.clientX, y: touch.clientY };\n startTime.current = new Date().getTime();\n };\n\n const handleEnd = (e: TouchEvent) => {\n const now = new Date().getTime();\n const touch = e.changedTouches.item(0);\n const isQuick = now - startTime.current < TAP_MAX_TIME_MS;\n const isClose =\n Math.abs(touch.clientX - startPosition.current.x) < TAP_MAX_OFFSET &&\n Math.abs(touch.clientY - startPosition.current.y) < TAP_MAX_OFFSET;\n\n if (isQuick && isClose) {\n callbackRef.current();\n }\n };\n\n el?.addEventListener('touchstart', handleStart);\n el?.addEventListener('touchend', handleEnd);\n\n return () => {\n el?.addEventListener('touchstart', handleStart);\n el?.removeEventListener('touchend', handleEnd);\n };\n }, []);\n\n return ref;\n};\n","import{_ as e}from\"./_rollupPluginBabelHelpers-fc5c8eb1.js\";import*as r from\"react\";import{Icon as t}from\"./index.js\";import\"./ThemeContext.js\";import\"styled-components\";var l=function(l){return r.createElement(t,e({},l,{viewBox:\"0 0 25 34\"}),r.createElement(\"path\",{fill:\"currentColor\",d:\"M8.6 0C5.9.4 3.5 2.5 2.8 5.3c-.3 1-.3 2.6 0 3.6s.5 1.3 1.1 1.3c.5 0 .8-.2.9-.6.2-.3.2-.4 0-1-.4-1.4-.2-3 .5-4.2 1.4-2.3 4.2-3 6.5-1.8 1.3.7 2.3 2.2 2.5 3.8.1.7.1.9-.1 1.6-.1.7-.1.9 0 1.2.1.4.5.6.9.6s.8-.4.9-.8c.4-1.4.4-3.1-.2-4.6C14.7 1.5 11.7-.3 8.6 0z\"}),r.createElement(\"g\",null,r.createElement(\"path\",{fill:\"currentColor\",d:\"M8.5 4.1c-.9.3-1.5.9-1.9 1.8-.2.4-.2.5-.2 7.4v7l-.7-1.2c-.8-1.3-1.1-1.7-1.8-2.1-.4-.2-.6-.2-1.2-.2s-.8 0-1.3.3c-1 .5-1.4 1.3-1.4 2.7 0 1.1.2 1.8 1.3 4.6 1.5 3.7 3.2 6.9 4.8 8.7.7.9 1 1.1 1.6.9.5-.1.7-.6.7-1.1 0-.3-.2-.5-.5-.9-1.7-2-3.6-5.6-5.3-10.3-.3-.8-.4-1.2-.4-1.8 0-.7 0-.7.2-.9.3-.3.7-.3 1-.1.1.1.9 1.4 1.7 2.9.8 1.5 1.6 2.8 1.8 3 .4.4.9.4 1.3 0l.3-.3v-9c0-9.8 0-9.2.6-9.5.4-.2 1-.1 1.3.1 0 0 .2.2.3.4.1.5 0 6.3 0 6.3.1 7 0 6.7.7 6.9.4.1.9 0 1.1-.4.2-.3.2-.5.2-3 0-3 0-3.1.5-3.4.3-.2.9-.2 1.2 0 .5.3.5.4.5 3.6 0 2.7 0 3 .2 3.2.4.6 1.3.6 1.6 0 .2-.3.2-.5.2-2.5 0-2.5 0-2.7.5-2.9.5-.3 1.2-.1 1.5.4.1.2.1.7.1 2.5 0 2.5 0 2.6.5 2.9.3.2.6.2.9 0 .5-.2.5-.5.5-2 0-1.2 0-1.3.2-1.6.1-.2.4-.4.5-.4.5-.2.9.1 1.2.7.2.4.2.5.3 3.2.1 5.1-.3 9-1.1 11.4-.3 1.1-.3 1.3 0 1.7.5.5 1.2.4 1.5-.2.6-1 1.2-4.2 1.4-7.6.1-1.6.1-7.3 0-8.1-.3-2.3-2.3-3.8-4.1-3-.1 0-.2 0-.3-.3-.2-.5-.9-1.2-1.5-1.5-.5-.3-.7-.3-1.2-.3-.4 0-.8.1-1.1.1-.4.1-.4.1-.5 0 0-.1-.3-.4-.5-.6-.7-.8-1.7-1.1-2.8-.9l-.4.1V8.5c0-2.3 0-2.3-.3-2.8-.5-1-1.4-1.7-2.6-1.7-.5-.1-.8 0-1.1.1z\"})))};export{l as TouchIcon};\n//# sourceMappingURL=TouchIcon.js.map\n","import { styled } from '@notino/react-styleguide';\n\nexport const ModiFaceWrapper = styled.div`\n position: absolute;\n top: 50%;\n left: 50%;\n transform: translate(-50%, -50%);\n\n width: 6.25rem;\n height: 6.25rem;\n border-radius: 50%;\n\n background: rgba(0, 0, 0, 0.75);\n\n display: flex;\n flex-direction: column;\n align-items: center;\n justify-content: center;\n cursor: pointer;\n`;\n\nexport const TextWrapper = styled.div`\n font-size: 0.75rem;\n color: ${({ theme }) => theme.palette.basicInverse};\n font-weight: normal;\n padding: 0.25rem 10%;\n`;\n","import * as React from 'react';\nimport { FormattedMessage } from 'react-intl';\n\nimport { Colors, TouchIcon } from '@notino/react-styleguide';\n\nimport messages from '../../../messages';\n\nimport { ModiFaceWrapper, TextWrapper } from './styled';\n\ninterface IModiFaceOverlayProps {\n onClick: (e: React.MouseEvent) => void;\n}\n\nexport const ModiFaceOverlay: React.FC = React.memo(\n ({ onClick }) => (\n \n \n \n \n \n \n )\n);\n","import * as React from 'react';\n\nimport styled from 'styled-components';\n\nimport { theme } from '@notino/react-styleguide';\n\nimport { useNewPdDesignEnabled } from '../../hooks/useNewPdDesignEnabled';\n\ntype Props = { count: number; index: number };\n\nconst Track = styled.div`\n position: absolute;\n bottom: 0;\n left: 0;\n right: 0;\n`;\n\nconst Indicator = styled.div<{ width: string; left: string }>`\n height: 0.2rem;\n background-color: ${theme.palette.neutralDark};\n position: relative;\n border-radius: 1rem;\n width: ${({ width }) => width};\n left: ${({ left }) => left};\n transition: left 250ms;\n`;\n\nexport const ProgressBar = ({ count, index }: Props) => {\n const isNewDesign = useNewPdDesignEnabled();\n\n if (!isNewDesign || count < 2) {\n return null;\n }\n\n const width = 100 / count;\n const left = width * index;\n\n return (\n \n );\n};\n","import * as React from 'react';\nimport { useSpringCarousel } from 'react-spring-carousel';\n\nimport ErrorBoundary from '@components/ErrorBoundary';\nimport { ResizableImagePlaceholder } from '@containers/ProductDetailContainer/ProductDetail/ProductImageGallery/ImageSlider/ResizableImagePlaceholder';\nimport { IImageProperties } from '@helpers/utils';\n\nimport { youtubeIframe } from '../../../utils';\nimport { useNewPdDesignEnabled } from '../../hooks/useNewPdDesignEnabled';\nimport { ModiFaceOverlay } from '../ModiFaceOverlay';\n\nimport { ProgressBar } from './ProgressBar';\nimport { ImageSliderWrapper, PlayerWrapper, StyledArrow } from './styled';\nimport { useTrackSwiping } from './useTrackSwiping';\n\nexport interface IImageSliderProps {\n imagesAttrs: IImageProperties[];\n videos?: string[];\n hasModiface?: boolean;\n openModifaceModal?: () => void;\n}\n\nexport const ImageSlider: React.FC = React.memo(\n ({ imagesAttrs, videos = [], hasModiface, openModifaceModal }) => {\n const newPdDesign = useNewPdDesignEnabled();\n const { trackSwiping } = useTrackSwiping();\n const [currentIndex, setCurrentIndex] = React.useState(0);\n\n const items: Array<{ id: string; renderItem: JSX.Element }> = React.useMemo(\n () => [\n ...imagesAttrs\n .map((imgProps, index) => {\n const showModifaceOverlay = hasModiface && index === 0;\n\n // if modiface overlay on mobile, clone main image so customer can swipe to image a view it without overlay\n if (showModifaceOverlay) {\n return [\n {\n id: `${imgProps.src}-modiface`,\n renderItem: (\n \n \n \n ),\n },\n {\n id: imgProps.src,\n renderItem: ,\n },\n ];\n }\n\n return {\n id: imgProps.src,\n renderItem: (\n \n ),\n };\n })\n .flat(),\n\n ...videos.map((video) => ({\n id: video,\n renderItem: (\n \n ),\n })),\n ],\n [hasModiface, imagesAttrs, newPdDesign, openModifaceModal, videos]\n );\n\n const hasNext = currentIndex < items.length - 1;\n const hasPrev = currentIndex > 0;\n\n const {\n carouselFragment,\n useListenToCustomEvent,\n slideToNextItem,\n slideToPrevItem,\n } = useSpringCarousel({\n items,\n });\n\n useListenToCustomEvent((event) => {\n if (event.eventName === 'onDrag') {\n trackSwiping('swipe');\n } else if (event.eventName === 'onSlideStartChange') {\n setCurrentIndex(event.nextItem.index);\n }\n });\n\n return (\n \n \n {hasPrev && (\n {\n slideToPrevItem();\n trackSwiping('button');\n }}\n />\n )}\n\n {carouselFragment}\n\n \n\n {hasNext && (\n {\n slideToNextItem();\n trackSwiping('button');\n }}\n />\n )}\n \n \n );\n }\n);\n","import * as React from 'react';\n\nimport { useTrackingContext } from '@context/tracking/TrackingContext';\n\nimport { useProductDetailContext } from '../../ProductDetailContext';\nimport { trackSwitchingImages } from '../tracking';\n\nexport const useTrackSwiping = () => {\n const context = useProductDetailContext(false);\n const variantId = context?.currentVariant.id || '';\n const hasAlreadyTracked = React.useRef(false);\n const { getTimeFromInit } = useTrackingContext();\n\n React.useEffect(() => {\n hasAlreadyTracked.current = false;\n }, [variantId]);\n\n const trackSwiping = (interaction: 'swipe' | 'button') => {\n if (hasAlreadyTracked.current) {\n return;\n }\n hasAlreadyTracked.current = true;\n trackSwitchingImages({ timing: getTimeFromInit(), interaction });\n };\n\n return { trackSwiping };\n};\n","import { useRef, MutableRefObject } from 'react'\nimport screenfull from 'screenfull'\nimport { EmitObservableFn } from '../types'\nimport { useIsomorphicMount } from '../utils'\n\ntype FullscreenModule = {\n mainCarouselWrapperRef: MutableRefObject\n emitObservable: EmitObservableFn\n handleResize?(): void\n}\n\nexport function useFullscreenModule({\n mainCarouselWrapperRef,\n emitObservable,\n handleResize,\n}: FullscreenModule) {\n const isFullscreen = useRef(false)\n\n useIsomorphicMount(() => {\n function handleFullscreenChange() {\n if (document.fullscreenElement) {\n setIsFullscreen(true)\n emitObservable({\n eventName: 'onFullscreenChange',\n isFullscreen: true,\n })\n\n handleResize && handleResize()\n }\n\n if (!document.fullscreenElement) {\n setIsFullscreen(false)\n emitObservable({\n eventName: 'onFullscreenChange',\n isFullscreen: false,\n })\n handleResize && handleResize()\n }\n }\n\n if (screenfull.isEnabled) {\n screenfull.on('change', handleFullscreenChange)\n return () => {\n if (screenfull.isEnabled) {\n screenfull.off('change', handleFullscreenChange)\n }\n }\n }\n })\n\n function setIsFullscreen(_isFullscreen: boolean) {\n isFullscreen.current = _isFullscreen\n }\n\n function getIsFullscreen() {\n return isFullscreen.current\n }\n\n function enterFullscreen(elementRef?: HTMLElement) {\n if (screenfull.isEnabled) {\n screenfull.request((elementRef || mainCarouselWrapperRef.current) as Element)\n }\n }\n\n function exitFullscreen() {\n screenfull.isEnabled && screenfull.exit()\n }\n\n return {\n enterFullscreen,\n exitFullscreen,\n getIsFullscreen,\n }\n}\n","import { useRef } from 'react'\nimport { useSpring, SpringConfig, animated } from 'react-spring'\nimport { useIsomorphicMount } from 'src/utils'\nimport { UseSpringCarouselProps, SlideActionType } from '../types'\nimport { ReactSpringCarouselItemWithThumbs } from '../types/useSpringCarousel'\nimport { PrepareThumbsData } from '../types/index'\n\ntype OffsetDimension = 'offsetWidth' | 'offsetHeight'\ntype OffsetDirection = 'offsetLeft' | 'offsetTop'\ntype ScrollDirection = 'scrollLeft' | 'scrollTop'\n\ntype Props = {\n items: ReactSpringCarouselItemWithThumbs[]\n withThumbs: boolean\n slideType: UseSpringCarouselProps['slideType']\n thumbsSlideAxis: UseSpringCarouselProps['thumbsSlideAxis']\n springConfig: SpringConfig\n prepareThumbsData?: UseSpringCarouselProps['prepareThumbsData']\n getFluidWrapperScrollValue?(): number\n getSlideValue?(): number\n}\n\nexport function useThumbsModule({\n items,\n withThumbs,\n thumbsSlideAxis = 'x',\n springConfig,\n prepareThumbsData,\n getFluidWrapperScrollValue = () => 0,\n getSlideValue = () => 0,\n slideType,\n}: Props) {\n const internalThumbsWrapperRef = useRef(null)\n const [thumbListStyles, setThumbListStyles] = useSpring(() => ({\n x: 0,\n y: 0,\n config: springConfig,\n onChange: ({ value }) => {\n if (internalThumbsWrapperRef.current && slideType === 'fluid') {\n internalThumbsWrapperRef.current[\n thumbsSlideAxis === 'x' ? 'scrollLeft' : 'scrollTop'\n ] = Math.abs(value[thumbsSlideAxis])\n }\n },\n }))\n\n useIsomorphicMount(() => {\n if (withThumbs && !internalThumbsWrapperRef.current) {\n throw new Error(\n \"The thumbs wrapper is not defined. If you've passed a Functional component, be sure to wrap your component in forwardRef.\",\n )\n }\n })\n\n function getCurrentThumbScrollValue() {\n return internalThumbsWrapperRef.current![\n thumbsSlideAxis === 'x' ? 'scrollLeft' : 'scrollTop'\n ]\n }\n function getThumbsTotalScrollableValue() {\n return Math.round(\n Number(\n internalThumbsWrapperRef.current?.[\n thumbsSlideAxis === 'x' ? 'scrollWidth' : 'scrollHeight'\n ],\n ) -\n internalThumbsWrapperRef.current!.getBoundingClientRect()[\n thumbsSlideAxis === 'x' ? 'width' : 'height'\n ],\n )\n }\n\n function getThumbSlideValue() {\n const thumbSlideTotal = Math.round(getFluidWrapperScrollValue() / getSlideValue())\n const totalScrollableValue = getThumbsTotalScrollableValue()\n return totalScrollableValue / thumbSlideTotal\n }\n\n function handleThumbsScroll(activeItem: number, actionType?: SlideActionType) {\n if (slideType === 'fluid') {\n const totalScrollableValue = getThumbsTotalScrollableValue()\n\n if (actionType === 'next') {\n const nextValue = getCurrentThumbScrollValue() + getThumbSlideValue()\n setThumbListStyles.start({\n from: {\n [thumbsSlideAxis]: getCurrentThumbScrollValue(),\n },\n to: {\n [thumbsSlideAxis]:\n nextValue > totalScrollableValue ? totalScrollableValue : nextValue,\n },\n })\n }\n if (actionType === 'prev') {\n const nextValue = getCurrentThumbScrollValue() - getThumbSlideValue()\n setThumbListStyles.start({\n from: {\n [thumbsSlideAxis]: getCurrentThumbScrollValue(),\n },\n to: {\n [thumbsSlideAxis]: nextValue < 0 ? 0 : nextValue,\n },\n })\n }\n } else {\n function getOffsetDirection() {\n return thumbsSlideAxis === 'x' ? 'offsetLeft' : 'offsetTop'\n }\n function getOffsetDimension() {\n return thumbsSlideAxis === 'x' ? 'offsetWidth' : 'offsetHeight'\n }\n function getScrollDirecton() {\n return thumbsSlideAxis === 'x' ? 'scrollLeft' : 'scrollTop'\n }\n function getThumbNode() {\n return internalThumbsWrapperRef.current!.querySelector(\n `#thumb-${items[activeItem].id}`,\n ) as HTMLElement\n }\n function getThumbOffsetPosition({\n thumbNode,\n offsetDirection,\n offsetDimension,\n }: {\n thumbNode: HTMLElement\n offsetDirection: OffsetDirection\n offsetDimension: OffsetDimension\n }) {\n return thumbNode[offsetDirection] + thumbNode[offsetDimension] / 2\n }\n function getThumbScrollDimension({\n thumbWrapper,\n offsetDimension,\n }: {\n thumbWrapper: HTMLDivElement\n offsetDimension: OffsetDimension\n }) {\n return thumbWrapper[offsetDimension] / 2\n }\n function getScrollFromValue({\n thumbWrapper,\n scrollDirection,\n }: {\n thumbWrapper: HTMLDivElement\n scrollDirection: ScrollDirection\n }) {\n return thumbWrapper[scrollDirection]\n }\n function getScrollToValue({\n thumbWrapper,\n thumbOffsetPosition,\n thumbScrollDimension,\n offsetDimension,\n }: {\n thumbWrapper: HTMLDivElement\n thumbOffsetPosition: number\n thumbScrollDimension: number\n offsetDimension: OffsetDimension\n }) {\n const scrollDimensionProperty =\n thumbsSlideAxis === 'x' ? 'scrollWidth' : 'scrollHeight'\n\n if (\n activeItem === items.length - 1 ||\n thumbOffsetPosition - thumbScrollDimension >\n thumbWrapper[scrollDimensionProperty] - thumbWrapper[offsetDimension]\n ) {\n return thumbWrapper[scrollDimensionProperty] - thumbWrapper[offsetDimension]\n }\n if (activeItem === 0) {\n return 0\n }\n\n return thumbOffsetPosition - thumbScrollDimension\n }\n\n const thumbNode = getThumbNode()\n\n if (thumbNode) {\n const thumbWrapper = internalThumbsWrapperRef.current!\n const offsetDirection = getOffsetDirection()\n const offsetDimension = getOffsetDimension()\n const scrollDirection = getScrollDirecton()\n const thumbOffsetPosition = getThumbOffsetPosition({\n thumbNode,\n offsetDimension,\n offsetDirection,\n })\n const thumbScrollDimension = getThumbScrollDimension({\n thumbWrapper,\n offsetDimension,\n })\n\n const fromValue = getScrollFromValue({\n thumbWrapper,\n scrollDirection,\n })\n const toValue = getScrollToValue({\n thumbWrapper,\n thumbOffsetPosition,\n thumbScrollDimension,\n offsetDimension,\n })\n\n setThumbListStyles.start({\n from: {\n [thumbsSlideAxis]: fromValue,\n },\n to: {\n [thumbsSlideAxis]: actionType === 'prev' && toValue < 0 ? 0 : toValue,\n },\n onChange: ({ value }) => {\n if (thumbsSlideAxis === 'x') {\n internalThumbsWrapperRef!.current!.scrollLeft = value.x\n } else {\n internalThumbsWrapperRef!.current!.scrollTop = value.y\n }\n },\n })\n }\n }\n }\n function handlePrepareThumbsData() {\n function getPreparedItems(\n _items: ReturnType,\n ): ReturnType {\n return _items.map(i => ({\n id: i.id,\n renderThumb: i.renderThumb,\n }))\n }\n\n if (prepareThumbsData) {\n return prepareThumbsData(getPreparedItems(items))\n }\n return getPreparedItems(items)\n }\n\n const thumbsFragment = withThumbs ? (\n {\n thumbListStyles[thumbsSlideAxis].stop()\n }}\n style={{\n display: 'flex',\n flex: 1,\n position: 'relative',\n width: '100%',\n height: '100%',\n flexDirection: thumbsSlideAxis === 'x' ? 'row' : 'column',\n ...(thumbsSlideAxis === 'x'\n ? { overflowX: 'auto' }\n : {\n overflowY: 'auto',\n maxHeight: '100%',\n }),\n }}\n >\n {handlePrepareThumbsData().map(({ id, renderThumb }) => {\n const thumbId = `thumb-${id}`\n return (\n \n {renderThumb}\n
\n )\n })}\n \n ) : null\n\n return {\n thumbsFragment,\n handleThumbsScroll,\n }\n}\n","import styled, { css } from 'styled-components';\n\nimport { breakpoints } from '@notino/react-styleguide';\n\nimport { ImageProps } from './utils';\n\nexport const BufferImage = styled.img`\n position: absolute;\n opacity: 0;\n z-index: -1;\n`;\n\ntype MainImgProps = ImageProps & {\n newPd: boolean;\n};\nexport const MainImg = styled.img`\n display: none;\n\n ${({ newPd, hasShadow, fullHeight }) => css`\n ${newPd &&\n css`\n height: auto;\n\n ${hasShadow &&\n css`\n margin-bottom: -2rem;\n `}\n\n ${fullHeight\n ? css`\n height: 100%;\n\n max-height: unset !important;\n max-width: unset !important;\n\n aspect-ratio: unset;\n\n @media (min-width: ${breakpoints.xl}) {\n height: calc(100% - 3rem); // 1.5rem padding on top and bottom\n width: unset;\n }\n `\n : css`\n max-height: min(85%, 21.875rem) !important;\n\n @media (min-width: 1280px) {\n max-height: min(85%, 25rem) !important;\n }\n @media (min-width: 1680px) {\n max-height: min(85%, 28.125rem) !important;\n }\n `}\n `}\n\n ${!newPd &&\n css`\n width: auto;\n height: auto;\n `}\n `}\n\n @media (min-width: ${breakpoints.sm}) {\n display: block;\n max-width: 100%;\n max-height: 100%;\n\n cursor: zoom-in;\n flex-grow: 0;\n transition: opacity 0.5s;\n }\n`;\n","export function hasImageShadow(src: string) {\n return src.includes('-o/');\n}\n\nexport type ImageProps = {\n hasShadow: boolean;\n fullHeight: boolean;\n};\n\nexport function getImageProps(img: HTMLImageElement, idx: number): ImageProps {\n const aspectRatio = img.naturalWidth / img.naturalHeight;\n\n const hasShadow = hasImageShadow(img.src);\n const canBeSized = idx !== 0 && !hasShadow;\n const isMoodImage = aspectRatio === 1;\n\n return {\n fullHeight: isMoodImage && canBeSized,\n hasShadow,\n };\n}\n","import * as React from 'react';\n\nimport { useNewPdDesignEnabled } from '@containers/ProductDetailContainer/ProductDetail/hooks/useNewPdDesignEnabled';\nimport { IImageProperties } from '@helpers/utils';\n\nimport { MainImg, BufferImage } from './styled';\nimport { getImageProps, hasImageShadow, ImageProps } from './utils';\n\ninterface IMainImageProps {\n imageAttrs: IImageProperties;\n onClick: React.MouseEventHandler;\n index: number;\n}\n\nexport const MainImage: React.FC = (props) => {\n const { imageAttrs, onClick, index } = props;\n const newPdDesignEnabled = useNewPdDesignEnabled();\n\n const [imgProps, setImageProps] = React.useState<\n ImageProps & IMainImageProps['imageAttrs']\n >(() => ({\n hasShadow: hasImageShadow(imageAttrs.src),\n fullHeight: false,\n ...props.imageAttrs,\n }));\n\n const handleImageLoad: React.ReactEventHandler = (\n event\n ) => {\n setImageProps({\n ...getImageProps(event.currentTarget, index),\n ...props.imageAttrs,\n });\n };\n\n return (\n <>\n \n\n {/* Preload image and calculate its size before we render it so there is no flickering*/}\n \n >\n );\n};\n","import styled, { css } from 'styled-components';\n\nimport { breakpoints, Col, theme } from '@notino/react-styleguide';\n\nexport const StyledCol = styled(Col)<{ newPdDesign: boolean }>`\n ${({ newPdDesign }) =>\n newPdDesign &&\n css`\n padding: 0;\n `};\n`;\n\nexport const MainImgWrapper = styled.div<{ newPdDesign?: boolean }>`\n position: relative;\n display: none;\n\n width: 21.875rem;\n max-width: 100%;\n height: 21.875rem;\n\n margin: 0 auto;\n text-align: center;\n\n ${({ newPdDesign }) =>\n newPdDesign\n ? css`\n height: 70vh;\n max-height: 46.875rem;\n width: 100%;\n cursor: zoom-in;\n background-color: ${theme.palette.neutralLighter};\n @media (min-width: ${breakpoints.lg}) {\n display: block;\n }\n `\n : css`\n @media (min-width: ${breakpoints.md}) {\n display: block;\n }\n `};\n`;\n\nexport const PictogramWrapper = styled.div<{ newPd: boolean }>`\n position: absolute;\n top: ${(props) => (props.newPd ? '1.25rem' : '0.25rem')};\n left: 0.25rem;\n\n display: flex;\n flex-direction: column;\n align-items: center;\n gap: 0.5rem;\n\n z-index: 1;\n\n ${(props) =>\n props.newPd &&\n css`\n @media (min-width: ${breakpoints.md}) {\n top: 1rem;\n left: 1rem;\n }\n `}\n`;\n\nexport const Container = styled.div<{\n brandLogo: boolean;\n newPdDesign: boolean;\n}>`\n width: 100%;\n position: relative;\n text-align: center;\n height: 20rem;\n margin-top: 1.25rem;\n\n ${({ newPdDesign, brandLogo }) =>\n newPdDesign\n ? css`\n height: auto;\n @media (min-width: ${breakpoints.lg}) {\n margin-top: 0;\n }\n `\n : css`\n @media (min-width: ${breakpoints.md}) {\n height: auto;\n ${!brandLogo ? 'margin-top: 0;' : ''};\n }\n `};\n`;\n\nexport const ImageWrapper = styled.div<{ newPdDesign?: boolean }>`\n width: 100%;\n height: 100%;\n display: none;\n justify-content: center;\n align-items: center;\n\n ${({ newPdDesign }) =>\n newPdDesign\n ? css`\n overflow: hidden;\n mix-blend-mode: multiply;\n > img,\n > ${VideoWrapper} {\n max-height: 25rem;\n @media (min-width: ${breakpoints.lg}) {\n max-height: min(85%, 28.125rem);\n max-width: min(85%, 28.125rem);\n }\n }\n `\n : css`\n padding-left: 1.25rem;\n padding-right: 1.25rem;\n `};\n\n @media (min-width: ${breakpoints.md}) {\n display: flex;\n }\n`;\n\nexport const VideoWrapper = styled.div`\n position: relative;\n display: flex;\n flex-direction: column;\n align-items: center;\n justify-content: center;\n\n img {\n max-width: 100%;\n }\n\n :hover {\n cursor: pointer;\n svg {\n opacity: 0.95;\n }\n }\n\n svg {\n opacity: 0.8;\n position: absolute;\n top: 50%;\n left: 50%;\n transform: translate(-50%, -50%);\n }\n`;\n","import { getImageAttributes } from '@notino/react-web-utils';\nimport { GetProductViewQuery } from '@notino/shared/definitions/types';\n\nexport const getImagesAttributes = (\n images: GetProductViewQuery['productDetailByMasterId'][number]['variants'][number]['imagesByCategory']['mainImage'][],\n domain: string,\n sourceKey: string\n) => images.map((image) => getImageAttributes(image, domain, sourceKey));\n","import { getImageAttributes } from '@notino/react-web-utils';\n\nexport const getVideoAttrs = (video: string, type: string = 'default') =>\n getImageAttributes(\n {\n src: `https://i.ytimg.com/vi/${video}/${type}.jpg`,\n alt: `${video}`,\n isVideo: true,\n },\n '',\n 'video'\n );\n","import * as React from 'react';\n\nimport {\n Colors,\n PlayCircleIcon,\n ModalContext,\n ModalModel,\n} from '@notino/react-styleguide';\nimport { GetProductViewQuery } from '@notino/shared/definitions/types';\n\nimport { ModalGallery } from '@components/ModalGallery';\nimport { useTrackingContext } from '@context/tracking/TrackingContext';\n\nimport { useNewPdDesignEnabled } from '../hooks/useNewPdDesignEnabled';\n\nimport { THUMBS_SLIDER_CONFIG } from './constants';\nimport { ImageSlider } from './ImageSlider';\nimport { MainImage } from './MainImg';\nimport { ModiFaceOverlay } from './ModiFaceOverlay';\nimport {\n Container,\n MainImgWrapper,\n ImageWrapper,\n VideoWrapper,\n StyledCol,\n PictogramWrapper,\n} from './styled';\nimport { Thumbs } from './Thumbs';\nimport { ScrollThumbs } from './Thumbs/ScrollThumbs';\nimport { trackProductImageClick } from './tracking';\nimport { getImagesAttributes, getVideoAttrs } from './utils';\n\nexport interface IProductImageGalleryProps {\n images: GetProductViewQuery['productDetailByMasterId'][number]['variants'][number]['imagesByCategory'];\n videos: string[];\n domain: string;\n hasModiface?: boolean;\n modifaceMobileEnabled?: boolean;\n modifaceDesktopEnabled?: boolean;\n openModifaceModal?: () => void;\n isBrandLogoVisible?: boolean;\n pictogram?: JSX.Element | null;\n variantId: string;\n}\n\nexport const ProductImageGallery: React.FC = ({\n domain,\n images,\n videos,\n openModifaceModal,\n hasModiface = false,\n modifaceMobileEnabled = false,\n modifaceDesktopEnabled = false,\n isBrandLogoVisible = false,\n pictogram,\n variantId,\n}) => {\n const [imageIndex, setImageIndex] = React.useState(0);\n const newPdDesign = useNewPdDesignEnabled();\n const { toggleModal, hideModal } = ModalContext.useModalContext();\n\n const { getTimeFromInit } = useTrackingContext();\n\n React.useEffect(() => {\n setImageIndex(0);\n }, [images]);\n\n const { imagesAttrsOrder, imagesAttrsZoom, imagesAttrsOriginal } =\n React.useMemo(() => {\n const imagesArray = [\n images.mainImage,\n images.previewImage,\n ...images.others,\n ].filter((item) => item);\n\n const [mainImage, ...otherImages] = imagesArray;\n return {\n imagesAttrsOrder: [\n ...getImagesAttributes(imagesArray, domain, 'order'),\n ...videos.map((video) => getVideoAttrs(video)),\n ],\n imagesAttrsZoom: getImagesAttributes(imagesArray, domain, 'zoomImage'),\n imagesAttrsOriginal: [\n // let first image be in a little worse quality, so it loads faster for better LCP and UX\n ...getImagesAttributes([mainImage], domain, 'detailMain'),\n ...getImagesAttributes(otherImages, domain, 'detailGallery'),\n ],\n };\n }, [images, domain, videos]);\n\n const handleMainImageClick = React.useCallback(\n (imgIndex?: number) => (e: React.MouseEvent) => {\n e.stopPropagation();\n trackProductImageClick({\n timing: getTimeFromInit(),\n });\n\n if (imgIndex) {\n setImageIndex(imgIndex);\n }\n\n toggleModal(\n ,\n {\n center: true,\n size: ModalModel.Sizes.s1200,\n onlyTopBorder: true,\n noBorders: true,\n }\n );\n },\n [\n getTimeFromInit,\n toggleModal,\n hideModal,\n imagesAttrsOrder,\n imagesAttrsZoom,\n videos,\n ]\n );\n\n const videoIndex = imageIndex - imagesAttrsOriginal.length;\n const isVideo: boolean = videoIndex >= 0;\n\n const modifaceEnabledMobile = hasModiface && modifaceMobileEnabled;\n const modifaceEnabledDesktop = hasModiface && modifaceDesktopEnabled;\n\n return (\n \n {pictogram ? (\n {pictogram}\n ) : null}\n\n \n\n \n \n \n {!isVideo && (\n \n )}\n\n {isVideo && (\n \n
\n\n \n \n )}\n\n {modifaceEnabledDesktop && imageIndex === 0 && (\n {\n e.stopPropagation();\n openModifaceModal();\n }}\n />\n )}\n \n \n\n {imagesAttrsOrder.length > 1 && !newPdDesign && (\n \n )}\n\n {imagesAttrsOrder.length > 1 && newPdDesign && (\n \n )}\n \n \n );\n};\n","import { Offer, AggregateRating } from 'schema-dts';\n\nimport { IStock } from '@notino/shared/definitions/custom-definitions';\nimport { GetProductViewQuery } from '@notino/shared/definitions/types';\n\nimport { IVoucherDiscount } from '../ProductBaseInfo/VoucherDiscount/VoucherDiscount';\n\nconst SOURCE_KEY = 'order_2k';\n\nconst getImageAddress = (src: string, imageDomainPath: string) =>\n `${imageDomainPath}${SOURCE_KEY}/${src}`;\n\nexport const getMainImageSrc = (\n variant: GetProductViewQuery['productDetailByMasterId'][number]['variants'][number],\n imageDomainPath: string\n): string | null =>\n variant.imagesByCategory && variant.imagesByCategory.mainImage\n ? getImageAddress(variant.imagesByCategory.mainImage.src, imageDomainPath)\n : null;\n\nexport const getImagesSrc = (\n variant: GetProductViewQuery['productDetailByMasterId'][number]['variants'][number],\n imageDomainPath: string\n): string[] => {\n const result = [];\n\n if (variant.imagesByCategory) {\n const mainImageSrc = getMainImageSrc(variant, imageDomainPath);\n if (mainImageSrc) {\n result.push(mainImageSrc);\n }\n if (variant.imagesByCategory.others) {\n variant.imagesByCategory.others.forEach((image) =>\n result.push(getImageAddress(image.src, imageDomainPath))\n );\n }\n }\n\n return result;\n};\n\nconst getItemAvailability = (\n variant: GetProductViewQuery['productDetailByMasterId'][number]['variants'][number]\n) => {\n if (\n variant.stockAvailability.code === IStock.outOfStock &&\n variant.showWatchdog\n ) {\n return 'https://schema.org/LimitedAvailability';\n }\n if (variant.stockAvailability.code === IStock.outOfStock) {\n return 'https://schema.org/OutOfStock';\n }\n return 'https://schema.org/InStock';\n};\n\nexport const getVariantOffer = (\n variant: GetProductViewQuery['productDetailByMasterId'][number]['variants'][number],\n product: GetProductViewQuery['productDetailByMasterId'][number],\n imageDomainPath: string,\n hasJsonLdOfferVoucherPrice: boolean\n): Offer => {\n const variantMainImageSrc = getMainImageSrc(variant, imageDomainPath);\n const discount: IVoucherDiscount = variant.attributes.VoucherDiscount;\n const price =\n hasJsonLdOfferVoucherPrice && discount\n ? discount.discountedPrice\n : variant.price.value;\n\n return {\n '@type': 'Offer',\n name: `${product.brand} ${variant.name} ${variant.additionalInfo || ''}`,\n availability: getItemAvailability(variant),\n ...(variant.price && {\n price,\n priceCurrency: variant.price.currency,\n }),\n sku: variant.productCode,\n url: variant.url,\n ...(variantMainImageSrc && {\n image: variantMainImageSrc,\n }),\n };\n};\n\nexport const getAggregateRating = (\n reviewOverview: GetProductViewQuery['productDetailByMasterId'][number]['reviewOverview']\n): AggregateRating => ({\n '@type': 'AggregateRating',\n bestRating: '5',\n worstRating: '1',\n ratingValue: reviewOverview.rating,\n ratingCount: reviewOverview.count,\n});\n","import * as React from 'react';\n\nimport { ReactFCC } from '@notino/react-styleguide';\n\nimport { useTrackingContext } from '@context/tracking/TrackingContext';\nimport { dispatchTrackingEvent } from '@context/tracking/utils';\nimport { registerKeyboardListeners } from '@helpers/accessibility/registerKeyboardListeners';\nimport { useTabSwitcher } from '@sharedComponents/TabSwitcher/context/TabSwitcherContext';\nimport { useTabLink } from '@sharedComponents/TabSwitcher/useTabLink';\n\nimport { useNewPdDesignEnabled } from '../../hooks/useNewPdDesignEnabled';\n\nimport { LinkWrapper } from './styled';\n\ninterface IContentTabLinkProps {\n tabId: number;\n isDefaultTab?: boolean;\n id: string;\n trackingLabel?: string;\n ariaControls?: string;\n}\n\nexport const ContentTabLink: ReactFCC = ({\n tabId,\n isDefaultTab = false,\n trackingLabel,\n id,\n children,\n ariaControls,\n}) => {\n const ref = React.useRef();\n const { getTimeFromInit } = useTrackingContext();\n const { isActive, setActiveTabHandler } = useTabLink(tabId, isDefaultTab);\n const { register, selectNext, selectPrevious } = useTabSwitcher();\n const newDesign = useNewPdDesignEnabled();\n\n React.useEffect(() => {\n register(tabId, ref);\n }, [register, tabId]);\n\n const handleClick = () => {\n setActiveTabHandler();\n if (trackingLabel) {\n dispatchTrackingEvent({\n event: 'subpage_view',\n subpage: {\n name: trackingLabel,\n type: 'tab',\n interaction: 'click',\n timing: getTimeFromInit(),\n action: 'click_on_tab',\n },\n _clear: true,\n });\n }\n };\n\n return (\n \n {children}\n \n );\n};\n","import { useTabSwitcher } from './context/TabSwitcherContext';\nimport { REVIEW_TAB_ID } from './TabLink';\n\nexport const useTabLink = (tabId: number, isDefaultTab?: boolean) => {\n const { activeTabId, setActiveTab, toggleIsReviewTabVisibleOnMobile } =\n useTabSwitcher();\n const setActiveTabHandler = () => {\n setActiveTab(tabId);\n\n if (tabId === REVIEW_TAB_ID) {\n toggleIsReviewTabVisibleOnMobile();\n }\n };\n\n const isActive: boolean =\n (typeof activeTabId === 'undefined' && isDefaultTab) ||\n activeTabId === tabId;\n\n return { isActive, setActiveTabHandler };\n};\n","import styled, { css } from 'styled-components';\n\nimport {\n breakpoints,\n IBasicStyledProps,\n theme,\n} from '@notino/react-styleguide';\ninterface IActiveTab extends IBasicStyledProps {\n isActive?: boolean;\n newDesign?: boolean;\n}\n\nexport const Container = styled.div<{ newPdDesignEnabled?: boolean }>`\n max-width: 64.6875rem;\n width: 100%;\n margin: 0 auto;\n position: relative;\n\n ${({ newPdDesignEnabled }) =>\n newPdDesignEnabled &&\n css`\n max-width: 80rem;\n @media (min-width: 105rem) {\n max-width: 100rem;\n }\n `}\n`;\n\nexport const ContentSwitcherContainer = styled.div<{\n newPdDesignEnabled: boolean;\n}>`\n display: none;\n @media (min-width: ${({ newPdDesignEnabled }) =>\n newPdDesignEnabled ? breakpoints.lg : breakpoints.md}) {\n display: block;\n margin-top: 4.0625rem;\n }\n`;\n\nexport const ContentSwitcherWrapper = styled.div`\n text-align: left;\n width: 100%;\n background: ${(props: IBasicStyledProps) => props.theme.palette.basicInverse};\n`;\n\nexport const TabList = styled.div`\n position: relative;\n`;\n\nconst HoveredTab = css`\n border-bottom: 1px solid ${(props) => props.theme.palette.basic};\n padding-bottom: 13px;\n`;\n\nexport const LinkWrapper = styled.button`\n color: ${theme.palette.basic};\n cursor: pointer;\n position: relative;\n border: 0;\n border-bottom: 3px solid\n ${(props: IActiveTab) =>\n props.isActive\n ? props.theme.palette.basic\n : props.theme.palette.transparent};\n background: transparent;\n\n display: inline-block;\n list-style-type: none;\n padding: 0.625rem 0 0.6875rem;\n margin-right: 1.875rem;\n\n &:hover {\n ${(props) => (props.isActive ? null : HoveredTab)}\n }\n z-index: 1;\n\n ${({ newDesign, isActive }) =>\n newDesign &&\n css`\n color: ${isActive ? theme.palette.basic : theme.palette.neutralDarker};\n font-weight: ${isActive ? 500 : 400};\n `}\n`;\n\nexport const ReviewSummaryCount = styled.span`\n font-size: 0.75rem;\n color: ${theme.palette.neutralDark};\n`;\n","import * as React from 'react';\n\nimport { ReactFCC } from '@notino/react-styleguide';\n\nimport { TabIds } from '../model';\n\ninterface ITabsWithExpandableTextContext {\n tabsWithExpandableTextState: TTabsWithExpandableTextState;\n toggleTabOpening: (tabId: number) => void;\n toggleFullText: (tabId: number) => void;\n}\n\ninterface ITabsWithExpandableTextProviderProps {\n tabsWithExpandableText?: TabIds[];\n}\n\ntype TTabsWithExpandableTextState = Record<\n number,\n { isTabOpenForTheFirstTime: boolean; shouldShowFullText: boolean }\n>;\n\nconst TabsWithExpandableTextContext =\n React.createContext(null);\n\nexport const TabsWithExpandableTextProvider: ReactFCC<\n ITabsWithExpandableTextProviderProps\n> = ({ children, tabsWithExpandableText }) => {\n const initialState = React.useMemo(() => {\n return tabsWithExpandableText?.reduce(\n (acc, cur) => {\n acc[cur] = {\n isTabOpenForTheFirstTime: true,\n shouldShowFullText: false,\n };\n\n return acc;\n },\n {} as TTabsWithExpandableTextState\n );\n }, []);\n\n const [tabsWithExpandableTextState, setTabsWithExpandableTextState] =\n React.useState(initialState);\n\n const toggleTabOpening = React.useCallback(\n (tabId: number) => {\n if (tabsWithExpandableTextState && tabsWithExpandableTextState[tabId]) {\n setTabsWithExpandableTextState({\n ...tabsWithExpandableTextState,\n [tabId]: {\n ...tabsWithExpandableTextState[tabId],\n isTabOpenForTheFirstTime: false,\n },\n });\n }\n },\n [tabsWithExpandableTextState]\n );\n\n const toggleFullText = React.useCallback((tabId: number) => {\n if (tabsWithExpandableTextState && tabsWithExpandableTextState[tabId]) {\n setTabsWithExpandableTextState({\n ...tabsWithExpandableTextState,\n [tabId]: {\n ...tabsWithExpandableTextState[tabId],\n shouldShowFullText: true,\n },\n });\n }\n }, []);\n\n const value: ITabsWithExpandableTextContext = {\n toggleTabOpening,\n toggleFullText,\n tabsWithExpandableTextState,\n };\n\n return (\n \n {children}\n \n );\n};\n\nexport const useTabsWithExpandableText = () => {\n return React.useContext(TabsWithExpandableTextContext);\n};\n","import { ReactFCC } from '@notino/react-styleguide';\n\ninterface ILoadingWrapperProps {\n isLoading: boolean;\n isError: boolean;\n skeleton: JSX.Element;\n error: JSX.Element;\n}\n\nexport const LoadingWrapper: ReactFCC = ({\n isLoading,\n isError,\n error,\n skeleton,\n children,\n}) => {\n if (isLoading) {\n return skeleton;\n }\n if (isError) {\n return error;\n }\n\n return children as JSX.Element;\n};\n","import * as React from 'react';\n\nimport {\n Col,\n ReviewsSummaryWrapper,\n ReviewsWrapper,\n SkeletonElement,\n} from './styled';\n\nexport const ReviewSummarySkeleton: React.FC = () => {\n return (\n \n \n \n \n \n\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n );\n};\n","import * as React from 'react';\n\nimport { arrayFromNumber } from '@helpers/utils';\n\nimport { SortBox } from '../ReviewItems/styled';\n\nimport { ReviewsWrapper, SkeletonElement, Row } from './styled';\n\nexport const ReviewsSkeleton: React.FC = () => {\n return (\n <>\n \n {arrayFromNumber(5).map((i) => (\n \n \n \n \n \n \n \n
\n \n ))}\n >\n );\n};\n","import {\n breakpoints,\n SkeletonElement as NotinoSkeletonElement,\n styled,\n} from '@notino/react-styleguide';\n\nexport const SkeletonElement = styled(NotinoSkeletonElement)<{\n marginY?: number;\n marginX?: number;\n}>`\n margin: ${({ marginY = 0 }) => marginY}rem ${({ marginX = 0 }) => marginX}rem;\n`;\n\nexport const Col = styled.div<{\n mobileWidth?: number;\n width: number;\n hideOnMobile?: boolean;\n}>`\n width: ${({ mobileWidth, width }) => mobileWidth ?? width}%;\n\n @media (min-width: ${breakpoints.md}) {\n width: ${({ width }) => width}%;\n }\n`;\nexport const ReviewsWrapper = styled(Col)`\n padding: 0.75rem 0;\n display: ${({ hideOnMobile = false }) => (hideOnMobile ? 'none' : 'flex')};\n flex-direction: column;\n @media (min-width: ${breakpoints.md}) {\n display: flex;\n }\n`;\n\nexport const Row = styled.div`\n display: flex;\n align-items: center;\n`;\n\nexport const ReviewSkeleton = styled.div`\n width: 80%;\n`;\n\nexport const ReviewsSummaryWrapper = styled.div`\n display: flex;\n flex-direction: column;\n justify-content: space-between;\n\n @media (min-width: ${breakpoints.md}) {\n flex-direction: row;\n }\n`;\n","import { ReviewOrderBy } from '@notino/shared/definitions/types';\n\nimport { IOrderBy } from '@containers/ProductDetailContainer/model';\n\nexport interface IReviewItemsContext {\n actions: IActions;\n state: IReviewItemsState;\n}\n\nexport interface IReviewItemsState {\n orderBy: ReviewOrderBy;\n orderDesc: boolean;\n page: number;\n nextPage: number;\n hideTranslated: boolean;\n allowHideTranslatedCheckbox: boolean;\n}\n\nexport interface IActions {\n changeOrder: (order: IOrderBy) => void;\n incNextPage: () => void;\n toggleHideTranslatedReviews: () => void;\n}\n\nexport enum ActionTypes {\n CHANGE_ORDER = 'CHANGE_ORDER',\n INC_NEXT_PAGE = 'INC_NEXT_PAGE',\n TOGGLE_HIDE_TRANSLATED_REVIEWS = 'TOGGLE_HIDE_TRANSLATED_REVIEWS',\n}\n\nexport type Action =\n | { type: ActionTypes.CHANGE_ORDER; value: IOrderBy }\n | { type: ActionTypes.INC_NEXT_PAGE }\n | { type: ActionTypes.TOGGLE_HIDE_TRANSLATED_REVIEWS };\n","import { ReviewOrderBy } from '@notino/shared/definitions/types';\n\nimport { Action, ActionTypes, IReviewItemsState } from './model';\n\nexport const initialState: IReviewItemsState = {\n orderBy: ReviewOrderBy.DateTime,\n orderDesc: true,\n page: 1,\n nextPage: 2,\n hideTranslated: false,\n allowHideTranslatedCheckbox: false,\n};\n\nexport const reducer = (\n state: IReviewItemsState,\n action: Action\n): IReviewItemsState => {\n switch (action.type) {\n case ActionTypes.CHANGE_ORDER:\n if (\n state.orderBy === action.value.key &&\n state.orderDesc === action.value.desc\n ) {\n return state;\n }\n return {\n ...state,\n orderBy: action.value.key,\n orderDesc: action.value.desc,\n page: 1,\n nextPage: 2,\n };\n case ActionTypes.INC_NEXT_PAGE: {\n return {\n ...state,\n nextPage: state.nextPage + 1,\n };\n }\n case ActionTypes.TOGGLE_HIDE_TRANSLATED_REVIEWS: {\n return {\n ...state,\n hideTranslated: !state.hideTranslated,\n allowHideTranslatedCheckbox:\n state.allowHideTranslatedCheckbox || !state.hideTranslated,\n };\n }\n default:\n return state;\n }\n};\n","import * as React from 'react';\n\nimport { ReactFCC } from '@notino/react-styleguide';\n\nimport { IReviewItemsContext } from './model';\nimport { reducer, initialState } from './reducer';\nimport { useActions } from './useActions';\n\nconst initialContext = {\n state: initialState,\n actions: {},\n};\n\nconst ReviewItemsContext = React.createContext(\n initialContext as IReviewItemsContext\n);\n\nexport const ReviewItemsProvider: ReactFCC = ({ children }) => {\n const [state, dispatch] = React.useReducer(reducer, initialState);\n\n const actions = useActions(dispatch);\n const value = React.useMemo(() => ({ state, actions }), [state, actions]);\n\n return (\n \n {children}\n \n );\n};\n\nexport const useReviewItemsContext = () => React.useContext(ReviewItemsContext);\n","import * as React from 'react';\n\nimport { IOrderBy } from '@containers/ProductDetailContainer/model';\n\nimport { Action, ActionTypes, IActions } from './model';\n\nexport const useActions = (dispatch: React.Dispatch): IActions => {\n return React.useMemo(\n () => ({\n changeOrder: (order: IOrderBy) => {\n dispatch({ type: ActionTypes.CHANGE_ORDER, value: order });\n },\n incNextPage: () => {\n dispatch({ type: ActionTypes.INC_NEXT_PAGE });\n },\n toggleHideTranslatedReviews: () => {\n dispatch({ type: ActionTypes.TOGGLE_HIDE_TRANSLATED_REVIEWS });\n },\n }),\n [dispatch]\n );\n};\n","import styled from 'styled-components';\n\nexport const ReviewsFetch = styled.div`\n display: flex;\n align-items: center;\n justify-content: center;\n margin: 3.125rem 0;\n font-size: 0.875rem;\n svg {\n margin-right: 0.5rem;\n }\n a {\n text-decoration: underline;\n text-transform: lowercase;\n cursor: pointer;\n }\n`;\n","import * as React from 'react';\nimport { FormattedMessage } from 'react-intl';\n\nimport { Col, Colors, WarningIcon } from '@notino/react-styleguide';\n\nimport messages from '../../../../../messages';\n\nimport { ReviewsFetch } from './styled';\n\ninterface IFailedBlockProps {\n loadReviews: () => void;\n}\n\nexport const FetchBlockFailed: React.FC = ({\n loadReviews,\n}) => (\n \n \n \n \n \n \n);\n","import { VoteType, GetReviewsQuery } from '@notino/shared/definitions/types';\n\nexport const canBeVoted = (\n review: GetReviewsQuery['reviews'][number],\n likeDislike: string\n) =>\n (!review.alreadyLiked && !review.alreadyDisliked) ||\n (review.alreadyLiked && likeDislike === VoteType.Like) ||\n (review.alreadyDisliked && likeDislike === VoteType.Dislike);\n","import styled from 'styled-components';\n\nimport { Tag, Link, theme } from '@notino/react-styleguide';\n\nexport const ReviewMotivationTag = styled(Tag)`\n font-size: 0.75rem;\n text-transform: none;\n padding: 0.25rem 0.375rem;\n\n ${(props: { isVerified: boolean }) =>\n props.isVerified && `color: ${theme.palette.success};`}\n`;\n\nexport const TooltipContent = styled.div`\n max-width: max-content;\n width: 15rem;\n padding: 0.5rem;\n`;\n\nexport const MoreInfoLink = styled(Link)`\n margin-left: 0.25rem;\n text-decoration: underline;\n`;\n","import * as React from 'react';\nimport { useIntl } from 'react-intl';\n\nimport { TagModel, TooltipModel } from '@notino/react-styleguide';\nimport { AuthorType } from '@notino/shared/definitions/types';\n\nimport { PortalTooltip } from '@components/PortalTooltip';\nimport { registerKeyboardListeners } from '@helpers/accessibility/registerKeyboardListeners';\n\nimport { messages, messages as reviewMessages } from '../../../messages';\n\nimport { ReviewMotivationTag, MoreInfoLink } from './styled';\n\ninterface IReviewMotivationProps {\n authorType: AuthorType;\n}\n\nexport const ReviewMotivation: React.FC = ({\n authorType,\n}) => {\n const [isOpen, setOpen] = React.useState(false);\n const { formatMessage } = useIntl();\n\n const title = formatMessage(\n reviewMessages[`review${String(authorType)}Title`]\n );\n\n if (title === 'null') {\n return null;\n }\n\n const tooltip = formatMessage(\n reviewMessages[`review${String(authorType)}Description`]\n );\n const moreInfoLink = formatMessage(\n reviewMessages[`review${String(authorType)}MoreLink`]\n );\n\n const authorTag = (\n \n setOpen(false)}\n {...registerKeyboardListeners({\n Enter: () => setOpen((prev) => !prev),\n })}\n >\n {title}\n \n \n );\n\n if (tooltip === 'null') {\n return authorTag;\n }\n\n return (\n \n {tooltip}\n\n {moreInfoLink !== 'null' && (\n \n {formatMessage(messages.reviewMoreText)}\n \n )}\n \n }\n // true || undefined => cuz when undefined, tooltip is uncontrolled\n isOpen={isOpen || undefined}\n interactive={true}\n position={TooltipModel.Position.topCenter}\n >\n {authorTag}\n \n );\n};\n","import styled, { css, keyframes } from 'styled-components';\n\nimport {\n breakpoints,\n IBasicStyledProps,\n theme,\n} from '@notino/react-styleguide';\n\nimport { RequestState } from '@containers/App/model';\n\nexport const WasItUsefulWrapper = styled.div`\n display: flex;\n`;\n\nexport const WasItUseful = styled.span<{ newPdDesignEnabled: boolean }>`\n display: inline-flex;\n align-items: center;\n padding-right: ${({ newPdDesignEnabled }) =>\n newPdDesignEnabled ? '0.75rem' : '0.9375rem'};\n`;\n\nexport const Vote = styled.a`\n padding-right: 0.3125rem;\n text-decoration: underline;\n user-select: none;\n`;\n\nexport const Separator = styled.span`\n color: ${(props) => props.theme.palette.neutral};\n margin-right: 0.375rem;\n`;\n\nexport const TooltipWrapper = styled.div`\n position: absolute;\n width: 100%;\n height: 1rem;\n left: 0;\n top: 0;\n transform: translateY(-50%);\n padding-right: 0.625rem;\n z-index: 81;\n`;\n\nexport const TooltipBox = styled.div`\n width: 1rem;\n height: 1rem;\n`;\n\nexport const ErrorMsg = styled.div`\n padding: 0.5rem;\n color: ${theme.palette.danger};\n`;\n\nexport const Votes = styled.div`\n display: flex;\n align-items: center;\n line-height: 0;\n`;\n\nconst voting = keyframes`\n 50% { color: ${theme.palette.neutralDarker}; }\n`;\n\nconst votingSuccess = keyframes`\n 0% { transform: scale(1); }\n 50% { transform: scale(1.2); }\n 100% { transform: scale(1); }\n`;\n\nconst votingFailed = keyframes`\n 10%, 90% { transform: translate3d(-1px, 0, 0); }\n 20%, 80% { transform: translate3d(2px, 0, 0); }\n 30%, 50%, 70% { transform: translate3d(-4px, 0, 0); }\n 40%, 60% { transform: translate3d(4px, 0, 0); }\n`;\n\nconst VotingInProgress = css`\n animation: ${voting} 1s ease infinite;\n`;\n\nconst VotingSuccess = css`\n transition: all 1s ease;\n animation: ${votingSuccess} 0.8s ease;\n`;\n\nconst VotingFailed = css`\n transition: all 1s ease;\n animation: ${votingFailed} 0.8s ease;\n`;\n\nexport interface IVoteWrapperProps extends IBasicStyledProps {\n state: RequestState;\n voted: boolean;\n}\n\nexport const VoteWrapper = styled.button`\n display: flex;\n align-items: center;\n margin-right: 0.375rem;\n position: relative;\n cursor: pointer;\n user-select: none;\n\n background: none;\n border: none;\n font-size: 0.75rem;\n color: ${theme.palette.neutralDarker};\n\n font-weight: ${(props: IVoteWrapperProps) =>\n props.voted && props.state !== RequestState.IN_PROGRESS ? 700 : 300};\n\n @media only screen and (hover: hover) {\n &:hover a {\n text-decoration: none;\n }\n }\n\n a {\n margin-right: 0.125rem;\n transition: all 0.4s ease;\n\n ${(props: IVoteWrapperProps) =>\n props.state === RequestState.DONE ? VotingSuccess : ''};\n ${(props: IVoteWrapperProps) =>\n props.state === RequestState.FAILED ? VotingFailed : ''};\n ${(props: IVoteWrapperProps) =>\n props.state === RequestState.IN_PROGRESS ? VotingInProgress : ''};\n }\n`;\n\nexport const ReviewItem = styled.li<{ newPdDesignEnabled: boolean }>`\n margin-top: 2.5rem;\n\n ${({ newPdDesignEnabled }) =>\n newPdDesignEnabled\n ? css`\n ${theme.typography.bodyRegular}\n @media (min-width: ${breakpoints.lg}) {\n ${theme.typography.bodyLarge}\n }\n `\n : css`\n font-size: 0.875rem;\n `}\n`;\n\nexport const ReviewDetails = styled.div<{ newPdDesignEnabled: boolean }>`\n display: flex;\n flex-wrap: wrap;\n align-items: center;\n\n color: ${(props) => props.theme.palette.basic};\n\n ${({ newPdDesignEnabled }) =>\n newPdDesignEnabled\n ? css`\n ${theme.typography.labelRegular}\n margin-top: 0.75rem;\n `\n : css`\n font-size: 0.8rem;\n line-height: 1.5rem;\n margin-top: 0.375rem;\n `}\n > *:not(:last-child) {\n margin-right: 0.75rem;\n }\n`;\n\nexport const Name = styled.div`\n font-size: 0.75rem;\n font-weight: bold;\n`;\n\nexport const Date = styled.div<{ newPdDesignEnabled: boolean }>`\n ${({ newPdDesignEnabled }) => !newPdDesignEnabled && 'font-size: 0.75rem;'}\n`;\n\nexport const CheckboxWrapper = styled.div`\n margin: 1rem 0;\n`;\n\nexport const ReviewSource = styled.div`\n color: ${theme.palette.neutralDark};\n border-bottom: 1px ${theme.palette.neutralDark} dashed;\n line-height: 1.5;\n cursor: pointer;\n`;\n","import * as React from 'react';\nimport { MessageDescriptor, FormattedMessage } from 'react-intl';\n\nimport { Tooltip, TooltipModel, ReactFCC } from '@notino/react-styleguide';\n\nimport { RequestState } from '@containers/App/model';\n\nimport messages from '../../../../../messages';\n\nimport {\n Vote as StyledVote,\n VoteWrapper,\n TooltipWrapper,\n TooltipBox,\n ErrorMsg,\n} from './styled';\n\ninterface IVoteProps {\n message: MessageDescriptor;\n onClick: () => void;\n state: RequestState;\n voted?: boolean;\n describedBy?: string;\n}\n\nexport const Vote: ReactFCC = ({\n children,\n message,\n onClick,\n state,\n voted,\n describedBy,\n}) => (\n \n {state === RequestState.FAILED && (\n \n \n \n \n }\n >\n \n \n \n )}\n \n \n \n {children}\n \n);\n","import * as React from 'react';\nimport { FormattedDate, FormattedMessage, useIntl } from 'react-intl';\n\nimport { useMutation } from '@apollo/client';\n\nimport { Colors, Ratings, Tooltip } from '@notino/react-styleguide';\nimport {\n VoteType,\n VoteAction,\n GetReviewsQuery,\n HandleVoteMutation,\n HandleVoteMutationVariables,\n} from '@notino/shared/definitions/types';\n\nimport ErrorBoundary from '@components/ErrorBoundary';\nimport { RequestState } from '@containers/App/model';\nimport { useNewPdDesignEnabled } from '@containers/ProductDetailContainer/ProductDetail/hooks/useNewPdDesignEnabled';\n\nimport messages from '../../../../../messages';\nimport handleVoteMutationQuery from '../../queries/handleVote.graphql';\nimport { canBeVoted } from '../utils';\n\nimport { ReviewMotivation } from './ReviewMotivation';\nimport {\n WasItUsefulWrapper,\n WasItUseful,\n Separator,\n Votes,\n ReviewDetails,\n ReviewItem,\n Date,\n Name,\n ReviewSource,\n} from './styled';\nimport { Vote } from './Vote';\n\ninterface ISingleReviewProps {\n item: GetReviewsQuery['reviews'][number];\n index: number;\n}\n\nexport const SingleReviewData: React.FC = ({\n item,\n index,\n}) => {\n const { formatMessage } = useIntl();\n\n const newPdDesignEnabled = useNewPdDesignEnabled();\n\n const [\n { [VoteType.Like]: likeLoading, [VoteType.Dislike]: dislikeLoading },\n setState,\n ] = React.useState({\n [VoteType.Like]: RequestState.DEFAULT,\n [VoteType.Dislike]: RequestState.DEFAULT,\n });\n\n const [handleVoteMutation] = useMutation<\n HandleVoteMutation,\n HandleVoteMutationVariables\n >(handleVoteMutationQuery);\n\n const handleVote = async (type: VoteType) => {\n const unvote =\n (item.alreadyLiked && type === VoteType.Like) ||\n (item.alreadyDisliked && type === VoteType.Dislike);\n\n const otherType = type === VoteType.Like ? VoteType.Dislike : VoteType.Like;\n\n setState((old) => ({\n ...old,\n [type]: RequestState.IN_PROGRESS,\n [otherType]: RequestState.DEFAULT,\n }));\n\n try {\n await handleVoteMutation({\n variables: {\n id: item.id.toString(),\n type,\n action: unvote ? VoteAction.Remove : VoteAction.Add,\n },\n });\n\n setState((old) => ({\n ...old,\n [type]: RequestState.DONE,\n }));\n } catch (e) {\n setState((old) => ({\n ...old,\n [type]: RequestState.FAILED,\n }));\n }\n };\n\n const like = async () => {\n if (\n canBeVoted(item, VoteType.Like) &&\n likeLoading !== RequestState.IN_PROGRESS\n ) {\n await handleVote(VoteType.Like);\n }\n };\n\n const dislike = async () => {\n if (\n canBeVoted(item, VoteType.Dislike) &&\n dislikeLoading !== RequestState.IN_PROGRESS\n ) {\n await handleVote(VoteType.Dislike);\n }\n };\n\n return (\n \n {item.text}\n \n \n\n \n {item.originalShopId ? (\n \n ) : (\n item.userName\n )}\n \n\n {item.translatedFrom && (\n \n \n \n \n \n )}\n\n \n \n \n\n \n \n \n \n\n \n \n {item.like}\n \n\n |\n\n \n {item.dislike}\n \n \n \n\n \n \n \n \n \n );\n};\n","import * as React from 'react';\nimport { FormattedMessage, useIntl } from 'react-intl';\n\nimport { ObservableQueryFields } from '@apollo/client';\n\nimport { ButtonModel, Checkbox } from '@notino/react-styleguide';\nimport {\n GetReviewsQuery,\n GetReviewsQueryVariables,\n} from '@notino/shared/definitions/types';\n\nimport { useNewPdDesignEnabled } from '@containers/ProductDetailContainer/ProductDetail/hooks/useNewPdDesignEnabled';\nimport { useShouldShowReviewPage } from '@containers/ProductDetailContainer/ProductDetail/hooks/useShouldShowReviewPage';\nimport { useFeatureFlags } from '@context/launch-darkly/LDProvider';\nimport { useProductView } from '@context/product-view';\nimport { useTrackingContext } from '@context/tracking/TrackingContext';\nimport { dispatchTrackingEvent } from '@context/tracking/utils';\n\nimport messages from '../../../../../messages';\nimport { PAGE_SIZE } from '../../constants';\nimport { useReviewItemsContext } from '../context';\nimport {\n ShowNextWrapper,\n StyledButton,\n StyledCol,\n ReviewsList,\n ShowAllReviewsLink,\n} from '../styled';\n\nimport { SingleReviewData } from './SingleReviewData';\nimport { CheckboxWrapper } from './styled';\n\ninterface IFetchDoneProps {\n textReviewsCount: number;\n items: GetReviewsQuery['reviews'];\n productCode: string;\n isAllReviewsPage?: boolean;\n loadReviews: ObservableQueryFields<\n GetReviewsQuery,\n GetReviewsQueryVariables\n >['fetchMore'];\n}\n\nexport const FetchDoneBlock: React.FC = ({\n textReviewsCount,\n items,\n productCode,\n loadReviews,\n isAllReviewsPage,\n}) => {\n const { pdHasAllReviewsPage } = useFeatureFlags();\n const newDesign = useNewPdDesignEnabled();\n const { getTimeFromInit } = useTrackingContext();\n const [isLoading, setLoading] = React.useState(false);\n const {\n state: {\n orderBy,\n orderDesc,\n nextPage,\n hideTranslated,\n allowHideTranslatedCheckbox,\n },\n actions: { incNextPage, toggleHideTranslatedReviews },\n } = useReviewItemsContext();\n\n const [loadMoreError, setLoadMoreError] = React.useState(false);\n const { product } = useProductView();\n const { formatMessage } = useIntl();\n\n const handleLoadMore = React.useCallback(async () => {\n try {\n dispatchTrackingEvent({\n event: 'element_click',\n element: {\n action: 'show_more_reviews',\n type: isAllReviewsPage ? 'review' : 'product_detail',\n timing: getTimeFromInit(),\n interaction: 'click',\n name: 'reviews',\n promo_labels: undefined,\n mode: undefined,\n },\n _clear: true,\n });\n\n setLoading(true);\n\n await loadReviews({\n variables: {\n code: productCode,\n orderBy,\n orderDesc,\n page: nextPage,\n pageSize: PAGE_SIZE,\n hideTranslated,\n },\n });\n incNextPage();\n } catch (e) {\n setLoadMoreError(true);\n } finally {\n setLoading(false);\n }\n }, [\n isAllReviewsPage,\n getTimeFromInit,\n loadReviews,\n productCode,\n orderBy,\n orderDesc,\n nextPage,\n hideTranslated,\n incNextPage,\n ]);\n\n const displayShowMoreButton = textReviewsCount > items.length;\n const { count: reviewCount, rating: ratingSummary } = product.reviewOverview;\n\n const shouldShowAllReviewsLink =\n useShouldShowReviewPage(reviewCount, ratingSummary) && !isAllReviewsPage;\n\n const allReviewsPageLink = pdHasAllReviewsPage\n ? formatMessage(messages.redirectReviewsPageLink)\n : null;\n\n const handleTrackShowAllReviews = () => {\n dispatchTrackingEvent({\n event: 'element_click',\n element: {\n action: 'all',\n type: 'product_detail',\n timing: getTimeFromInit(),\n name: 'all_reviews',\n },\n _clear: true,\n });\n };\n\n return (\n <>\n \n {((items.some((item) => item.translatedFrom) &&\n items.some((item) => !item.translatedFrom)) ||\n allowHideTranslatedCheckbox) && (\n \n \n \n \n \n )}\n\n \n {items?.map((item, index) => (\n \n ))}\n \n \n\n {displayShowMoreButton && (\n \n \n \n \n \n\n {shouldShowAllReviewsLink && (\n \n \n \n )}\n \n \n )}\n >\n );\n};\n","import styled from 'styled-components';\n\nimport { keyframes } from '@notino/react-styleguide';\n\nconst rotation8Steps = keyframes`\n 0%, 100% { transform: rotate(0deg); }\n 12.5% { transform: rotate(45deg); }\n 25% { transform: rotate(90deg); }\n 37.5% { transform: rotate(135deg); }\n 50% { transform: rotate(180deg); }\n 62.5% { transform: rotate(225deg); }\n 75% { transform: rotate(270deg); }\n 87.5% { transform: rotate(315deg); }\n`;\n\ninterface IReviewsFetchingProps {\n minHeight?: string;\n}\n\nexport const ReviewsFetching = styled.div`\n display: flex;\n justify-content: center;\n align-items: center;\n margin: 3.125rem 0;\n min-height: ${({ minHeight = 'auto' }) => minHeight};\n svg {\n animation: ${rotation8Steps} 1s infinite step-start;\n }\n`;\n","import * as React from 'react';\n\nimport { Col, Colors, SpinnerIcon } from '@notino/react-styleguide';\n\nimport { REVIEW_ITEM_HEIGHT } from '../../constants';\n\nimport { ReviewsFetching } from './styled';\n\ninterface IFetchingBlockProps {\n reviewsCount?: number;\n}\n\nexport const FetchingBlock: React.FC = ({\n reviewsCount,\n}) => (\n \n \n \n \n \n);\n","import * as React from 'react';\nimport { useIntl } from 'react-intl';\n\nimport { ApolloError, ObservableQueryFields } from '@apollo/client';\n\nimport { Select } from '@notino/react-styleguide';\nimport {\n ReviewOrderBy,\n GetReviewsQuery,\n GetReviewsQueryVariables,\n} from '@notino/shared/definitions/types';\n\nimport ErrorBoundary from '@components/ErrorBoundary';\nimport { IOrderBy } from '@containers/ProductDetailContainer/model';\n\nimport messages from '../../../../messages';\nimport { PAGE_SIZE } from '../constants';\n\nimport { useReviewItemsContext } from './context';\nimport { FetchBlockFailed } from './FetchBlockFailed';\nimport { FetchDoneBlock } from './FetchDoneBlock';\nimport { FetchingBlock } from './FetchingBlock';\nimport { SortBox, StyledCol, StyledRow } from './styled';\n\ninterface IReviewsItemsProps {\n textReviewsCount: number;\n isAllReviewsPage?: boolean;\n refetch: () => void;\n fetchMore: ObservableQueryFields<\n GetReviewsQuery,\n GetReviewsQueryVariables\n >['fetchMore'];\n reviews: GetReviewsQuery['reviews'];\n productCode: string;\n reviewsLoading: boolean;\n reviewsError: ApolloError;\n}\n\nconst orderByOptions: IOrderBy[] = [\n { key: ReviewOrderBy.DateTime, desc: true },\n { key: ReviewOrderBy.DateTime, desc: false },\n { key: ReviewOrderBy.Rating, desc: true },\n { key: ReviewOrderBy.Rating, desc: false },\n { key: ReviewOrderBy.Favorite, desc: true },\n { key: ReviewOrderBy.Favorite, desc: false },\n];\n\nconst selectKeyboardConfig = {\n keyNavigation: true,\n};\n\nexport const ReviewsItems: React.FC = ({\n refetch,\n fetchMore,\n reviews,\n reviewsLoading,\n reviewsError,\n productCode,\n textReviewsCount,\n isAllReviewsPage,\n}) => {\n const { formatMessage } = useIntl();\n const {\n state: { orderBy: currentOrderBy, orderDesc: currentOrderDesc },\n actions: { changeOrder },\n } = useReviewItemsContext();\n const lastReview = React.useRef(0);\n\n if (!reviewsLoading && reviews.length > 0) {\n lastReview.current = reviews.length;\n }\n\n const onSortSelect = React.useCallback(\n (option) => {\n const orderBy = orderByOptions[option.id];\n changeOrder(orderBy);\n },\n [changeOrder]\n );\n\n const options = React.useMemo(\n () =>\n orderByOptions.map((orderBy, index) => ({\n id: index,\n label: formatMessage(\n messages[\n `orderReviewsBy${orderBy.key}${orderBy.desc ? 'Desc' : 'Asc'}`\n ]\n ),\n key: orderBy.key,\n desc: orderBy.desc,\n })),\n [formatMessage]\n );\n\n return (\n \n {(reviews.length > 0 || lastReview.current > 0) && (\n <>\n \n \n \n \n\n {reviews.length > 0 && (\n \n \n \n )}\n >\n )}\n\n {reviews.length === 0 && reviewsLoading && (\n \n )}\n\n {reviews.length === 0 && reviewsError && (\n \n )}\n \n );\n};\n","import styled, { css } from 'styled-components';\n\nimport { Row, Col, ActionButton, breakpoints } from '@notino/react-styleguide';\n\nexport const SortBox = styled.div`\n font-size: 0.925rem;\n border-bottom: 1px solid ${(props) => props.theme.palette.neutralLighter};\n margin-top: 1.25rem;\n`;\n\nexport const ShowNextWrapper = styled.div<{\n shouldShowAllReviewsLink: boolean;\n}>`\n display: flex;\n margin-top: 3rem;\n margin-bottom: 1.25rem;\n justify-content: center;\n align-items: center;\n flex-direction: column;\n\n @media (min-width: ${breakpoints.md}) {\n ${({ shouldShowAllReviewsLink }) =>\n shouldShowAllReviewsLink &&\n css`\n flex-direction: row;\n justify-content: space-between;\n `}\n }\n`;\n\nexport const ShowAllReviewsLink = styled('a')`\n display: block;\n color: #000;\n text-decoration: underline;\n cursor: pointer;\n margin-top: 1.3rem;\n\n @media (min-width: ${breakpoints.md}) {\n margin-top: 0rem;\n }\n`;\n\nexport const StyledButton = styled(ActionButton)<{\n newDesign: boolean;\n}>`\n padding: 0 0.5625rem !important;\n\n ${({ newDesign }) =>\n newDesign\n ? css`\n font-weight: 500 !important;\n `\n : css`\n font-weight: normal !important;\n `}\n`;\n\nexport const StyledRow = styled(Row)`\n display: flex !important;\n margin: 0;\n`;\n\nexport const StyledCol = styled(Col)`\n padding: 0;\n`;\n\nexport const ReviewsList = styled.ol`\n list-style: none;\n margin-left: 0 !important; // important to override layout styles\n\n overflow-x: hidden;\n @media (min-width: ${breakpoints.md}) {\n overflow-x: unset;\n }\n`;\n","import loadable from '@loadable/component';\n\nexport const ReviewsForm = loadable(() => import('./'));\n","var e,s,f,l;f=s||(s={}),function(e){e.default=\"default\",e.inverse=\"inverse\",e.success=\"success\"}(l||(l={})),f.Styles=l;var t=((e={})[s.Styles.default]={fill:\"#ffffff\",stroke:\"#000000\"},e[s.Styles.inverse]={fill:\"#B2B2B2\",stroke:\"#000000\"},e[s.Styles.success]={fill:\"#dff5dd\",stroke:\"#328b30\"},e);export{s as CheckCircleIconModel,t as CheckCircleIconStyles};\n//# sourceMappingURL=CheckCircleIconModel.js.map\n","import{a as e,_ as t}from\"./_rollupPluginBabelHelpers-fc5c8eb1.js\";import*as l from\"react\";import{CheckCircleIconModel as r,CheckCircleIconStyles as n}from\"./CheckCircleIconModel.js\";import{Icon as o}from\"./index.js\";import\"./ThemeContext.js\";import\"styled-components\";var a=[\"iconStyle\",\"border\"],i=function(i){var m=i.iconStyle,c=void 0===m?r.Styles.default:m,s=i.border,f=void 0===s||s,p=e(i,a),d=n[c],E=d.stroke,u=d.fill;return l.createElement(o,t({},p,{viewBox:\"0 0 67 67\"}),l.createElement(\"g\",{stroke:\"none\",strokeWidth:\"1\",fill:\"none\",fillRule:\"evenodd\"},l.createElement(\"g\",{transform:\"translate(1.000000, 1.000000)\"},l.createElement(\"circle\",{fill:u,stroke:f?E:\"none\",cx:\"32\",cy:\"32\",r:\"32\"}),l.createElement(\"g\",{transform:\"translate(12.000000, 12.000000)\"},l.createElement(\"mask\",{fill:\"white\"},l.createElement(\"polygon\",{points:\"6.8796875 21.1302083 5 23.0098958 13.4140625 31.4239583 34.6296875 10.2130208 32.75 8.33333333 13.4140625 27.6692708\"})),l.createElement(\"polygon\",{fill:E,fillRule:\"nonzero\",points:\"6.8796875 21.1302083 5 23.0098958 13.4140625 31.4239583 34.6296875 10.2130208 32.75 8.33333333 13.4140625 27.6692708\"}),l.createElement(\"mask\",null,l.createElement(\"g\",null,l.createElement(\"g\",null,l.createElement(\"rect\",{x:\"0\",y:\"0\",width:\"40\",height:\"40\"}))))))))};export{i as CheckCircleIcon};\n//# sourceMappingURL=CheckCircleIcon.js.map\n","import { defineMessages } from 'react-intl';\n\nexport const messages = defineMessages({\n reviewSent: {\n id: 'pd.reviews.review.sent',\n defaultMessage: 'Hodnocení odesláno',\n },\n reviewSentThanks: {\n id: 'pd.reviews.review.sent.thanks',\n defaultMessage:\n 'Děkujeme, že pomáháte ostatním zákazníkům s lepším výběrem',\n },\n});\n","import styled from 'styled-components';\n\nimport { theme } from '@notino/react-styleguide';\n\nexport const ReviewsSentSuccesModalWrapper = styled.div`\n margin: 3rem;\n text-align: center;\n`;\n\nexport const ReviewSentText = styled.div`\n font-size: 1.75rem;\n line-height: 1.2;\n font-weight: 300;\n margin-top: 2rem;\n margin-bottom: 1rem;\n`;\n\nexport const ReviewSentThanksText = styled.div`\n font-size: 0.875rem;\n color: ${theme.palette.neutralDarker};\n`;\n","import * as React from 'react';\nimport { FormattedMessage } from 'react-intl';\n\nimport { CheckCircleIcon, Colors } from '@notino/react-styleguide';\n\nimport { messages } from './messages';\nimport {\n ReviewSentText,\n ReviewSentThanksText,\n ReviewsSentSuccesModalWrapper,\n} from './styled';\n\nexport const ReviewsSentSuccessModalBody: React.FC = () => {\n return (\n \n \n \n \n \n \n \n \n \n );\n};\n","import styled from 'styled-components';\n\nimport { Row, Col } from '@notino/react-styleguide';\n\ninterface IRatingRow {\n isFirst: boolean;\n}\n\nexport const RatingRow = styled.div`\n display: flex;\n align-items: center;\n font-size: 0.8rem;\n height: 1.875rem;\n margin-top: ${(props: IRatingRow) => (props.isFirst ? '-0.6875rem' : '0')};\n`;\n\nexport const RatingsValue = styled.div`\n min-width: 7.5rem;\n display: flex;\n`;\n\nexport const ShowLink = styled.div`\n text-decoration: underline;\n cursor: pointer;\n font-size: 0.925rem;\n margin: 1.25rem 0;\n`;\n\nexport const StyledRow = styled(Row)`\n display: flex !important;\n margin: 0;\n`;\n\nexport const StyledCol = styled(Col)`\n padding: 0;\n`;\n","import styled, { css } from 'styled-components';\n\nimport { breakpoints, theme } from '@notino/react-styleguide';\n\ninterface IAttributesReviewsProps {\n visible: boolean;\n}\n\nexport const AttributesReviews = styled.div`\n display: ${(props: IAttributesReviewsProps) =>\n props.visible ? 'block' : 'none'};\n @media (min-width: ${breakpoints.md}) {\n display: block;\n margin-top: 3.125rem;\n }\n`;\n\nexport const ParamProgressWrapper = styled.div`\n width: 50%;\n padding-right: 0.625rem;\n`;\n\nexport const ParamValue = styled.div<{ newPdDesignEnabled: boolean }>`\n ${({ newPdDesignEnabled }) =>\n newPdDesignEnabled\n ? css`\n ${theme.typography.labelSmall400};\n `\n : css`\n font-size: 0.75rem;\n line-height: 1.2;\n `}\n width: 50%;\n`;\n","import * as React from 'react';\nimport { FormattedMessage } from 'react-intl';\n\nimport ProgressBar from '@components/ProgressBar';\nimport { IRating } from '@containers/ProductDetailContainer/model';\n\nimport messages from '../../../../../messages';\nimport { useNewPdDesignEnabled } from '../../../../hooks/useNewPdDesignEnabled';\nimport { StyledCol, RatingRow } from '../styled';\n\nimport { AttributesReviews, ParamProgressWrapper, ParamValue } from './styled';\n\ninterface IAttributesSummaryProps {\n ratings: IRating[];\n showAttributesReviews: boolean;\n}\n\nexport const AttributesSummary: React.FC = ({\n ratings,\n showAttributesReviews,\n}) => {\n const newPdDesignEnabled = useNewPdDesignEnabled();\n return (\n \n \n {ratings.map((rating, ratingKey) => (\n \n \n \n \n \n \n \n \n ))}\n \n \n );\n};\n","import styled, { css } from 'styled-components';\n\nimport { breakpoints, Ratings, Button } from '@notino/react-styleguide';\n\ninterface IHeaderProps {\n baseline?: boolean;\n}\n\nexport const Header = styled.div`\n height: 3.125rem;\n line-height: 3.125rem;\n display: flex;\n align-items: ${(props: IHeaderProps) =>\n props.baseline ? 'baseline' : 'center'};\n font-size: 0.875rem;\n\n strong {\n font-size: 1.125rem;\n padding-right: 0.3125rem;\n }\n`;\n\nexport const NoReviewText = styled.div`\n padding-bottom: 1.25rem;\n`;\n\nexport const OverallValue = styled.span<{ newDesign: boolean }>`\n display: inline-block;\n box-sizing: content-box;\n padding-right: 0.625rem;\n\n ${({ newDesign }) =>\n newDesign\n ? css`\n font-size: 1.5rem;\n font-weight: 500;\n line-height: 2rem;\n `\n : css`\n font-size: 1.875rem;\n font-weight: bold;\n width: 2.8125rem;\n `}\n`;\n\nexport const StyledButton = styled(Button)<{ newDesign: boolean }>`\n margin-bottom: 1.25rem;\n font-weight: normal;\n\n @media (min-width: ${breakpoints.md}) {\n margin: 0;\n }\n\n ${({ newDesign }) =>\n newDesign &&\n css`\n font-weight: 500;\n `}\n`;\n\nexport const StyledRatings = styled(Ratings)`\n display: inline-block !important;\n`;\n","import * as React from 'react';\nimport { FormattedMessage } from 'react-intl';\n\nimport { Colors, ButtonModel } from '@notino/react-styleguide';\n\nimport { IReviewSummary } from '@containers/ProductDetailContainer/model';\nimport { useNewPdDesignEnabled } from '@containers/ProductDetailContainer/ProductDetail/hooks/useNewPdDesignEnabled';\n\nimport messages from '../../../../../messages';\nimport { StyledCol } from '../styled';\n\nimport {\n Header,\n NoReviewText,\n OverallValue,\n StyledButton,\n StyledRatings,\n} from './styled';\n\ninterface IHeaderProps {\n hasReviews: boolean;\n isAllReviewsPage?: boolean;\n reviewSummary: IReviewSummary;\n onWriteMessageClick: () => void;\n}\n\nexport const ReviewHeader = ({\n hasReviews,\n reviewSummary,\n onWriteMessageClick,\n isAllReviewsPage,\n}: IHeaderProps) => {\n const newDesign = useNewPdDesignEnabled();\n const headerColProps = hasReviews ? { md: 3 } : {};\n\n const buttonStyle = isAllReviewsPage\n ? ButtonModel.Styles.secondary\n : ButtonModel.Styles.primary;\n\n return (\n \n \n {hasReviews && (\n \n {reviewSummary.averageRating.toFixed(1)}\n \n )}\n\n \n \n\n {!hasReviews && (\n \n \n \n )}\n\n \n \n \n \n );\n};\n","import * as React from 'react';\n\nimport { ModalModel, ModalContext } from '@notino/react-styleguide';\n\nimport {\n ReviewSourceTypes,\n IRating,\n} from '@containers/ProductDetailContainer/model';\n\nimport { shouldOpenReviewModal } from '../../utils/shouldOpenReviewModal';\n\nimport { ReviewsForm } from './ReviewsFormModalBody/loadable';\nimport { ReviewsSentSuccessModalBody } from './ReviewsSentSuccesModal';\n\ninterface IUseReviewModalParameters {\n ratings: IRating[];\n productCode: string;\n masterId: string;\n isAllReviewsPage?: boolean;\n}\n\nconst MODAL_OPTIONS = {\n size: ModalModel.Sizes.default,\n type: ModalModel.Types.default,\n header: null,\n center: false,\n noBorders: true,\n withFocusTrap: true,\n};\nexport const useReviewModal = ({\n ratings,\n productCode,\n masterId,\n isAllReviewsPage,\n}: IUseReviewModalParameters) => {\n const { toggleModal } = ModalContext.useModalContext();\n const [success, markAsSuccess] = React.useReducer(() => true, false);\n\n React.useEffect(() => {\n const reviewSource = shouldOpenReviewModal();\n if (reviewSource) {\n toggleModal(\n \n \n
,\n MODAL_OPTIONS\n );\n }\n }, [masterId, productCode, ratings, toggleModal]);\n\n return React.useCallback(() => {\n toggleModal(\n \n {success ? (\n \n ) : (\n \n )}\n
,\n MODAL_OPTIONS\n );\n }, [masterId, productCode, ratings, toggleModal, success, isAllReviewsPage]);\n};\n","import styled, { css } from 'styled-components';\n\nimport { breakpoints, theme } from '@notino/react-styleguide';\n\nexport const ProgressWrapper = styled.div`\n flex: 1 1;\n padding-right: 0.625rem;\n display: inline-block;\n`;\n\nexport const RatingsValue = styled.div`\n min-width: 7.5rem;\n display: flex;\n`;\n\nexport const StarsBox = styled.div`\n display: inline-block;\n`;\n\nexport const StarsValue = styled.div<{ newPdDesignEnabled: boolean }>`\n display: inline-block;\n width: 2.1875rem;\n padding-right: 0.625rem;\n\n ${({ newPdDesignEnabled }) =>\n newPdDesignEnabled\n ? css`\n ${theme.typography.labelSmall400};\n `\n : css`\n font-size: 0.75rem;\n `}\n`;\n\nexport const StarReviews = styled.div`\n margin-top: 1.5rem;\n\n @media (min-width: ${breakpoints.md}) {\n margin-top: 3.125rem;\n }\n`;\n","import * as React from 'react';\n\nimport { StarIcon } from '@notino/react-styleguide';\n\nimport ProgressBar from '@components/ProgressBar';\nimport { IReviewSummary } from '@containers/ProductDetailContainer/model';\nimport { arrayFromNumber } from '@helpers/utils';\n\nimport { useNewPdDesignEnabled } from '../../../../hooks/useNewPdDesignEnabled';\nimport { RatingRow, StyledCol } from '../styled';\n\nimport {\n ProgressWrapper,\n RatingsValue,\n StarsBox,\n StarsValue,\n StarReviews,\n} from './styled';\n\ninterface IStarSummaryProps {\n reviewSummary: IReviewSummary;\n}\n\nexport const StarsSummary: React.FC = ({\n reviewSummary,\n}) => {\n const newPdDesignEnabled = useNewPdDesignEnabled();\n return (\n \n \n {Object.keys(reviewSummary.votes)\n .sort()\n .reverse()\n .map((stars, index) => (\n \n \n \n \n \n \n {reviewSummary.votes[stars]}\n \n \n {arrayFromNumber(parseInt(stars, 10)).map((i) => (\n \n ))}\n \n \n \n ))}\n \n \n );\n};\n","import * as React from 'react';\nimport { FormattedMessage } from 'react-intl';\n\nimport { IReviewSummary } from '@containers/ProductDetailContainer/model';\nimport { useTrackingContext } from '@context/tracking/TrackingContext';\nimport { dispatchTrackingEvent } from '@context/tracking/utils';\n\nimport messages from '../../../../messages';\n\nimport { AttributesSummary } from './AttributesSummary';\nimport { ReviewHeader } from './ReviewHeader';\nimport { useReviewModal } from './ReviewModal/useReviewModal';\nimport { StarsSummary } from './StarsSummary';\nimport { ShowLink, StyledRow, StyledCol } from './styled';\n\ninterface IReviewsSummaryProps {\n reviewSummary: IReviewSummary;\n productCode: string;\n isAllReviewsPage?: boolean;\n masterId: string;\n}\n\nexport const ReviewsSummary: React.FC = React.memo(\n ({ reviewSummary, productCode, masterId, isAllReviewsPage }) => {\n const { getTimeFromInit } = useTrackingContext();\n\n const [showAttributesReviews, toggleAttributesReviews] = React.useReducer(\n (prev) => !prev,\n false\n );\n\n const openReviewModal = useReviewModal({\n ratings: reviewSummary.ratings,\n productCode,\n masterId,\n isAllReviewsPage,\n });\n\n const handleAddNewReview = () => {\n dispatchTrackingEvent({\n event: 'element_click',\n element: {\n action: 'wt_add_review',\n interaction: 'click',\n name: 'review',\n type: isAllReviewsPage ? 'review' : 'product_detail',\n mode: 'open',\n promo_labels: undefined,\n timing: getTimeFromInit(),\n },\n _clear: true,\n });\n\n openReviewModal();\n };\n\n if (!reviewSummary) {\n return null;\n }\n\n const hasReviews = reviewSummary.totalVotes > 0;\n\n return (\n \n \n {hasReviews && (\n <>\n \n \n \n {showAttributesReviews ? (\n \n ) : (\n \n )}\n \n \n \n >\n )}\n \n );\n }\n);\n","export const PAGE_SIZE = 5;\nexport const MODAL_AUTO_CLOSE_TIMEOUT = 5000;\nexport const REVIEW_ITEM_HEIGHT = 4.75; // rem\n","import { defineMessages } from 'react-intl';\n\nexport const messages = defineMessages({\n reviewsSummaryFailed: {\n id: 'pd.reviews.fail.summary',\n defaultMessage: 'Nepodařilo se načíst souhrn recenzí',\n },\n reviewsFailed: {\n id: 'pd.reviews.fail.reviews',\n defaultMessage: 'Nepodařilo se načíst recenze',\n },\n reviewMotivatedTitle: {\n id: 'pd.reviews.motivated.title',\n defaultMessage: 'Dárek',\n },\n reviewUnknownTitle: {\n id: 'pd.reviews.unknown.title',\n defaultMessage: 'Neověřená recenze',\n },\n reviewVerifiedTitle: {\n id: 'pd.reviews.verified.title',\n defaultMessage: 'Ověřená recenze',\n },\n reviewMotivatedDescription: {\n id: 'pd.reviews.motivated.description',\n defaultMessage:\n 'Recenzovaný produkt od nás dostal autor recenze zdarma jako dárek.',\n },\n reviewUnknownDescription: {\n id: 'pd.reviews.unknown.description',\n defaultMessage:\n 'Recenzovaný produkt si u nás autor recenze nekoupil nebo jeho nákup nemůžeme ověřit.',\n },\n reviewVerifiedDescription: {\n id: 'pd.reviews.verified.description',\n defaultMessage:\n 'Recenzovaný produkt si u nás autor recenze opravdu koupil.',\n },\n reviewMotivatedMoreLink: {\n id: 'pd.reviews.motivated.more.link',\n defaultMessage: 'null',\n },\n reviewUnknownMoreLink: {\n id: 'pd.reviews.unknown.more.link',\n defaultMessage: 'null',\n },\n reviewVerifiedMoreLink: {\n id: 'pd.reviews.verified.more.link',\n defaultMessage: 'null',\n },\n reviewMoreText: {\n id: 'pd.reviews.verified.more.text',\n defaultMessage: 'Více informací',\n },\n tryAgain: {\n id: 'reviews.send.review.retry',\n defaultMessage: 'Zkusit znovu',\n },\n collectingReviewsText: {\n id: 'pd.reviews.collect.reviews.text',\n defaultMessage: 'Jak sbíráme recenze',\n },\n collectingReviewsLink: {\n id: 'pd.reviews.collect.reviews.link',\n defaultMessage: '/produktove-recenze',\n },\n});\n","import styled, { css } from 'styled-components';\n\nimport { breakpoints } from '@notino/react-styleguide';\n\nexport const Wrapper = styled.div<{ newPd: boolean }>`\n ${({ newPd }) =>\n newPd &&\n css`\n width: 100%;\n @media (min-width: ${breakpoints.lg}) {\n padding-right: 2rem;\n }\n `}\n`;\n\nexport const CenterWrapper = styled.div`\n display: flex;\n flex-direction: column;\n align-items: center;\n justify-content: center;\n\n padding: 0.75rem 0;\n`;\n\nexport const ErrorMessage = styled.span`\n font-size: 0.875rem;\n`;\n\nexport const CollectReviewsWrapper = styled.div`\n margin-bottom: 1rem;\n\n display: flex;\n gap: 0.06rem;\n align-items: center;\n`;\n\nexport const CollectReviewsLink = styled('a')`\n color: #404040;\n font-size: 0.75rem;\n\n &:hover {\n text-decoration: none;\n color: #000;\n }\n`;\n","import { ReviewSourceTypes } from '@containers/ProductDetailContainer/model';\n\nexport const shouldOpenReviewModal = (): ReviewSourceTypes => {\n if (typeof location !== 'undefined' && location.search) {\n const searchParams = new URLSearchParams(location.search);\n return searchParams.get('mail') === '1'\n ? ReviewSourceTypes.EMAIL\n : searchParams.get('reviewLogin') === 'true'\n ? ReviewSourceTypes.DIRECT\n : null;\n }\n return null;\n};\n","import loadable from '@loadable/component';\n\nexport const RichContentArea = loadable(() => import('../RichContentArea'));\n","import { useEffect } from 'react';\n\nexport const REVIEW_DIGEST_LOCAL_STORAGE_KEY = 'nushop-review-digest';\nexport const REVIEW_DIGEST_TOKEN_QUERY_KEY = 'reviewDigest';\nexport const REVIEW_DIGEST_VALID_TO_QUERY_KEY = 'validTo';\n\ntype ReviewDigest = { token: string; validTo: string };\n\nexport const useHandleReviewDigest = () => {\n useEffect(() => {\n deleteInvalidDigestsFromLS();\n const params = new URLSearchParams(window.location.search);\n\n if (params.has(REVIEW_DIGEST_TOKEN_QUERY_KEY)) {\n addNewDigestToLS(\n params.get(REVIEW_DIGEST_TOKEN_QUERY_KEY),\n params.get(REVIEW_DIGEST_VALID_TO_QUERY_KEY)\n );\n }\n }, []);\n};\n\nexport const getReviewDigestsFromBrowser = () =>\n getValidDigestsOnly().map((x) => x.token);\n\nconst getDigestsFromLS = (): ReviewDigest[] => {\n const data = localStorage.getItem(REVIEW_DIGEST_LOCAL_STORAGE_KEY);\n if (!data) {\n return [];\n }\n try {\n const parsed = JSON.parse(data);\n if (!Array.isArray(parsed)) {\n return [];\n }\n return parsed;\n } catch (err) {\n return [];\n }\n};\n\nconst getValidDigestsOnly = () =>\n getDigestsFromLS().filter(\n (digest) => digest.token && isDigestValid(digest.validTo)\n );\n\nconst saveToLS = (digests: ReviewDigest[]) => {\n localStorage.setItem(\n REVIEW_DIGEST_LOCAL_STORAGE_KEY,\n JSON.stringify(digests)\n );\n};\n\nconst deleteInvalidDigestsFromLS = () => saveToLS(getValidDigestsOnly());\n\nconst isDigestValid = (validTo: string) => new Date(validTo) > new Date();\n\nconst addNewDigestToLS = (token: string, validTo: string) => {\n const existing = getValidDigestsOnly();\n if (existing.some((x) => x.token === token) || !isDigestValid(validTo)) {\n return;\n }\n const digest: ReviewDigest = { token, validTo };\n saveToLS([...existing, digest]);\n};\n","import { Tabs } from '../../model';\n\ninterface IRenderConditions {\n shouldRenderDescriptionTab: boolean;\n shouldRenderLivestreamsTab: boolean;\n}\n\nexport const getDefaultTab = ({\n shouldRenderDescriptionTab,\n shouldRenderLivestreamsTab,\n}: IRenderConditions) => {\n if (shouldRenderLivestreamsTab) {\n return Tabs.Livestreams;\n }\n\n if (shouldRenderDescriptionTab) {\n return Tabs.Description;\n }\n\n return Tabs.AboutBrand;\n};\n","import { GetProductViewQuery } from '@notino/shared/definitions/types';\n\nexport const getProductFullName = (\n product: GetProductViewQuery['productDetailByMasterId'][number]\n): string =>\n [\n product.brandInfo && product.brandInfo.name,\n product.collection,\n product.subName,\n ]\n .filter(Boolean)\n .join(' ');\n","import { GetProductViewQuery } from '@notino/shared/definitions/types';\n\nimport { isEmpty } from '@helpers/utils';\n\ninterface IGetDescriptionIndex {\n description: string;\n composition: GetProductViewQuery['productDetailByMasterId'][number]['composition'];\n variant: GetProductViewQuery['productDetailByMasterId'][number]['variants'][number];\n}\nexport const getDescriptionIndex = ({\n description,\n composition,\n variant,\n}: IGetDescriptionIndex) => {\n if (\n !isEmpty(description) ||\n composition.length > 0 ||\n variant.characteristics.length > 0\n ) {\n return 'prodDescriptionTabName';\n }\n return '';\n};\n","import * as React from 'react';\n\nimport { useQuery, QueryHookOptions } from '@apollo/client';\n\nimport {\n GetProductsByIdBatchedQuery,\n GetProductsByIdBatchedQueryVariables,\n} from '@notino/shared/definitions/types';\n\nimport getProductsByIdBatchedQuery from '@components/Universals/BatchVPProductQuery/queries/productsByIdBatched.graphql';\n\ntype Options = {\n filterNullProducts?: boolean;\n} & Partial<\n Omit<\n QueryHookOptions<\n GetProductsByIdBatchedQuery,\n GetProductsByIdBatchedQueryVariables\n >,\n 'variables'\n >\n>;\n\nexport const useProductsByIdsBatched = (\n ids: string[],\n {\n filterNullProducts = true,\n skip = false,\n ssr = false,\n errorPolicy = 'all',\n onCompleted,\n ...restOptions\n }: Options = {}\n) => {\n const { data, ...restResponse } = useQuery<\n GetProductsByIdBatchedQuery,\n GetProductsByIdBatchedQueryVariables\n >(getProductsByIdBatchedQuery, {\n // we have to check if ids.length === 0 to avoid call to api with empty ids\n skip: ids.length === 0 || skip,\n errorPolicy,\n ssr,\n variables: {\n ids,\n },\n onCompleted: filterNullProducts\n ? (response) => {\n onCompleted?.({\n ...response,\n vpProductByIds: response.vpProductByIds?.filter(Boolean),\n });\n }\n : onCompleted,\n ...restOptions,\n });\n\n const products = React.useMemo(() => {\n if (!data) {\n return data;\n }\n return {\n ...data,\n vpProductByIds: filterNullProducts\n ? data.vpProductByIds?.filter(Boolean)\n : data.vpProductByIds,\n };\n }, [filterNullProducts, data]);\n\n return {\n data: products,\n ...restResponse,\n };\n};\n","import styled from 'styled-components';\n\nimport { breakpoints, css } from '@notino/react-styleguide';\n\nimport { TitleCSS } from '../styled';\n\nexport const Title = styled.div`\n ${TitleCSS}\n`;\n\ninterface IContainerProps {\n withDivider: boolean;\n newPdDesignEnabled: boolean;\n}\n\nexport const Container = styled.section`\n ${({ newPdDesignEnabled }) =>\n !newPdDesignEnabled &&\n css`\n margin: 0 -0.9375rem;\n `}\n box-sizing: border-box;\n display: flex;\n @media (min-width: ${breakpoints.md}) {\n margin: 0;\n border-top: 0;\n }\n`;\n\nexport const MarginWrapper = styled.div<{ newPdDesignEnabled: boolean }>`\n height: inherit;\n margin: 1rem 0;\n width: 100%;\n\n @media (min-width: ${({ newPdDesignEnabled }) =>\n newPdDesignEnabled ? breakpoints.lg : breakpoints.md}) {\n margin: 0;\n }\n`;\n","import * as React from 'react';\nimport { FormattedMessage, MessageDescriptor } from 'react-intl';\n\nimport { Divider } from '@components/Divider';\n\nimport { useNewPdDesignEnabled } from '../hooks/useNewPdDesignEnabled';\nimport { Clear } from '../styled';\n\nimport { Container, MarginWrapper, Title } from './styled';\n\nexport interface ISectionContainerProps {\n titleMessage?: MessageDescriptor;\n titleId?: string;\n containerId?: string;\n listLabel?: string;\n withDivider?: boolean;\n children: React.ReactNode;\n}\n\nexport const SectionContainer = React.forwardRef<\n HTMLDivElement,\n ISectionContainerProps\n>(({ containerId, withDivider, titleId, titleMessage, children }, ref) => {\n const newPdDesignEnabled = useNewPdDesignEnabled();\n return (\n <>\n \n \n {withDivider && }\n\n {Boolean(titleMessage) && (\n \n \n \n )}\n\n {children}\n \n \n\n \n >\n );\n});\n","import * as React from 'react';\n\nimport { dispatchNotinoEvent } from '@utils/notino-events';\n\ninterface Options {\n section: string;\n}\n\nconst EMPTY: Options = {\n section: undefined,\n};\n\nexport const dispatchProductsShownEvent = (\n products:\n | Array<{\n id: number | string;\n attributes?: { Pharmacy?: boolean };\n } | null>\n | null\n | undefined,\n options = EMPTY\n) => {\n if (products && Array.isArray(products)) {\n dispatchNotinoEvent({\n name: 'products:shown',\n payload: {\n section: options.section,\n products: products.filter(Boolean).map((product) => ({\n id: String(product.id),\n isPharmacy: Boolean(product.attributes?.Pharmacy),\n })),\n },\n });\n }\n};\n\nexport const useDispatchProductsShowEvent = (\n products: Array<{\n id: number | string;\n attributes?: { Pharmacy: boolean };\n }>,\n options = EMPTY\n) => {\n const { section } = options;\n React.useEffect(() => {\n dispatchProductsShownEvent(products, { section });\n }, [products, section]);\n};\n","import { useIntl } from 'react-intl';\n\nimport { useApolloClient } from '@apollo/client';\n\nimport { GetRequestHeadersQuery } from '@notino/shared/definitions/types';\n\nimport { useFeatureFlags } from '@context/launch-darkly/LDProvider';\n\nimport getRequestHeadersQuery from '../../../../queries/requestHeaders.graphql';\n\ntype Return =\n | {\n isDualPriceForCroatia: true;\n getRightSidePrice: (value: number) => number;\n getRightSideFormattedPrice: (value: number) => string;\n getLeftSidePrice: (value: number) => number;\n getLeftSideFormattedPrice: (value: number) => string;\n rightSideCurrency: string;\n rightSideCurrencyCode: string;\n leftSideCurrency: string;\n }\n | {\n isDualPriceForCroatia: false;\n getRightSidePrice: null;\n getRightSideFormattedPrice: null;\n getLeftSidePrice: null;\n getLeftSideFormattedPrice: null;\n rightSideCurrency: null;\n rightSideCurrencyCode: null;\n leftSideCurrency: null;\n };\n\nconst HRK_EUR_EXCHANGE_SEPTEMBER_2022 = 7.5345;\nconst EUR_HRK_EXCHANGE_SEPTEMBER_2022 = 1 / HRK_EUR_EXCHANGE_SEPTEMBER_2022;\n\nexport const useIsDualPriceForCroatia = (): Return => {\n const { dualCurrencyPrice } = useFeatureFlags();\n const { cache } = useApolloClient();\n const { formatNumber } = useIntl();\n\n if (!dualCurrencyPrice) {\n return {\n isDualPriceForCroatia: false,\n getRightSidePrice: null,\n getRightSideFormattedPrice: null,\n getLeftSidePrice: null,\n getLeftSideFormattedPrice: null,\n rightSideCurrency: null,\n leftSideCurrency: null,\n rightSideCurrencyCode: null,\n };\n }\n\n const {\n RequestHeaders: {\n hrkDefaultCurrency: { code, exchangeRates },\n },\n } = cache.readQuery({\n query: getRequestHeadersQuery,\n });\n\n const isEurBaseCurrency = code === 'EUR';\n const toEurRate = exchangeRates?.EUR || HRK_EUR_EXCHANGE_SEPTEMBER_2022;\n const toHrkRate = exchangeRates?.HRK || EUR_HRK_EXCHANGE_SEPTEMBER_2022;\n const exchangeRate = isEurBaseCurrency ? toHrkRate : toEurRate;\n const convert = (value: number) => value / exchangeRate;\n const format = (value: number) =>\n formatNumber(value, {\n minimumFractionDigits: 2,\n maximumFractionDigits: 2,\n });\n\n const getRightSidePrice = (value: number) =>\n isEurBaseCurrency ? convert(value) : value;\n const getLeftSidePrice = (value: number) =>\n isEurBaseCurrency ? value : convert(value);\n\n return {\n isDualPriceForCroatia: true,\n getRightSidePrice,\n getLeftSidePrice,\n getRightSideFormattedPrice: (value) => format(getRightSidePrice(value)),\n getLeftSideFormattedPrice: (value) => format(getLeftSidePrice(value)),\n rightSideCurrency: 'kn',\n leftSideCurrency: '€',\n rightSideCurrencyCode: 'HRK',\n };\n};\n","import { useFeatureFlags } from '@context/launch-darkly/LDProvider';\n\nexport const useNewPdDesignEnabled = () => {\n const { newPdDesign } = useFeatureFlags();\n return newPdDesign;\n};\n","import { useFeatureFlags } from '@context/launch-darkly/LDProvider';\n\nconst REVIEWS_MIN_COUNT = 3;\nconst RATING_MIN_COUNT = 3;\n\nexport const useShouldShowReviewPage = (\n reviewCount: number,\n ratingSummary: number\n) => {\n const { pdHasAllReviewsPage } = useFeatureFlags();\n\n return (\n reviewCount >= REVIEWS_MIN_COUNT &&\n ratingSummary >= RATING_MIN_COUNT &&\n pdHasAllReviewsPage\n );\n};\n","import * as React from 'react';\n\nimport { ProductDetailViewFragmentFragment } from '@notino/shared/definitions/types';\n\nimport { usePriceConfig } from '@components/PriceLabel/usePriceConfig';\n\nexport const useSsrProductUrl = (\n product: ProductDetailViewFragmentFragment\n) => {\n const { data: { currentLocale } = {} } = usePriceConfig();\n\n const xDefault = React.useMemo(() => {\n const findByCulture = (culture: string) =>\n product.alternateSites.find((site) => site.culture === culture);\n return (\n findByCulture('en-GB') ||\n findByCulture('cs-CZ') ||\n product.alternateSites[0]\n );\n }, [product.alternateSites]);\n\n const url = React.useMemo(\n () =>\n (\n product.alternateSites.find(\n (site) => site.culture === currentLocale?.tag\n ) || xDefault\n )?.link,\n [product, currentLocale, xDefault]\n );\n\n return { url, xDefault } as const;\n};\n","export enum Tabs {\n Description = 'description',\n // eslint-disable-next-line no-shadow\n RelatedArticles = 'relatedArticles',\n // eslint-disable-next-line no-shadow\n AboutBrand = 'aboutBrand',\n Livestreams = 'livestreams',\n}\n\nexport enum TabIds {\n Description = 1,\n Composition = 2,\n RelatedArticles = 3,\n AboutBrand = 4,\n RichContent = 5,\n Reviews = 6,\n Other = 7,\n Livestreams = 8,\n}\n","import styled, { css } from 'styled-components';\n\nimport { breakpoints, theme, Grid } from '@notino/react-styleguide';\n\nimport { ViewPort } from '@components';\n\nexport const Main = styled.div``;\n\nexport const Clear = styled.div`\n clear: both;\n`;\n\nexport const ProductTabContentGrid = styled(Grid)<{\n newPdDesignEnabled: boolean;\n}>`\n ${({ newPdDesignEnabled }) =>\n newPdDesignEnabled &&\n css`\n padding-left: 0;\n padding-right: 0;\n margin-bottom: 2rem;\n @media (min-width: ${breakpoints.lg}) {\n margin-bottom: 4rem;\n }\n `}\n`;\n\nexport const TopSection = styled.div<{\n newPdDesign: boolean;\n isLoading?: boolean;\n}>`\n display: flex;\n flex-direction: column;\n position: relative;\n\n ${({ newPdDesign }) => css`\n @media (min-width: ${newPdDesign ? breakpoints.lg : breakpoints.md}) {\n flex-direction: row;\n }\n `}\n\n ${({ newPdDesign }) =>\n newPdDesign &&\n css`\n @media (min-width: ${breakpoints.lg}) {\n padding-top: 1.625rem;\n }\n `}\n\n ${({ newPdDesign, isLoading }) =>\n !newPdDesign &&\n !isLoading &&\n css`\n margin: 0 -0.9375rem;\n `}\n`;\n\nexport const GalleryWrapper = styled.div<{ newPdDesignEnabled: boolean }>`\n ${({ newPdDesignEnabled }) =>\n newPdDesignEnabled\n ? css`\n @media (min-width: ${breakpoints.md}) {\n width: 100%;\n }\n @media (min-width: ${breakpoints.lg}) {\n height: 100%;\n padding-right: 2rem;\n padding-top: 1.25rem;\n width: 65%;\n }\n @media (min-width: ${breakpoints.xl}) {\n width: 70%;\n padding-right: 3rem;\n }\n `\n : css`\n @media (min-width: ${breakpoints.md}) {\n width: 42%;\n margin-top: 1rem;\n padding-right: 2rem;\n }\n `}\n`;\n\nexport const MainInfoWrapper = styled.div<{ newPdDesignEnabled: boolean }>`\n ${({ newPdDesignEnabled }) =>\n newPdDesignEnabled\n ? css`\n @media (min-width: ${breakpoints.md}) {\n width: 100%;\n }\n @media (min-width: ${breakpoints.lg}) {\n width: 35%;\n }\n @media (min-width: ${breakpoints.xl}) {\n width: 30%;\n }\n `\n : css`\n @media (min-width: ${breakpoints.md}) {\n width: 58%;\n }\n `}\n`;\n\nexport const PDViewPort = styled(ViewPort)<{ newPdDesignEnabled: boolean }>`\n ${({ newPdDesignEnabled }) =>\n !newPdDesignEnabled &&\n css`\n padding-left: 16px;\n padding-right: 16px;\n `}\n`;\n\nexport const TitleCSS = css<{ newPdDesignEnabled: boolean }>`\n text-align: left !important;\n display: flex;\n align-items: center;\n justify-content: flex-start;\n cursor: pointer;\n flex-basis: 100%;\n\n font-size: 1rem;\n line-height: 1.18;\n\n ${({ newPdDesignEnabled }) =>\n newPdDesignEnabled\n ? css`\n font-weight: 500;\n @media (min-width: ${breakpoints.lg}) {\n ${theme.typography.titleLarge};\n }\n `\n : css`\n font-weight: bold;\n margin: 0.625rem 0 !important;\n `}\n\n @media (min-width: ${({ newPdDesignEnabled }) =>\n newPdDesignEnabled ? breakpoints.lg : breakpoints.md}) {\n ${({ newPdDesignEnabled }) =>\n !newPdDesignEnabled &&\n css`\n font-size: 1.75rem !important;\n margin: 3rem 0 1.75rem !important;\n `}\n\n display: block;\n cursor: text;\n }\n`;\n\nexport const TitleWrapper = styled.div<{ newPdDesignEnabled: boolean }>`\n ${TitleCSS};\n`;\n\nexport const ProductTitleWrapper = styled.div<{ newPdDesignEnabled: boolean }>`\n text-align: left;\n display: inline-flex;\n align-items: flex-start;\n justify-content: space-between;\n cursor: pointer;\n flex-basis: 100%;\n width: 100%;\n margin: 0.625rem 0 !important;\n\n font-size: 1rem;\n line-height: 1.18;\n\n ${({ newPdDesignEnabled }) =>\n newPdDesignEnabled\n ? css`\n display: flex;\n font-weight: 500;\n @media (min-width: ${breakpoints.lg}) {\n padding-left: 0;\n ${theme.typography.titleLarge};\n }\n `\n : css`\n font-weight: bold;\n @media (min-width: ${breakpoints.md}) {\n font-size: 1.75rem;\n }\n `}\n\n @media (min-width: ${breakpoints.md}) {\n cursor: text;\n }\n`;\n\ninterface IDescriptionTitleProps {\n fullWidth: boolean;\n}\n\nexport const DescriptionTitle = styled.div<{\n newPdDesignEnabled: boolean;\n fullWidth: boolean;\n}>`\n ${({ newPdDesignEnabled, fullWidth }) =>\n newPdDesignEnabled\n ? css`\n width: 100%;\n @media (min-width: ${breakpoints.lg}) {\n width: ${fullWidth ? '100%' : '65%'};\n }\n\n @media (min-width: ${breakpoints.xl}) {\n width: ${fullWidth ? '100%' : '70%'};\n }\n `\n : css`\n @media (min-width: ${breakpoints.md}) {\n width: ${(props: IDescriptionTitleProps) =>\n props.fullWidth ? '100%' : '66%'};\n padding-right: ${(props: IDescriptionTitleProps) =>\n props.fullWidth ? '0' : '2rem'};\n }\n `}\n`;\n\nexport const CompositionTitle = styled.div`\n max-width: 66%;\n`;\n\nexport const CharacteristicsTitle = styled.div<{\n newPdDesignEnabled?: boolean;\n}>`\n visibility: hidden;\n\n @media (min-width: ${({ newPdDesignEnabled }) =>\n newPdDesignEnabled ? breakpoints.lg : breakpoints.md}) {\n visibility: visible;\n\n ${({ newPdDesignEnabled }) =>\n newPdDesignEnabled\n ? css`\n width: 100%;\n font-size: 1.5rem;\n margin-bottom: 1rem;\n @media (min-width: ${breakpoints.lg}) {\n width: 35%;\n }\n\n @media (min-width: ${breakpoints.xl}) {\n width: 30%;\n }\n `\n : css`\n width: 34%;\n `}\n }\n`;\n\nexport const TextNormal = styled.span<{ newPdDesignEnabled: boolean }>`\n display: none;\n\n @media (min-width: ${({ newPdDesignEnabled }) =>\n newPdDesignEnabled ? breakpoints.lg : breakpoints.md}) {\n display: inline;\n ${({ newPdDesignEnabled }) =>\n !newPdDesignEnabled &&\n css`\n font-weight: 300;\n `}\n }\n`;\n\nexport const CommonContainer = styled.section`\n display: flex;\n`;\n\nexport const CommonMarginWrapper = styled.div`\n flex-basis: 100%;\n height: inherit;\n\n @media (min-width: ${breakpoints.md}) {\n margin: 0;\n }\n`;\n\nexport const PriceWrapper = styled.span``;\n\nexport const CurrencyWrapper = styled(PriceWrapper)`\n margin-left: 0.3rem;\n margin-right: 0.3rem;\n`;\n\nexport const ShowOnDesktop = styled.div<{ newPdDesign?: boolean }>`\n ${({ newPdDesign }) => css`\n display: none;\n @media (min-width: ${newPdDesign ? breakpoints.lg : breakpoints.md}) {\n display: block;\n }\n `}\n`;\n\nexport const ShowOnMobile = styled.div<{ newPdDesign?: boolean }>`\n ${({ newPdDesign }) => css`\n display: block;\n @media (min-width: ${newPdDesign ? breakpoints.lg : breakpoints.md}) {\n display: none;\n }\n `}\n`;\n\nexport const AbsoluteHeaderWrapper = styled.div<{\n newDesign: boolean;\n isLogoForBrandAvailable: boolean;\n}>`\n position: absolute;\n\n ${({ newDesign, isLogoForBrandAvailable }) =>\n newDesign\n ? css`\n top: ${isLogoForBrandAvailable ? '100px' : '60px'};\n @media (min-width: ${breakpoints.lg}) {\n all: unset;\n }\n `\n : css`\n top: ${isLogoForBrandAvailable ? '70px' : '34px'};\n @media (min-width: ${breakpoints.md}) {\n all: unset;\n }\n `}\n`;\n\nexport const AbsoluteTabsWrapper = styled.div<{\n newDesign: boolean;\n}>`\n ${({ newDesign }) =>\n newDesign &&\n css`\n width: 100%;\n position: absolute;\n top: 1400px; // this will be dynamically adjusted by useMoveTabsToMobilePlaceholder\n @media (min-width: ${breakpoints.lg}) {\n all: unset;\n }\n `}\n`;\n\nexport const ReviewSummaryCount = styled.span`\n margin-left: 0.5rem;\n font-size: 0.75rem;\n font-weight: 400;\n line-height: 1.125rem;\n color: ${theme.palette.neutralDark};\n @media (min-width: ${breakpoints.md}) {\n display: none;\n }\n`;\n","import { ApolloError } from '@apollo/client';\n\nimport {\n VariantFragmentFragment,\n GetProductViewQuery,\n} from '@notino/shared/definitions/types';\nimport {\n AdditionalServicesAvailability,\n LoyaltyDiscount,\n} from '@notino/web-tracking';\n\nimport { IEngraving } from './../../ProductListing/model';\nimport { IModifaceEffectVariants } from './ModiFaceModal/model';\n\nexport const getModifaceEffectsForVariant = (\n modifaceMakeUpData: GetProductViewQuery['productDetailByMasterId'][number]['variants'][number]['modifaceMakeUpData']\n) => {\n return modifaceMakeUpData.reduce((acc, effect) => {\n // Loop through shades\n const shades = effect.shades.map((shade) => ({\n ...shade,\n category: effect.category,\n }));\n return [...acc, ...shades];\n }, []);\n};\n\nexport const getModifaceVariants = (\n variants: GetProductViewQuery['productDetailByMasterId'][number]['variants'][number][]\n): IModifaceEffectVariants => {\n return variants.reduce((result, { id, modifaceMakeUpData }) => {\n if (!modifaceMakeUpData || modifaceMakeUpData.length === 0) {\n return result;\n }\n\n // If it has modiface effect\n return {\n ...result,\n [id]: getModifaceEffectsForVariant(modifaceMakeUpData),\n };\n }, {});\n};\n\nexport const getModifaceAvailability = (\n hasModiface: boolean\n): AdditionalServicesAvailability =>\n hasModiface\n ? AdditionalServicesAvailability.available\n : AdditionalServicesAvailability.notAvailable;\n\nexport const getTryItFirstAvailability = (\n cartTryItFirstShow: boolean,\n tryItFirstCampaigns: GetProductViewQuery['productDetailByMasterId'][number]['campaigns'],\n selectedVariant: VariantFragmentFragment,\n additionalServicesError: ApolloError\n) => {\n if (additionalServicesError) {\n return AdditionalServicesAvailability.timeout;\n }\n\n if (!cartTryItFirstShow) {\n return AdditionalServicesAvailability.hidden;\n }\n\n if (\n tryItFirstCampaigns.some(\n (tryItFirstCampaign) =>\n tryItFirstCampaign.productId === selectedVariant.id\n )\n ) {\n return AdditionalServicesAvailability.available;\n }\n\n return AdditionalServicesAvailability.notAvailable;\n};\n\nexport const getEngravingAvailability = (\n cartEngravingShow: boolean,\n engraving: IEngraving,\n additionalServicesError: ApolloError\n): AdditionalServicesAvailability => {\n if (additionalServicesError) {\n return AdditionalServicesAvailability.timeout;\n }\n\n if (engraving.allowed) {\n return cartEngravingShow && engraving.config.isAvailable\n ? AdditionalServicesAvailability.available\n : AdditionalServicesAvailability.hidden;\n }\n\n return AdditionalServicesAvailability.notAvailable;\n};\n\nexport const getVariantDescription = (variant, product): string =>\n variant.attributes && variant.attributes.Master\n ? product.annotation\n : variant.variantName;\n\nexport const getLoyaltyDiscountState = (\n customerBenefits: GetProductViewQuery['productDetailByMasterId'][number]['variants'][number]['customerBenefits']\n) => {\n if (customerBenefits?.availableDiscount) {\n return LoyaltyDiscount.available;\n }\n\n if (customerBenefits?.customerDiscount) {\n return LoyaltyDiscount.active;\n }\n\n return LoyaltyDiscount.notAvailable;\n};\n\nexport const isGiftAvailable = (\n campaigns: GetProductViewQuery['productDetailByMasterId'][number]['campaigns']\n) => {\n return Boolean(\n campaigns.find(\n (campaign) =>\n campaign.displayOnDetailPage &&\n (campaign.detailPageText || campaign.text)\n )\n );\n};\n\nexport const getModifaceHairEffectsForVariant = (\n hairModifaceEffects: GetProductViewQuery['productDetailByMasterId'][number]['variants'][number]['hairModifaceEffects']\n) => {\n return hairModifaceEffects.reduce((acc, effect) => {\n // Loop through shades\n const shades = effect.shades.map((shade) => ({\n ...shade,\n category: effect.category,\n histogram: {\n r: shade.histogram_r,\n g: shade.histogram_g,\n b: shade.histogram_b,\n c: shade.histogram_c,\n totalpixels: shade.totalpixels,\n },\n }));\n return [...acc, ...shades];\n }, []);\n};\n\nexport const getModifaceHairVariants = (\n variants: GetProductViewQuery['productDetailByMasterId'][number]['variants'][number][]\n): IModifaceEffectVariants => {\n return variants.reduce((result, { id, hairModifaceEffects }) => {\n if (!hairModifaceEffects || hairModifaceEffects.length === 0) {\n return result;\n }\n return {\n ...result,\n [id]: getModifaceHairEffectsForVariant(hairModifaceEffects),\n };\n }, {} as IModifaceEffectVariants);\n};\n","import { useQuery } from '@apollo/client';\n\nimport { GetSettingsQuery } from '@notino/shared/definitions/types';\n\nimport getSettingsQuery from './queries/getSettings.graphql';\n\nconst DEFAULT: GetSettingsQuery = {\n Settings: {\n showUnitPrices: null,\n __typename: 'Settings',\n isNewShoppingCartEndpointEnabled: null,\n },\n};\n\n/**\n * Feel free to extend this method to get more values if needed in future\n */\nexport const useSettings = (): GetSettingsQuery | Record => {\n const { data, error, loading } = useQuery(getSettingsQuery);\n\n if (error || loading || !data) {\n return DEFAULT;\n }\n\n return data;\n};\n","import { defineMessages } from 'react-intl';\n\nexport const messages = defineMessages({\n forbiddenDescription: {\n id: 'pd.forbidden.product.description',\n defaultMessage:\n 'Mrzí nás, že jste nenašli, co jste hledali. Věříme však, že si díky naší široké nabídce vyberete jiný produkt, který vám udělá radost.',\n },\n});\n","import styled from 'styled-components';\n\nimport { Heading, breakpoints } from '@notino/react-styleguide';\n\nexport const ForbiddenProductScreenWrapper = styled(Heading.H1)`\n margin: auto;\n display: block;\n min-height: 21.875rem;\n`;\n\nexport const TitleWrapper = styled.div`\n font-size: 1rem;\n display: flex;\n justify-content: center;\n padding-bottom: 1rem;\n @media (min-width: ${breakpoints.md}) {\n font-size: 1.5rem;\n }\n`;\n\nexport const Brand = styled.span`\n font-weight: 300;\n`;\n\nexport const Collection = styled.span`\n font-size: 1.125rem;\n font-weight: bold;\n margin-left: 0.5rem;\n\n @media (min-width: ${breakpoints.md}) {\n font-size: 1.5rem;\n }\n`;\n\nexport const ForbiddenInfoWrapper = styled.div`\n max-width: 50rem;\n text-align: left;\n font-size: 0.875rem;\n line-height: 1.5;\n font-weight: normal;\n\n @media (min-width: ${breakpoints.md}) {\n text-align: center;\n display: block;\n margin: auto;\n font-size: 1rem;\n }\n`;\nexport const ViewPort = styled.main`\n margin-top: 3.5rem;\n padding-bottom: 3.125rem;\n`;\n","import * as React from 'react';\nimport { FormattedMessage } from 'react-intl';\n\nimport { GetProductViewQuery } from '@notino/shared/definitions/types';\n\nimport { messages } from './messages';\nimport {\n ForbiddenProductScreenWrapper,\n Collection,\n Brand,\n ForbiddenInfoWrapper,\n TitleWrapper,\n ViewPort,\n} from './styled';\n\ninterface IForbiddenProductScreenProps {\n product: GetProductViewQuery['productDetailByMasterId'][number];\n}\n\nexport const ForbiddenProductScreen: React.FC = ({\n product,\n}) => (\n \n \n \n {product.brand}\n {product.collection}\n \n \n \n \n \n \n);\n","import * as React from 'react';\nimport { useInView } from 'react-intersection-observer';\n\nimport { ReactFCC } from '@notino/react-styleguide';\n\nexport const RenderInView: ReactFCC = ({ children }) => {\n const [ref, inView] = useInView({ triggerOnce: true, rootMargin: '100px' });\n\n return {inView ? children : null}
;\n};\n","import * as Sentry from '@sentry/browser';\n\ninterface MetaInformation {\n keywords: string;\n description: string;\n title: string;\n}\n\nexport const replaceMetaInformation = ({\n keywords,\n description,\n title,\n}: MetaInformation) => {\n try {\n const metaCollection =\n typeof document !== 'undefined' && document.getElementsByTagName('meta');\n if (metaCollection?.length > 0) {\n // eslint-disable-next-line\n const keywordsElement = metaCollection['keywords'];\n if (keywordsElement) {\n keywordsElement.content = keywords;\n }\n // eslint-disable-next-line\n const descriptionElement = metaCollection['description'];\n if (descriptionElement) {\n descriptionElement.content = description;\n }\n\n document.title = title;\n }\n } catch (e) {\n Sentry.captureMessage('Could not replace Meta Information', e);\n }\n};\n","import * as React from 'react';\nimport { Helmet } from 'react-helmet-async';\nimport { useLocation } from 'react-router';\n\nimport {\n ProductDetailViewFragmentFragment,\n VariantFragmentFragment,\n} from '@notino/shared/definitions/types';\n\nimport { useImageAttributes } from '@components/CdnImage/useImageAttributes';\n\nimport { useSsrProductUrl } from '../hooks/useSsrProductUrl';\n\nimport { replaceMetaInformation } from './utils';\n\ninterface HeaderMetaParams {\n product: ProductDetailViewFragmentFragment;\n variant: VariantFragmentFragment;\n}\n\nexport const HeadMeta = ({ product, variant }: HeaderMetaParams) => {\n const { url, xDefault } = useSsrProductUrl(product);\n const { pathname } = useLocation();\n const { src: imageSrc } = useImageAttributes(\n variant.imagesByCategory.mainImage.src,\n 'metaTag'\n );\n\n React.useEffect(() => {\n replaceMetaInformation({\n keywords: variant.seo.keywords,\n title: variant.seo.title,\n description: variant.seo.description,\n });\n }, [variant.seo.title, variant.seo.description, variant.seo.keywords]);\n\n const variantSite = /(\\/p-\\d+((\\/?)|(\\??.+)))$/;\n\n const showHrefLangs = !variantSite.test(pathname);\n\n return (\n \n \n \n \n \n \n \n \n\n {showHrefLangs\n ? product.alternateSites.map(({ link, culture }) => (\n \n ))\n : null}\n {showHrefLangs ? (\n \n ) : null}\n \n );\n};\n","const shim = (cb) => {\n return setTimeout(function () {\n const start = Date.now();\n cb({\n didTimeout: false,\n timeRemaining: function () {\n return Math.max(0, 50 - (Date.now() - start));\n },\n });\n }, 1);\n};\n\nconst isSupported = typeof requestIdleCallback !== 'undefined';\n\n// very optimistic shim\nconst requestIdleCallbackShim = isSupported ? requestIdleCallback : shim;\nconst cancelIdleCallbackShim = isSupported ? cancelIdleCallback : clearTimeout;\n\nexport {\n requestIdleCallbackShim as requestIdleCallback,\n cancelIdleCallbackShim as cancelIdleCallback,\n};\n","import * as Cookie from 'js-cookie';\n\nconst LAST_VISITED_PRODUCTS_COOKIE_KEY = 'lastProds';\nconst LAST_VISITED_PRODUCTS_COOKIE_DIVIDER = '-';\nconst MAX_COOKIE_ITEMS_COUNT = 15;\nconst YEAR_DAYS_COUNT = 365;\n\nconst getLastVisitedProducts = () => {\n try {\n return Cookie.get(LAST_VISITED_PRODUCTS_COOKIE_KEY);\n } catch (e) {\n return '';\n }\n};\n\nexport const loadProductsFromCookieOldestFirst = (): string[] => {\n const lastVisitedCookie = getLastVisitedProducts();\n const lastVisitedProductIdsCookie = lastVisitedCookie?.split(\n LAST_VISITED_PRODUCTS_COOKIE_DIVIDER\n );\n\n if (lastVisitedProductIdsCookie?.length) {\n return lastVisitedProductIdsCookie;\n }\n\n return [];\n};\n\nexport const appendProductToCookie = (productId: string) => {\n const productsWithoutCurrentProduct =\n loadProductsFromCookieOldestFirst().filter((id) => id !== productId);\n const productIds = [...productsWithoutCurrentProduct, productId].slice(\n -1 * MAX_COOKIE_ITEMS_COUNT\n );\n\n removeOldParfumsCookie();\n\n Cookie.set(\n LAST_VISITED_PRODUCTS_COOKIE_KEY,\n productIds.join(LAST_VISITED_PRODUCTS_COOKIE_DIVIDER),\n { expires: YEAR_DAYS_COUNT }\n );\n};\n\nconst removeOldParfumsCookie = () => {\n // eg. www.notino.cz\n const hostName = window.location.hostname;\n const allSubDomainsHostName = hostName.replace(/^(www\\.)/, '.');\n\n Cookie.remove(LAST_VISITED_PRODUCTS_COOKIE_KEY, {\n domain: allSubDomainsHostName,\n });\n};\n","import { useEffect } from 'react';\n\nimport { GetProductViewQuery } from '@notino/shared/definitions/types';\n\nimport { EXCLUDE_FROM_LAST_VISITED } from '@utils/constants';\nimport { requestIdleCallback } from '@utils/requestIdleCallback';\n\nimport { appendProductToCookie } from './utils';\n\ntype IUseAddToLastVisited = Pick<\n GetProductViewQuery['productDetailByMasterId'][number],\n 'variants' | 'id'\n>;\n\nexport const useAddToLastVisited = ({ id, variants }: IUseAddToLastVisited) => {\n useEffect(() => {\n requestIdleCallback(() => {\n const canBuy = variants.some((variant) => variant.canBuy === true);\n\n const canSaveToLastVisited = variants.every(\n (variant) =>\n !variant.navigationValues.includes(EXCLUDE_FROM_LAST_VISITED)\n );\n if (canBuy && canSaveToLastVisited) {\n appendProductToCookie(id);\n }\n });\n }, [id, variants]);\n};\n","export const MIN_PRICE_PROMO_LABEL = 'min_price';\nexport const RRP_PRICE_PROMO_LABEL = 'rrp_price';\n","import {\n GetProductViewQuery,\n ProductDetailViewFragmentFragment,\n} from '@notino/shared/definitions/types';\nimport {\n AdditionalServicesAvailability,\n LoyaltyDiscount,\n} from '@notino/web-tracking';\nimport { VariantSelector } from '@notino/web-tracking/dist/types/package/ga4/events/constants';\n\nimport {\n runTrackingEventViewItem,\n runExponeaFunctions,\n} from '@containers/App/utils';\nimport { dispatchTrackingEvent } from '@context/tracking/utils';\nimport { ProductEventWither } from '@helpers/googleTagManager';\n\nimport {\n RRP_PRICE_PROMO_LABEL,\n MIN_PRICE_PROMO_LABEL,\n} from './hooks/usePriceLogic/tracking';\nimport { IModifaceEffectVariants } from './ModiFaceModal/model';\n\ninterface ITrackPageReloadParams {\n variant: GetProductViewQuery['productDetailByMasterId'][number]['variants'][number];\n product: GetProductViewQuery['productDetailByMasterId'][number];\n modifaceVariants: IModifaceEffectVariants;\n engravingAvailable: AdditionalServicesAvailability;\n tryItFirstAvailable: AdditionalServicesAvailability;\n giftAvailable: boolean;\n loyaltyDiscount?: LoyaltyDiscount;\n rrpShown: boolean;\n minimalPriceShown: boolean;\n trackingDisplayType?: VariantSelector;\n}\nexport const trackPageReload = ({\n variant,\n product,\n modifaceVariants = {},\n engravingAvailable,\n tryItFirstAvailable,\n giftAvailable,\n loyaltyDiscount,\n rrpShown,\n minimalPriceShown,\n trackingDisplayType,\n}: ITrackPageReloadParams) => {\n // We need to use setTimeout for perf. optim. of hydration\n // This event is often called with 'page_view' event\n // They are often called in separate `useEffect` hooks\n // To make sure this event is logged after 'page_view' event\n // We need to use setTimeout, just to track this event a little later\n\n setTimeout(() => {\n dispatchTrackingEvent({\n event: 'view_item',\n variant_selector: trackingDisplayType,\n products: [\n ProductEventWither()\n .withProduct(product)\n .withVariant(variant)\n .withAdditionalPromoLabels([\n rrpShown && RRP_PRICE_PROMO_LABEL,\n minimalPriceShown && MIN_PRICE_PROMO_LABEL,\n ])\n .withServices({\n engravingAvailable,\n tryItFirstAvailable,\n modifaceVariants,\n loyaltyDiscount,\n giftAvailable,\n shadefinderAvailable: product.isShadeFinderAvailable,\n })\n .build(),\n ],\n _clear: true,\n });\n });\n};\n\nexport const trackItemInExponea = (\n product: ProductDetailViewFragmentFragment,\n variant: ProductDetailViewFragmentFragment['variants'][number]\n) => {\n runTrackingEventViewItem({\n id: variant.id,\n name: variant.name,\n code: variant.productCode,\n masterProductCode: product.code,\n brandName: product.brandInfo.name,\n gender: variant.gender,\n marketType: product.marketType,\n price: variant.price.value,\n kind: product.kind,\n categories: product.category,\n subcategories: product.subCategory,\n types: product.type,\n stockQuantity: variant.stockAvailability.code,\n attributes: variant.attributes,\n });\n runExponeaFunctions();\n};\n","import { defineMessages } from 'react-intl';\n\nexport default defineMessages({\n youSave: {\n id: 'pd.youSave',\n defaultMessage: 'Ušetříte',\n },\n betterOffer: {\n id: 'pd.betterOffer',\n defaultMessage: 'Výhodneji o {percentage}%',\n },\n recommendedPrice: {\n id: 'pd.price.recommended',\n defaultMessage: 'Tuto cenu doporučuje dodavatel produktu. {link}',\n },\n recommendedPriceLink: {\n id: 'pd.price.recommended.link',\n defaultMessage: 'null',\n },\n convertedPrice: {\n id: 'pd.price.converted',\n defaultMessage:\n 'Cena doporučená dodavatelem a přepočtená z cizí měny. {link}',\n },\n convertedPriceLink: {\n id: 'pd.price.converted.link',\n defaultMessage: 'null',\n },\n historicalPrice: {\n id: 'pd.price.historical',\n defaultMessage:\n 'Maximální cena, za kterou se produkt nabízel v posledních dvou letech. {link}',\n },\n historicalPriceLink: {\n id: 'pd.price.historical.link',\n defaultMessage: 'null',\n },\n recentPrice: {\n id: 'pd.price.recent',\n defaultMessage:\n 'Nejnižší cena, za kterou se produkt nabízel v posledním měsíci. {link}',\n },\n recentPriceLink: {\n id: 'pd.price.recent.link',\n defaultMessage: 'null',\n },\n priceLinkText: {\n id: 'pd.price.link.text',\n defaultMessage: 'Více informací',\n },\n});\n","import styled from 'styled-components';\n\nimport { theme } from '@notino/react-styleguide';\n\nexport const TooltipContent = styled.div`\n font-size: 0.8rem;\n padding: 0.5rem;\n text-align: center;\n white-space: normal;\n min-width: 16rem;\n\n a {\n font-weight: 400;\n color: ${theme.palette.basic};\n white-space: nowrap;\n\n :hover {\n text-decoration: none;\n }\n }\n`;\n","import * as React from 'react';\nimport { MessageDescriptor, useIntl } from 'react-intl';\n\nimport messages from './messages';\nimport { TooltipContent } from './styled';\n\nexport const useGetOriginalPriceTooltip = (): ((\n type: string\n) => JSX.Element) => {\n const { formatMessage } = useIntl();\n const getAnchorTag = useGetPriceLink();\n\n return (type: string) => {\n if (type === 'Recommended') {\n return (\n \n {formatMessage(messages.recommendedPrice, {\n link: getAnchorTag(messages.recommendedPriceLink),\n })}\n \n );\n }\n if (type === 'Converted') {\n return (\n \n {formatMessage(messages.convertedPrice, {\n link: getAnchorTag(messages.convertedPriceLink),\n })}\n \n );\n }\n if (type === 'HistoricalMax') {\n return (\n \n {formatMessage(messages.historicalPrice, {\n link: getAnchorTag(messages.historicalPriceLink),\n })}\n \n );\n }\n\n return null;\n };\n};\n\nexport const useRecentPriceTooltip = () => {\n const { formatMessage } = useIntl();\n const getAnchorTag = useGetPriceLink();\n\n return (\n \n {formatMessage(messages.recentPrice, {\n link: getAnchorTag(messages.recentPriceLink),\n })}\n \n );\n};\n\nconst useGetPriceLink = () => {\n const { formatMessage } = useIntl();\n\n return (href: MessageDescriptor) => {\n const formattedHref = formatMessage(href);\n if (formattedHref === 'null') {\n return null;\n }\n\n return (\n \n {formatMessage(messages.priceLinkText)}\n \n );\n };\n};\n\nexport const getDiscountPercentage = (price: number, prevPrice: number) =>\n Math.floor(100 - (price / prevPrice) * 100);\n\nexport const useGetYouSaveText = () => {\n const { formatMessage } = useIntl();\n\n return (price: number, prevPrice: number) =>\n `${formatMessage(messages.youSave)} ${getDiscountPercentage(\n price,\n prevPrice\n )}%`;\n};\n\nexport const useGetBetterOfferText = () => {\n const { formatMessage } = useIntl();\n\n return (price: number, prevPrice: number) =>\n `${formatMessage(messages.betterOffer, {\n percentage: getDiscountPercentage(price, prevPrice),\n })}`;\n};\n","import { VariantFragmentFragment } from '@notino/shared/definitions/types';\n\nimport { IPrice } from '@containers/ProductListing/model';\nimport { useFeatureFlags } from '@context/launch-darkly/LDProvider';\n\nimport {\n useGetBetterOfferText,\n useGetOriginalPriceTooltip,\n useGetYouSaveText,\n useRecentPriceTooltip,\n} from './utils';\n\n// https://gitlab.notino.com/web/nushopweb/-/blob/master/docs/diagrams/price-logic.md\ninterface IReturnValue {\n discountText?: string;\n topPrice?: {\n value: IPrice;\n isCrossed: boolean;\n tooltip: JSX.Element;\n };\n bottomRecentPrice?: { value: IPrice; tooltip: JSX.Element };\n\n /** Indicates whether converted/recommended/maxHistorical price is shown. */\n rrpShown: boolean;\n minimalPriceShown: boolean;\n}\n\nexport type UsePriceLogicParameters = Pick<\n VariantFragmentFragment,\n 'attributes' | 'price' | 'originalPrice' | 'recentMinPrice'\n>;\n\nexport const usePriceLogic = ({\n attributes,\n price,\n originalPrice,\n recentMinPrice,\n}: UsePriceLogicParameters): IReturnValue => {\n const getYouSaveText = useGetYouSaveText();\n const getBetterOfferText = useGetBetterOfferText();\n const getOriginalPriceTooltip = useGetOriginalPriceTooltip();\n const { strictSalesDisplay } = useFeatureFlags();\n const recentPriceTooltip = useRecentPriceTooltip();\n\n const isActionOrClearance: boolean =\n attributes?.Action || attributes?.Clearance;\n\n const hasVoucher = !!attributes.VoucherDiscount;\n\n if (isActionOrClearance) {\n if (recentMinPrice?.isActionEligible) {\n return {\n minimalPriceShown: true,\n rrpShown: false,\n discountText: getYouSaveText(price.value, recentMinPrice.value),\n topPrice: {\n value: recentMinPrice,\n isCrossed: true,\n tooltip: recentPriceTooltip,\n },\n };\n }\n\n if (recentMinPrice) {\n return originalPrice?.value\n ? {\n rrpShown: false,\n minimalPriceShown: true,\n bottomRecentPrice: {\n value: recentMinPrice,\n tooltip: recentPriceTooltip,\n },\n }\n : {\n minimalPriceShown: true,\n rrpShown: false,\n bottomRecentPrice: {\n value: recentMinPrice,\n tooltip: recentPriceTooltip,\n },\n };\n }\n\n return originalPrice?.value\n ? {\n rrpShown: false,\n minimalPriceShown: false,\n }\n : {\n rrpShown: false,\n minimalPriceShown: false,\n };\n }\n\n if (hasVoucher && strictSalesDisplay) {\n return recentMinPrice\n ? {\n minimalPriceShown: true,\n rrpShown: false,\n bottomRecentPrice: {\n value: recentMinPrice,\n tooltip: recentPriceTooltip,\n },\n }\n : { minimalPriceShown: false, rrpShown: false };\n }\n\n return originalPrice?.value\n ? {\n rrpShown: true,\n minimalPriceShown: false,\n topPrice: {\n value: originalPrice,\n isCrossed: false,\n tooltip: getOriginalPriceTooltip(originalPrice.type),\n },\n discountText: getBetterOfferText(price.value, originalPrice.value),\n }\n : { minimalPriceShown: false, rrpShown: false };\n};\n","import e from\"styled-components\";import{breakpoints as t}from\"./breakpoints.js\";import{P as o}from\"./index-cc00cee0.js\";var d=e(o).attrs({\"data-testid\":\"placeholder-product-title\"}).withConfig({displayName:\"styled__StyledPlaceholderProductTile\",componentId:\"sc-v935q3-0\"})([\"\"]),i=e.div.withConfig({displayName:\"styled__PlaceholdersContainer\",componentId:\"sc-v935q3-1\"})([\"display:flex;margin-top:1.25rem;\",\":nth-of-type (n + 3){display:none;@media (min-width:\",\"){display:block;}}\"],d,t.md);export{i as P,d as S};\n//# sourceMappingURL=styled-4a46840a.js.map\n","import{a as o,_ as t}from\"./_rollupPluginBabelHelpers-fc5c8eb1.js\";import*as r from\"react\";import{ProductTileModel as i}from\"./ProductTileModel.js\";import{P as e,S as s}from\"./styled-4a46840a.js\";import\"styled-components\";import\"./breakpoints.js\";import\"./theme.js\";import\"./index-cc00cee0.js\";import\"./BuyButton.js\";import\"./ButtonModel.js\";import\"./styled.js\";import\"./ActionButtonWithConfirmation.js\";import\"./CheckIcon.js\";import\"./index.js\";import\"./ThemeContext.js\";import\"./ActionButton.js\";import\"./index-19abfa9e.js\";import\"react-router-dom\";import\"./css.js\";import\"./Colors.js\";import\"./SpinnerIcon.js\";import\"./createSvgIcon-89a7bcb8.js\";import\"./WarningIcon.js\";import\"./ActionButtonModel.js\";import\"./keyframes.js\";import\"./IconButton.js\";import\"./index-ad1298a0.js\";import\"./ImagePlaceholder.js\";import\"./SkeletonElement.js\";import\"./BlackFridayIcon.js\";import\"./PowerOfBeautyIcon.js\";import\"./RecomendedByDermatologistsIcon.js\";import\"./StyledChristmasIcon.js\";import\"./ChristmasIcon.js\";import\"./StyledDermoIcon.js\";import\"./DermoIcon.js\";import\"./SummerBlackFridayIcon.js\";import\"./TagModel.js\";import\"./Tag.js\";import\"./Heading.js\";import\"./Price.js\";import\"./Ratings.js\";import\"./Stars.js\";import\"./withTheme.js\";import\"./InfoCircleIcon.js\";import\"./TooltipModel.js\";import\"./Tooltip-aa4269c8.js\";import\"./debounce.js\";import\"./WishlistButton.js\";import\"./usePrevious.js\";import\"./HeartIcon.js\";import\"./HeartOIcon.js\";import\"./utils-ec9be121.js\";import\"./styled-fd5dd143.js\";import\"./model.js\";var m=[\"placeholderCount\",\"id\",\"rows\",\"hasButton\",\"animate\",\"containerProps\"],p=function(p){var n=p.placeholderCount,a=void 0===n?4:n,c=p.id,j=void 0===c?\"listPlaceholder\":c,l=p.rows,d=void 0===l?6:l,u=p.hasButton,I=void 0!==u&&u,h=p.animate,y=void 0===h||h,B=p.containerProps,f=o(p,m),P={xs:a>2?7:6,sm:a>3?5:4,md:12/a},g={state:i.State.placeholder,rows:d,hasButton:I,animate:y};return r.createElement(e,t({},f,B),Array.from(Array(a).keys()).map((function(o){return r.createElement(s,{key:j+\"-\"+o,columns:P,productProps:g})})))};export{p as ProductSliderPlaceholder};\n//# sourceMappingURL=ProductSliderPlaceholder.js.map\n","import { css } from 'styled-components';\n\nimport { breakpoints, styled } from '@notino/react-styleguide';\n\nexport const Wrapper = styled.div<{ newDesign: boolean }>`\n ${({ newDesign }) =>\n newDesign\n ? css`\n margin-top: 1.5rem;\n @media (min-width: ${breakpoints.lg}) {\n margin-bottom: 4rem;\n }\n `\n : css`\n margin-top: 2rem;\n `};\n`;\n","import * as React from 'react';\n\nimport { findIndex } from 'lodash';\n\nimport { ProductSliderPlaceholder } from '@notino/react-styleguide';\nimport { GetProductsByIdBatchedQuery } from '@notino/shared/definitions/types';\n\nimport { ProductSlider } from '@components/ProductSlider';\nimport { useGetProductItemProps } from '@components/Universals/ProductItem/useGetProductItemProps';\nimport { IProduct } from '@containers/ProductListing/model';\nimport {\n useTrackDynamicList,\n ProductEventWither,\n} from '@helpers/googleTagManager';\n\nimport { useNewPdDesignEnabled } from '../../hooks/useNewPdDesignEnabled';\n\nimport { BULK_CALL_TIMEOUT } from './constants';\nimport { Wrapper } from './styled';\n\nexport interface IProductSectionContentProps {\n listLabel?: string;\n trackProductClick?: boolean;\n trackProductImpression?: boolean;\n data: GetProductsByIdBatchedQuery;\n itemsPerSlide: number;\n}\n\nexport const ProductsSectionContent: React.FC =\n React.memo(\n ({\n listLabel = '',\n trackProductClick = false,\n trackProductImpression = false,\n data,\n itemsPerSlide,\n }) => {\n const newDesign = useNewPdDesignEnabled();\n const trackVisibleItems = useTrackDynamicList({\n delay: BULK_CALL_TIMEOUT,\n listName: listLabel,\n });\n\n const onNewItemShow = (id: IProduct['id']) => {\n if (trackProductImpression && listLabel) {\n const index = findIndex(data.vpProductByIds, { id });\n if (index !== -1) {\n const product = data.vpProductByIds[index];\n trackVisibleItems(\n ProductEventWither()\n .withVpProduct(product)\n .withAdditionalData({\n list_position: index + 1,\n id: product.id,\n })\n .build()\n );\n }\n }\n };\n\n const getProductItemProps = useGetProductItemProps({\n noPriceIfUnavailable: true,\n useMasterUrl: true,\n inSectionOfType: 'slider',\n GTMEventLabel: listLabel,\n sendGTM: trackProductClick,\n wishlistActionLocation: 'list',\n });\n\n const products = data.vpProductByIds\n .map((product, index) =>\n getProductItemProps({\n product,\n position: index + 1,\n })\n )\n .filter(Boolean);\n\n return (\n \n {\n onNewItemShow(product.id);\n }}\n products={products}\n fallback={\n \n }\n />\n \n );\n }\n );\n","export const BULK_CALL_TIMEOUT = 500;\n","import styled, { css } from 'styled-components';\n\nimport { ProductSliderPlaceholder } from '@notino/react-styleguide';\n\nexport const StyledListPlaceholder = styled(ProductSliderPlaceholder)<{\n newDesign: boolean;\n}>`\n overflow-x: auto;\n\n ${({ newDesign }) =>\n newDesign &&\n css`\n margin: 1.5rem 0 4rem;\n `}\n`;\n","import * as React from 'react';\n\nimport useMedia from 'use-media';\n\nimport { theme } from '@notino/react-styleguide';\nimport { GetProductsByIdBatchedQuery } from '@notino/shared/definitions/types';\n\nimport { useNewPdDesignEnabled } from '../hooks/useNewPdDesignEnabled';\n\nimport { ProductsSectionContent } from './ProductSectionContent';\nimport { StyledListPlaceholder } from './styled';\n\ninterface IProductListContentParams {\n loading: boolean;\n listLabel: string;\n inView: boolean;\n data: GetProductsByIdBatchedQuery;\n trackProductClick: boolean;\n trackProductImpression: boolean;\n}\n\nexport const ProductListContent = ({\n loading,\n listLabel,\n inView,\n data,\n trackProductClick,\n trackProductImpression,\n}: IProductListContentParams) => {\n const newDesign = useNewPdDesignEnabled();\n const isBigScreen = useMedia(`(min-width: ${theme.breakpoints.xxl}rem)`);\n const itemsPerSlide = isBigScreen ? 5 : 4;\n\n if (inView && !loading) {\n return (\n \n );\n }\n return (\n \n );\n};\n","import * as React from 'react';\nimport { useInView } from 'react-intersection-observer';\n\nimport { IProduct } from '@containers/ProductListing/model';\n\nimport { dispatchProductsShownEvent } from '../hooks/useDispatchProductsShowEvent';\nimport { ISectionContainerProps, SectionContainer } from '../SectionContainer';\n\nimport { ProductListContent } from './ProductItemsContent';\nimport { useProductsByIdsBatched } from './useProductsByIdsBatched';\n\nexport interface IProductsSectionProps\n extends Omit {\n productIds: Array;\n productIdsLoading?: boolean;\n trackProductClick?: boolean;\n trackProductImpression?: boolean;\n}\n\nexport const ProductsSection: React.FC = React.memo(\n ({\n productIds,\n listLabel = '',\n containerId = '',\n titleId = '',\n productIdsLoading = false,\n titleMessage = null,\n withDivider = false,\n trackProductClick = false,\n trackProductImpression = false,\n }) => {\n const [ref, inView] = useInView({ triggerOnce: true });\n\n const { loading, data } = useProductsByIdsBatched(productIds, {\n skip: !inView || productIdsLoading,\n onCompleted: (response) => {\n dispatchProductsShownEvent(response?.vpProductByIds, {\n section: listLabel,\n });\n },\n });\n\n const noRecommendedProducts = !loading && !data?.vpProductByIds?.length;\n if (noRecommendedProducts && inView && !productIdsLoading) {\n return null;\n }\n\n return (\n \n \n \n );\n }\n);\n","import * as React from 'react';\n\nimport { GetProductViewQuery } from '@notino/shared/definitions/types';\n\nimport { IProduct } from '@containers/ProductListing/model';\n\nimport messages from '../../messages';\nimport { ProductsSection } from '../ProductsSection';\n\nimport { LIST_LABEL } from './constants';\nimport { useLastVisitedItems } from './useLastVisitedItems';\n\ninterface ILastVisitedProductsProps {\n productId: IProduct['id'];\n variants: GetProductViewQuery['productDetailByMasterId'][number]['variants'][number][];\n}\n\nexport const LastVisitedProducts: React.FC = (\n props\n) => {\n const items = useLastVisitedItems(props.productId);\n\n if (!items || !items.length || items.length < 3) {\n return null;\n }\n\n return (\n \n );\n};\n","import * as React from 'react';\n\nimport { IProduct } from '@containers/ProductListing/model';\n\nimport { loadProductsFromCookieOldestFirst } from '../hooks/useLastVisited/utils';\n\nconst NUMBER_OF_VISIBLE_LAST_VISITED_PRODUCTS = 5;\n\nexport const useLastVisitedItems = (productId: IProduct['id']) => {\n return React.useMemo>(() => {\n return loadProductsFromCookieOldestFirst()\n .filter((lastVisitedProductId) => lastVisitedProductId !== productId)\n .slice(-NUMBER_OF_VISIBLE_LAST_VISITED_PRODUCTS)\n .reverse();\n }, [productId]);\n};\n","export const LIST_LABEL = 'last_visited_products';\n","import { defineMessages } from 'react-intl';\nexport default defineMessages({\n inWishlist: {\n id: 'product.item.in.wishlist',\n defaultMessage: 'Máte v oblíbených',\n },\n addToWishlist: {\n id: 'product.item.add.to.wishlist',\n defaultMessage: 'Přidat do oblíbených',\n },\n});\n","import styled, { css } from 'styled-components';\n\nimport { IBasicStyledProps, keyframes } from '@notino/react-styleguide';\n\nconst adding = keyframes`\n\t0% {\n transform: scale(1);\n }\n 50% {\n transform: scale(1.5);\n }\n 100% {\n transform: scale(1);\n }\n`;\n\ninterface IStyledPulsatingHeart extends IBasicStyledProps {\n animate: boolean;\n newPdDesignEnabled: boolean;\n}\n\nconst complexMixin = css`\n animation: ${adding} 350ms ease forwards;\n`;\n\nexport const StyledPulsatingHeart = styled.div`\n ${({ newPdDesignEnabled }) =>\n newPdDesignEnabled\n ? css`\n display: flex;\n align-items: center;\n padding-right: 1.13rem;\n padding-left: 0.125rem;\n `\n : css`\n padding-right: 1rem;\n `}\n\n svg {\n ${(props: IStyledPulsatingHeart) => (props.animate ? complexMixin : '')};\n }\n`;\n","import * as React from 'react';\n\nimport { Colors, HeartIcon, HeartOIcon } from '@notino/react-styleguide';\n\nimport { useNewPdDesignEnabled } from '@containers/ProductDetailContainer/ProductDetail/hooks/useNewPdDesignEnabled';\n\nimport { StyledPulsatingHeart } from './styled';\n\ninterface IPulsatingHeartProps {\n wishlisted: boolean;\n loading: boolean;\n width?: string;\n height?: string;\n}\n\nexport const PulsatingHeart: React.FC = ({\n wishlisted,\n loading,\n}) => {\n const newPdDesignEnabled = useNewPdDesignEnabled();\n\n return (\n \n {wishlisted || loading ? (\n \n ) : (\n \n )}\n \n );\n};\n","import styled, { css } from 'styled-components';\n\nimport { theme } from '@notino/react-styleguide';\n\nexport const Wrapper = styled.div<{ newPdDesignEnabled: boolean }>`\n padding: 0 0 1rem 0;\n ${({ newPdDesignEnabled }) =>\n newPdDesignEnabled\n ? theme.typography.labelRegular400\n : css`\n font-size: 0.875rem;\n `}\n`;\n\nexport const StyledAddToWishlist = styled.a`\n display: flex;\n user-select: none;\n cursor: pointer;\n text-decoration: none !important;\n align-items: center;\n\n &:hover {\n text-decoration: underline !important;\n svg {\n color: ${(props) => props.theme.palette.primary} !important;\n }\n }\n`;\n","import { VariantFragmentFragment as IVariantFragment } from '@notino/shared/definitions/types';\n\nimport variantFragment from '@containers/ProductDetailContainer/queries/fragVariant.graphql';\n\nimport { ICallbackParameters } from '../model';\n\nexport const updateVariantCache = ({\n wishlisted,\n cache,\n productId,\n}: ICallbackParameters & { productId: string | number }) => {\n cache.updateFragment(\n {\n fragment: variantFragment,\n id: `Variant:${productId}`,\n },\n (prev) => ({\n ...prev,\n wishlisted,\n })\n );\n};\n","import * as React from 'react';\nimport { FormattedMessage } from 'react-intl';\n\nimport { useApolloClient } from '@apollo/client';\n\nimport { useNewPdDesignEnabled } from '@containers/ProductDetailContainer/ProductDetail/hooks/useNewPdDesignEnabled';\nimport { usePriceLogic } from '@containers/ProductDetailContainer/ProductDetail/hooks/usePriceLogic';\nimport {\n MIN_PRICE_PROMO_LABEL,\n RRP_PRICE_PROMO_LABEL,\n} from '@containers/ProductDetailContainer/ProductDetail/hooks/usePriceLogic/tracking';\nimport { useProductDetailContext } from '@containers/ProductDetailContainer/ProductDetail/ProductDetailContext';\n\nimport messages from '../messages';\nimport { PulsatingHeart } from '../PulsatingHeart';\nimport { useWishlistActions } from '../useWishlistActions';\n\nimport { Wrapper, StyledAddToWishlist } from './styled';\nimport { updateVariantCache } from './updateVariantCache';\n\ninterface IWishlistProductDetailIconProps {\n wishlisted: boolean;\n productId: string | number;\n}\n\nexport const WishlistProductDetailIcon: React.FC<\n IWishlistProductDetailIconProps\n> = ({ wishlisted, productId }) => {\n const newPdDesignEnabled = useNewPdDesignEnabled();\n const { currentVariant } = useProductDetailContext();\n const { minimalPriceShown, rrpShown } = usePriceLogic(currentVariant);\n const { cache } = useApolloClient();\n\n const updateCallback = React.useCallback(\n (_, addedToWishlist: boolean) =>\n updateVariantCache({ wishlisted: addedToWishlist, productId, cache }),\n [cache, productId]\n );\n\n const { handleWishlistClick, loading } = useWishlistActions({\n productId: productId.toString(),\n wishlisted: wishlisted,\n onCompleted: updateCallback,\n });\n\n return (\n \n \n handleWishlistClick('product_detail', [\n rrpShown && RRP_PRICE_PROMO_LABEL,\n minimalPriceShown && MIN_PRICE_PROMO_LABEL,\n ])\n }\n >\n \n \n \n \n );\n};\n","import * as React from 'react';\n\ninterface IKlarnaProps {\n price: number;\n priceDecimalPlaces: number;\n locale: string;\n}\n\nexport const Klarna: React.FC = ({\n price,\n locale,\n priceDecimalPlaces,\n}) => {\n // The amount of money in micro units ($120.00 = 12000), used for amount based credit promotions.\n const priceInMicroUnits = price * Math.pow(10, priceDecimalPlaces);\n const priceWithoutDecimal = Math.trunc(Math.round(priceInMicroUnits));\n\n React.useEffect(\n function refreshOnChange() {\n window.KlarnaOnsiteService = window.KlarnaOnsiteService || [];\n window.KlarnaOnsiteService.push({ eventName: 'refresh-placements' });\n },\n [price]\n );\n\n return (\n \n `,\n }}\n />\n );\n};\n","import { TrackingAttributes } from '@context/tracking/types';\nimport { dispatchTrackingEvent } from '@context/tracking/utils';\n\nexport const trackModalClose = ({\n timing,\n}: Omit
) => {\n dispatchTrackingEvent({\n event: 'element_click',\n element: {\n name: 'click_and_collect',\n interaction: 'click',\n action: 'close_click_and_collect',\n timing,\n mode: 'off',\n type: 'product_detail',\n promo_labels: undefined,\n },\n _clear: true,\n });\n};\n\nexport const trackModalOpen = ({\n timing,\n}: Omit) => {\n dispatchTrackingEvent({\n event: 'element_click',\n element: {\n name: 'click_and_collect',\n interaction: 'click',\n action: 'open_click_and_collect',\n timing,\n mode: 'on',\n type: 'product_detail',\n promo_labels: undefined,\n },\n _clear: true,\n });\n};\n","import { defineMessages } from 'react-intl';\n\nexport const messages = defineMessages({\n pickupNow: {\n id: 'pd.clickandcollect.pickup.now',\n defaultMessage: 'Vyzvednout ihned',\n },\n});\n","import{b as e}from\"./_rollupPluginBabelHelpers-fc5c8eb1.js\";import{styled as n}from\"./styled.js\";var t,r,o,i=n.span(t||(t=e([\"\\n position: absolute;\\n top: \",\";\\n left: 0;\\n height: 16px;\\n width: 16px;\\n border-radius: 100%;\\n background-color: \",\";\\n border: 1px solid\\n \",\";\\n box-sizing: border-box;\\n\\n &:after {\\n content: '';\\n position: absolute;\\n display: none;\\n top: 2px;\\n left: 2px;\\n width: 10px;\\n height: 10px;\\n border-radius: 100%;\\n background-color: \",\";\\n }\\n\"])),(function(e){var n=e.position;return n>0?n+\"px\":0}),(function(e){return e.disabled?e.theme.palette.neutralLighter:e.theme.palette.basicInverse}),(function(e){return e.disabled?e.theme.palette.neutralLight:e.isHovered?e.theme.palette.basic:e.theme.palette.neutralDark}),(function(e){return e.disabled?e.theme.palette.neutralLight:e.theme.palette.basic})),a=n.label(r||(r=e([\"\\n display: block;\\n position: relative;\\n padding-left: 32px;\\n cursor: \",\";\\n color: \",\";\\n user-select: none;\\n font-size: 14px;\\n font-weight: \",\";\\n\\n &:hover input:not(:disabled):not(:checked) ~ \",\" {\\n border: 1px solid \",\";\\n }\\n\"])),(function(e){return e.disabled?\"inherit\":\"pointer\"}),(function(e){var n=e.disabled,t=e.theme;return n?t.palette.neutral:t.palette.basic}),(function(e){var n=e.bold;return void 0!==n&&n?800:300}),i,(function(e){return e.theme.palette.basic})),l=n.input(o||(o=e([\"\\n position: absolute;\\n opacity: 0;\\n top: 0;\\n left: 0;\\n\\n &:checked {\\n ~ \",\" {\\n border-color: \",\";\\n\\n &:after {\\n display: block;\\n }\\n }\\n }\\n\"])),i,(function(e){return e.disabled?e.theme.palette.neutralLight:e.theme.palette.basic}));export{a as C,l as S,i as a};\n//# sourceMappingURL=styled-78109075.js.map\n","import*as e from\"react\";import{n as o}from\"./utils-ec9be121.js\";import{C as s,S as a,a as r}from\"./styled-78109075.js\";import\"./_rollupPluginBabelHelpers-fc5c8eb1.js\";import\"./styled.js\";import\"styled-components\";var t=function(t){var i=t.children,d=t.disabled,l=t.checked,n=t.isHovered,m=t.onChange,c=void 0===m?o:m,p=t.position,b=void 0===p?0:p,v=t.className,f=t.bold,h=t.name;return e.createElement(s,{disabled:d,className:v,bold:f},i,e.createElement(a,{disabled:d,type:\"radio\",checked:l,onChange:c,bold:f,name:h}),e.createElement(r,{disabled:d,position:b,isHovered:n}))};export{t as RadioButton};\n//# sourceMappingURL=RadioButton.js.map\n","import { styled, theme } from '@notino/react-styleguide';\n\nexport const StoreTitle = styled.p`\n ${theme.typography.labelRegular400}\n margin-bottom: 0.25rem;\n`;\n\nexport const StoreDescription = styled.span`\n font-weight: 300;\n ${theme.typography.labelRegular400}\n color:${theme.palette.neutralDark};\n padding-bottom: 0.25rem;\n`;\n\nexport const StoreStockAvailabilityWrapper = styled.div`\n color: ${theme.palette.success};\n ${theme.typography.labelRegular400}\n`;\n","/**\n * @return [tomorrow, date after tomorrow]\n */\nconst getNextTwoDays = (): [Date, Date] => {\n const now = new Date();\n const tomorrowTime = new Date(\n now.getFullYear(),\n now.getMonth(),\n now.getDate() + 1,\n 0,\n 0,\n 0\n );\n const dayAfterTomorrowTime = new Date(\n now.getFullYear(),\n now.getMonth(),\n now.getDate() + 2,\n 0,\n 0,\n 0\n );\n return [tomorrowTime, dayAfterTomorrowTime];\n};\n\nexport const isTomorrow = (date: Date) => {\n const timeToCompare = new Date(date).getTime();\n const [tomorrow, dayAfterTomorrow] = getNextTwoDays();\n\n return (\n timeToCompare >= tomorrow.getTime() &&\n timeToCompare < dayAfterTomorrow.getTime()\n );\n};\n\nexport const isToday = (date: Date) => {\n const timeToCompare = new Date(date).getTime();\n const startOfToday = new Date();\n startOfToday.setHours(0, 0, 0, 0);\n const [tomorrow] = getNextTwoDays();\n\n return (\n timeToCompare >= startOfToday.getTime() &&\n timeToCompare < tomorrow.getTime()\n );\n};\n","import { defineMessages } from 'react-intl';\n\nexport const messages = defineMessages({\n chooseStore: {\n id: 'pd.clickandcollect.choose.store',\n defaultMessage: 'Vyberte pobočku',\n },\n pickHere: {\n id: 'pd.clickandcollect.pick.here',\n defaultMessage: 'Vyzvednout zde',\n },\n availableFrom: {\n id: 'pd.clickandcollect.available.from',\n defaultMessage: '{date} od {time}',\n },\n today: {\n id: 'pd.clickandcollect.today',\n defaultMessage: 'dnes',\n },\n tomorrow: {\n id: 'pd.clickandcollect.tomorrow',\n defaultMessage: 'zítra',\n },\n lessThan5: {\n id: 'pd.clickandcollect.lessThan5',\n defaultMessage: '{count} ks',\n },\n moreThan5: {\n id: 'pd.clickandcollect.moreThan5',\n defaultMessage: 'Více než 5 ks',\n },\n moreThan20: {\n id: 'pd.clickandcollect.moreThan20',\n defaultMessage: 'Více než 20 ks',\n },\n});\n","import * as React from 'react';\n\nimport { IStore } from '../../model';\n\nimport { StoreStockAvailabilityWrapper } from './styled';\nimport { usePickUpTime } from './usePickupTime';\nimport { useStockCount } from './useStockCount';\n\nexport const StoreStockAvailability = ({\n stockCount,\n earliestPickup,\n}: IStore): JSX.Element => {\n const stockCountMessage = useStockCount(stockCount);\n const pickupTime = usePickUpTime(earliestPickup);\n return (\n \n {`${stockCountMessage} | ${pickupTime}`}\n \n );\n};\n","import { useIntl } from 'react-intl';\n\nimport { StockAvailability } from '@notino/shared/definitions/types';\n\nimport { messages } from '../../messages';\n\nexport const useStockCount = (stockCount: string): string => {\n const { formatMessage } = useIntl();\n switch (stockCount) {\n case StockAvailability.Last1:\n return formatMessage(messages.lessThan5, { count: 1 });\n case StockAvailability.Last2:\n return formatMessage(messages.lessThan5, { count: 2 });\n case StockAvailability.Last3:\n return formatMessage(messages.lessThan5, { count: 3 });\n case StockAvailability.Last4:\n return formatMessage(messages.lessThan5, { count: 4 });\n case StockAvailability.Last5:\n return formatMessage(messages.lessThan5, { count: 5 });\n case StockAvailability.MoreThan20:\n return formatMessage(messages.moreThan20, {});\n case StockAvailability.MoreThan5:\n return formatMessage(messages.moreThan5, {});\n }\n};\n","import { useIntl } from 'react-intl';\n\nimport { isToday, isTomorrow } from '../../../../utils/dateUtils';\nimport { messages } from '../../messages';\n\nimport { getEarliestPickupDate } from './utils';\n\nexport const usePickUpTime = (earliestPickup: string): string => {\n const { formatTime, formatDate, formatMessage } = useIntl();\n\n const earliestPickupDate = getEarliestPickupDate(new Date(earliestPickup));\n\n const time = formatTime(earliestPickupDate, {\n hour: 'numeric',\n minute: 'numeric',\n });\n\n let dateName: string;\n if (isToday(earliestPickupDate)) {\n dateName = formatMessage(messages.today);\n } else if (isTomorrow(earliestPickupDate)) {\n dateName = formatMessage(messages.tomorrow);\n } else {\n dateName = formatDate(earliestPickupDate, {\n weekday: 'long',\n });\n }\n\n return formatMessage(messages.availableFrom, {\n date: dateName,\n time,\n });\n};\n","/**\n * in case that API returns time in the past, change it to now\n * @param earliestPickup\n */\nexport const getEarliestPickupDate = (earliestPickup: Date) => {\n const now = new Date();\n return earliestPickup > now ? earliestPickup : now;\n};\n","import * as React from 'react';\n\nimport { IStore } from '../../model';\n\nimport { StoreStockAvailability } from './StoreStockAvailability';\nimport { StoreDescription, StoreTitle } from './styled';\n\ninterface IStoreProps {\n data: IStore;\n}\n\nexport const Store: React.FC = ({ data }) => {\n return (\n <>\n {data.name}\n \n \n >\n );\n};\n","import styled from 'styled-components';\n\nimport {\n ActionButtonWithConfirmation,\n breakpoints,\n theme,\n} from '@notino/react-styleguide';\n\nexport const StoreWrapper = styled.div`\n padding: 1.25rem 0;\n :not(:last-child) {\n border-bottom: solid 1px ${theme.palette.neutralAlt};\n }\n :first-child {\n padding-top: 0;\n }\n`;\nexport const ModalContentWrapper = styled.div`\n max-height: 50vh;\n overflow-y: auto;\n margin-bottom: 1rem;\n`;\n\nexport const CenterWrapper = styled.div`\n padding-top: 2.8125rem;\n display: flex;\n justify-content: center;\n`;\n\nexport const StyledActionButtonWithConfirmation = styled(\n ActionButtonWithConfirmation\n)`\n @media (min-width: ${breakpoints.sm}) {\n width: auto;\n margin: auto;\n display: block;\n }\n`;\n","export const redirectToClickAndCollectCart = (\n cartId: string,\n isNewEndpoint: boolean\n) => {\n window.location.href = isNewEndpoint\n ? `/reservation/${cartId}`\n : `/reservation-1/${cartId}`;\n};\n","import * as React from 'react';\nimport { FormattedMessage } from 'react-intl';\n\nimport { snakeCase } from 'lodash';\n\nimport {\n ButtonModel,\n Colors,\n RadioButton,\n Spinner,\n} from '@notino/react-styleguide';\nimport {\n GetProductViewQuery,\n StockAvailability,\n} from '@notino/shared/definitions/types';\nimport { CartType } from '@notino/web-tracking';\n\nimport { useModifaceVariants } from '@containers/ProductDetailContainer/ProductDetail/hooks/useModifaceVariants';\nimport { usePriceLogic } from '@containers/ProductDetailContainer/ProductDetail/hooks/usePriceLogic';\nimport {\n MIN_PRICE_PROMO_LABEL,\n RRP_PRICE_PROMO_LABEL,\n} from '@containers/ProductDetailContainer/ProductDetail/hooks/usePriceLogic/tracking';\nimport { useProductDetailContext } from '@containers/ProductDetailContainer/ProductDetail/ProductDetailContext';\nimport { dispatchTrackingEvent } from '@context/tracking/utils';\nimport { ProductEventWither } from '@helpers/googleTagManager';\n\nimport { Store } from './components/Store';\nimport { useTrackModalShow } from './hooks/useTrackModalShow';\nimport { messages } from './messages';\nimport {\n ModalContentWrapper,\n StoreWrapper,\n CenterWrapper,\n StyledActionButtonWithConfirmation,\n} from './styled';\nimport { useClickAndCollect } from './useClickAndCollect';\n\ninterface IClickAndCollectModalProps {\n product: GetProductViewQuery['productDetailByMasterId'][number];\n selectedVariant: GetProductViewQuery['productDetailByMasterId'][number]['variants'][number];\n}\n\nexport const ClickAndCollectModal = ({\n selectedVariant,\n product,\n}: IClickAndCollectModalProps) => {\n const [selectedStore, setSelectedStore] = React.useState('');\n const { rrpShown, minimalPriceShown } = usePriceLogic(selectedVariant);\n\n const {\n tryItFirstAvailable,\n engravingAvailable,\n product: { variants },\n } = useProductDetailContext();\n const modifaceVariants = useModifaceVariants(variants);\n\n const {\n stores,\n storesLoading,\n handleAddToClickAndCollect,\n addToClickAndCollectData: {\n loading: addToCnCLoading,\n error: addToCnCError,\n },\n } = useClickAndCollect(Number(selectedVariant.id));\n\n useTrackModalShow();\n\n if (storesLoading) {\n return (\n \n \n \n );\n }\n\n const onRadioButtonClick = (storeId: string) => () => {\n setSelectedStore(storeId);\n };\n\n const handleSubmit = async () => {\n dispatchTrackingEvent({\n event: 'add_to_cart',\n product: ProductEventWither()\n .withProduct(product)\n .withVariant(selectedVariant)\n .withServices({\n modifaceVariants,\n tryItFirstAvailable,\n engravingAvailable,\n shadefinderAvailable: product.isShadeFinderAvailable,\n })\n .withAdditionalData({\n quantity: 1,\n cart_type: snakeCase(CartType.clickAndCollect),\n })\n .withAdditionalPromoLabels([\n rrpShown && RRP_PRICE_PROMO_LABEL,\n minimalPriceShown && MIN_PRICE_PROMO_LABEL,\n ])\n .build(),\n _clear: true,\n });\n\n await handleAddToClickAndCollect(selectedStore);\n };\n\n return (\n <>\n \n {stores?.collectStoresByProductId?.map((store) => {\n const outOfStock = store.stockCount === StockAvailability.OutOfStock;\n if (outOfStock) {\n return null;\n }\n\n return (\n \n \n \n \n \n );\n })}\n \n\n \n \n \n >\n );\n};\n","import * as React from 'react';\n\nimport { GetProductViewQuery } from '@notino/shared/definitions/types';\n\nimport { getModifaceVariants, getModifaceHairVariants } from '../utils';\n\nexport const useModifaceVariants = (\n variants: GetProductViewQuery['productDetailByMasterId'][number]['variants']\n) => {\n return React.useMemo(() => getModifaceVariants(variants), [variants]);\n};\n\nexport const useModiface = (\n variants: GetProductViewQuery['productDetailByMasterId'][number]['variants']\n) => {\n return React.useMemo(\n () => ({\n makeup: getModifaceVariants(variants),\n hair: getModifaceHairVariants(variants),\n }),\n [variants]\n );\n};\n","import * as React from 'react';\n\nimport { useMutation, useQuery } from '@apollo/client';\n\nimport {\n AddToClickAndCollectMutation,\n AddToClickAndCollectMutationVariables,\n GetStoresQuery,\n} from '@notino/shared/definitions/types';\n\nimport { useSettings } from '@containers/ProductDetailContainer/hooks/useSettings';\n\nimport addToClickAndCollectMutation from './mutations/addToClickAndCollect.graphql';\nimport getStoresQuery from './queries/stores.graphql';\nimport { redirectToClickAndCollectCart } from './utils';\n\nexport const useClickAndCollect = (variantId: number) => {\n const { Settings: { isNewShoppingCartEndpointEnabled: isNewEndpoint } = {} } =\n useSettings();\n\n const { data: stores, loading: storesLoading } = useQuery(\n getStoresQuery,\n {\n variables: { productId: variantId },\n }\n );\n\n const [addToClickAndCollectCart, addToClickAndCollectData] = useMutation<\n AddToClickAndCollectMutation,\n AddToClickAndCollectMutationVariables\n >(addToClickAndCollectMutation, {\n onCompleted: (response) => {\n redirectToClickAndCollectCart(\n response.addProductToClickAndCollect.cartId,\n isNewEndpoint\n );\n },\n });\n\n const handleAddToClickAndCollect = React.useCallback(\n async (warehouseCode: string) => {\n await addToClickAndCollectCart({\n variables: {\n productId: variantId,\n warehouseCode: warehouseCode,\n },\n });\n },\n [addToClickAndCollectCart, variantId]\n );\n\n return React.useMemo(\n () => ({\n stores,\n storesLoading,\n handleAddToClickAndCollect,\n addToClickAndCollectData,\n }),\n [\n stores,\n storesLoading,\n handleAddToClickAndCollect,\n addToClickAndCollectData,\n ]\n );\n};\n","import * as React from 'react';\n\nimport { useTrackingContext } from '@context/tracking/TrackingContext';\nimport { dispatchTrackingEvent } from '@context/tracking/utils';\n\nexport const useTrackModalShow = () => {\n const { getTimeFromInit } = useTrackingContext();\n\n React.useEffect(() => {\n dispatchTrackingEvent({\n event: 'subpage_view',\n subpage: {\n name: 'click_and_collect_modal',\n interaction: 'automatic',\n timing: getTimeFromInit(),\n action: 'click_and_collect_opened',\n type: 'click_and_collect',\n },\n _clear: true,\n });\n }, [getTimeFromInit]);\n};\n","import styled, { css } from 'styled-components';\n\nimport { breakpoints, Button, theme } from '@notino/react-styleguide';\n\nexport const PickUpButtonWrapper = styled.div`\n margin-top: 0.5rem;\n flex: 0 1 100%;\n @media (min-width: ${breakpoints.sm}) {\n margin-top: 0;\n flex: auto;\n }\n`;\n\nexport const PickUpButton = styled(Button)<{ newDesign: boolean }>`\n width: 100%;\n ${({ newDesign }) =>\n newDesign\n ? css`\n height: 3.25rem;\n ${theme.typography.labelRegular}\n `\n : css`\n @media (min-width: ${breakpoints.sm}) {\n width: auto;\n }\n `}\n`;\n","import * as React from 'react';\nimport { FormattedMessage, useIntl } from 'react-intl';\n\nimport { ButtonModel, ModalContext, Text } from '@notino/react-styleguide';\nimport { GetProductViewQuery } from '@notino/shared/definitions/types';\n\nimport { useTrackingContext } from '@context/tracking/TrackingContext';\n\nimport { useNewPdDesignEnabled } from '../../hooks/useNewPdDesignEnabled';\n\nimport { trackModalClose, trackModalOpen } from './gtm/tracking';\nimport { messages } from './messages';\nimport { ClickAndCollectModal } from './Modal';\nimport { messages as modalMessages } from './Modal/messages';\nimport { PickUpButton, PickUpButtonWrapper } from './styled';\n\ntype IClickAndCollectProps = {\n selectedVariant: GetProductViewQuery['productDetailByMasterId'][number]['variants'][number];\n product: GetProductViewQuery['productDetailByMasterId'][number];\n onModalClosing: () => void;\n disabled?: boolean;\n};\n\nexport const ClickAndCollect: React.FC = ({\n selectedVariant,\n product,\n onModalClosing,\n disabled = false,\n}) => {\n const newDesign = useNewPdDesignEnabled();\n const { formatMessage } = useIntl();\n const { getTimeFromInit } = useTrackingContext();\n const { toggleModal, hideModal } = ModalContext.useModalContext();\n\n const toggleClickAndCollectModal = React.useCallback(() => {\n trackModalOpen({ timing: getTimeFromInit() });\n\n const handleModalCloseTracking = () => {\n hideModal();\n trackModalClose({ timing: getTimeFromInit() });\n onModalClosing();\n };\n\n const header = formatMessage(modalMessages.chooseStore);\n\n toggleModal(\n ,\n {\n header: {header},\n center: true,\n positionBottomOnMobile: true,\n withFocusTrap: true,\n onClose: handleModalCloseTracking,\n }\n );\n }, [\n getTimeFromInit,\n toggleModal,\n selectedVariant,\n product,\n formatMessage,\n hideModal,\n onModalClosing,\n ]);\n\n return (\n \n \n \n \n \n );\n};\n","export const REGISTER_URL = '/myaccount.asp#registration';\nexport const MY_NOTINO_BENEFIT_PAGE = '/mynotino/my-benefits/';\n\nexport const STANDARD_MODAL_IMAGE_PATH =\n 'https://cdn.notinoimg.com/images/gallery/loyalty/new-vip-modal.jpg';\nexport const RETINA_MODAL_IMAGE_PATH =\n 'https://cdn.notinoimg.com/images/gallery/loyalty/new-vip-modal-retina.jpg';\nexport const MOBILE_MODAL_IMAGE_PATH =\n 'https://cdn.notinoimg.com/images/gallery/loyalty/mobile-vip-modal.jpg';\n","import { trackPageView } from '@notino/web-tracking';\n\nimport { dispatchTrackingEvent } from '@context/tracking/utils';\n\nimport { FullfilledSteps } from './components/HowToLoyaltyDiscountModal';\n\nexport enum LoyaltyStatus {\n nonRegistered = 'non-registered',\n registered = 'registered',\n subscribed = 'subscribed',\n activated = 'activated',\n unsubscribed = 'unsubscribed',\n}\n\nexport const determineStatus = (fullfilledSteps: FullfilledSteps) => {\n let status = LoyaltyStatus.nonRegistered;\n\n if (fullfilledSteps.registerStep) {\n status = LoyaltyStatus.registered;\n }\n\n if (fullfilledSteps.registerStep && fullfilledSteps.newsletterStep) {\n status = LoyaltyStatus.subscribed;\n }\n\n if (\n fullfilledSteps.registerStep &&\n fullfilledSteps.chooseBrandAndCategoryStep &&\n !fullfilledSteps.newsletterStep\n ) {\n status = LoyaltyStatus.unsubscribed;\n }\n\n return status;\n};\n\nexport const trackLoyaltyModalOpen = (\n status: LoyaltyStatus,\n timing: number\n) => {\n dispatchTrackingEvent({\n event: 'subpage_view',\n _clear: true,\n subpage: {\n name: `/overlay_vip-discount/${status}/`,\n action: 'open_vip_overlay',\n interaction: 'click',\n type: 'service',\n title: 'VIP program',\n timing,\n },\n });\n};\n\nexport const trackLoyaltyModalClose = () =>\n dispatchTrackingEvent({\n event: 'subpage_close',\n subpage: undefined,\n _clear: true,\n });\n\nexport const trackLoyaltyModalDeprecated = (\n status: LoyaltyStatus,\n path?: string\n) => {\n trackPageView({\n type: 'Service',\n path: path ?? `/overlay_vip-discount/${status}/`,\n description: `/overlay_vip-discount/${status}/`,\n title: 'VIP program',\n params: {\n loyaltyStatus: status,\n },\n });\n};\n","import{_ as e}from\"./_rollupPluginBabelHelpers-fc5c8eb1.js\";import*as l from\"react\";import{Icon as m}from\"./index.js\";import\"./ThemeContext.js\";import\"styled-components\";var t=function(t){return l.createElement(m,e({},t,{viewBox:\"0 0 256.62 256\"}),l.createElement(\"path\",{fill:\"#6e6e6e\",d:\"M201.7,52.25H67.58L13.77,111,134.64,256,255.51,111ZM137.18,113.1H172l-20.43,72.12Zm-19.51,72.12L97.24,113.1H132.1ZM97,108.94,102.77,76l27.42,32.91Zm42.12,0L166.51,76l5.8,32.91Zm37.45,0L171,77.37l29.71,31.57Zm-41.9-1.18L104.58,71.69l30.06-15,30.06,15Zm-41.9,1.18H68.6L98.31,77.37Zm.17,4.16,19.15,67.61L67.64,113.1Zm41.73,8.54L149.12,194,134.64,245.1,120.17,194Zm41.73-8.54h25.27l-44.42,67.61Zm28-6.33L170.7,71l11.73-14.07L212.55,72Zm11.13-29.12,12.89,31.29H208.13ZM167.44,68.4l-24-12h34Zm-65.6,0-10-12h34ZM98.58,71,64.92,106.77,56.73,72,86.85,56.93ZM61.15,108.94H40.9L53.79,77.65Zm1,4.16L82.8,187.28l-5.48-6.57-36-67.61Zm7.16,10.19L115.84,194,130,243.88,89.29,195.07ZM153.44,194l46.49-70.75L180,195.07l-40.68,48.81Zm53.65-80.94H228l-36,67.61-5.48,6.57Zm25.65,0H248.3L205.07,165Zm.14-4.16L220.54,79l27.47,30ZM207.61,64.86l-16.9-8.45h9.15ZM69.42,56.41h9.15l-16.9,8.45ZM48.74,79l-12.34,30H21.27ZM36.54,113.1,64.21,165,21,113.1Z\"}),l.createElement(\"polygon\",{fill:\"#dc0069\",points:\"42.52 56.69 48.19 34.01 70.86 28.34 48.19 22.68 42.52 0 36.85 22.68 14.17 28.34 36.85 34.01 42.52 56.69\"}),l.createElement(\"polygon\",{fill:\"#6e6e6e\",points:\"18.9 87.4 22.68 72.28 37.79 68.5 22.68 64.72 18.9 49.6 15.12 64.72 0 68.5 15.12 72.28 18.9 87.4\"}),l.createElement(\"polygon\",{fill:\"#dc0069\",points:\"226.76 56.69 229.6 45.35 240.94 42.52 229.6 39.68 226.76 28.34 223.93 39.68 212.59 42.52 223.93 45.35 226.76 56.69\"}),l.createElement(\"polygon\",{fill:\"#6e6e6e\",points:\"108.66 44.88 111.49 33.54 122.83 30.71 111.49 27.87 108.66 16.54 105.82 27.87 94.48 30.71 105.82 33.54 108.66 44.88\"}))};export{t as DiamondBigIcon};\n//# sourceMappingURL=DiamondBigIcon.js.map\n","import { defineMessages } from 'react-intl';\n\nexport const messages = defineMessages({\n imgAlt: {\n id: 'pd.loyalty.modal.img.alt',\n defaultMessage: 'zákaznická sleva',\n },\n});\n","import styled, { css } from 'styled-components';\n\nimport {\n Heading,\n breakpoints,\n ImagePlaceholder,\n theme,\n} from '@notino/react-styleguide';\n\nexport const ModalHeader = styled(Heading.H3)`\n text-align: left;\n margin: 0 auto;\n ${theme.typography.titleLarge}\n`;\n\nexport const DiamondIconWrapper = styled.div`\n display: flex;\n flex-basis: 20%;\n height: 100%;\n`;\n\nexport const ModalContent = styled.div`\n display: flex;\n flex-direction: column;\n text-align: center;\n max-height: 90vh;\n\n @media (min-width: ${breakpoints.md}) {\n flex-direction: row-reverse;\n height: auto;\n }\n\n flex-direction: column-reverse;\n max-height: 80vh;\n overflow-y: auto;\n`;\n\nexport const ContentWrapper = styled.div`\n width: 100%;\n padding: 2rem 0;\n\n @media (min-width: ${breakpoints.md}) {\n width: 50%;\n }\n`;\n\nexport const TextContentWrapper = styled.div`\n display: flex;\n padding: 1rem;\n\n @media (min-width: ${breakpoints.md}) {\n padding: 2rem;\n }\n\n padding: 0 1.25rem 1.5rem;\n flex-direction: column;\n gap: 1rem;\n\n @media (min-width: ${breakpoints.md}) {\n padding: 0 2rem 1.5rem;\n }\n`;\n\nexport const InfoWrapper = styled.div`\n flex-basis: 75%;\n\n @media (min-width: ${breakpoints.md}) {\n flex-basis: 80%;\n }\n`;\n\nexport const ImageWrapper = styled.div<{ hideOnMobile: boolean }>`\n display: flex;\n justify-content: center;\n align-items: flex-start;\n overflow: hidden;\n\n ${({ hideOnMobile }) =>\n hideOnMobile &&\n css`\n display: none;\n `}\n\n @media (min-width: ${breakpoints.md}) {\n display: flex;\n align-items: center;\n }\n`;\n\nexport const StyledImagePlaceholder = styled(ImagePlaceholder)`\n min-width: 100%;\n min-height: 100%;\n`;\n","import * as React from 'react';\nimport { useIntl } from 'react-intl';\n\nimport { DiamondBigIcon } from '@notino/react-styleguide';\n\nimport { useIsDesktop } from '@utils/helpers';\n\nimport {\n STANDARD_MODAL_IMAGE_PATH,\n RETINA_MODAL_IMAGE_PATH,\n MOBILE_MODAL_IMAGE_PATH,\n} from '../../constants';\n\nimport { messages } from './messages';\nimport {\n ModalHeader,\n ModalContent,\n TextContentWrapper,\n DiamondIconWrapper,\n InfoWrapper,\n ContentWrapper,\n ImageWrapper,\n StyledImagePlaceholder,\n} from './styled';\n\ninterface IBaseLoyaltyModalProps {\n headerMessage: string;\n children: JSX.Element;\n footerContent: JSX.Element;\n hideImageOnMobile?: boolean;\n}\n\nexport const BaseLoyaltyModal: React.FC = ({\n headerMessage,\n children,\n footerContent,\n hideImageOnMobile,\n}) => {\n const { formatMessage } = useIntl();\n const isDesktop = useIsDesktop();\n\n return (\n \n \n \n \n \n \n \n {headerMessage}\n {children}\n \n \n {footerContent}\n \n \n \n \n \n );\n};\n","import styled from 'styled-components';\n\nimport { Button } from '@notino/react-styleguide';\n\nexport const StyledButton = styled(Button)`\n padding: 0 3rem;\n`;\n","import { defineMessages, MessageDescriptor } from 'react-intl';\n\nexport const benefitsModalTableMessages: {\n [key: string]: MessageDescriptor;\n} = defineMessages({\n notChosen: {\n id: 'pd.loyalty.benefits.modal.table.not.chosen',\n defaultMessage: 'nevybráno',\n },\n ok: {\n id: 'pd.loyalty.benefits.modal.ok',\n defaultMessage: 'Ok',\n },\n settings: {\n id: 'pd.loyalty.benefits.modal.settings',\n defaultMessage: 'Nastavení',\n },\n});\n","import styled from 'styled-components';\n\nimport { theme } from '@notino/react-styleguide';\n\nexport const BenefitsModalTableWrapper = styled.div``;\n\ninterface IBenefitsModalTableLineProps {\n isOpaque?: boolean;\n}\n\nexport const BenefitsModalTableLine = styled.div`\n display: flex;\n justify-content: space-between;\n padding: 0.5rem 0 0.5rem 0.5rem;\n border-bottom: 1px solid ${theme.palette.neutralLight};\n opacity: ${({ isOpaque }) => (isOpaque ? '0.5' : '1')};\n`;\n\nexport const BrandName = styled.span`\n text-align: left;\n text-overflow: ellipsis;\n white-space: nowrap;\n overflow: hidden;\n width: 75%;\n`;\n","import * as React from 'react';\nimport { FormattedMessage } from 'react-intl';\n\nimport { GetCustomerBenefitsQuery } from '@notino/shared/definitions/types';\n\nimport { benefitsModalTableMessages } from './messages';\nimport {\n BenefitsModalTableLine,\n BenefitsModalTableWrapper,\n BrandName,\n} from './styled';\n\ninterface IBenefitsModalTableProps {\n customerBenefits: GetCustomerBenefitsQuery['customerBenefits'];\n}\n\nexport const BenefitsModalTable: React.FC = ({\n customerBenefits,\n}) => {\n const availableDiscounts = new Array(\n customerBenefits.brandDiscountLimit +\n customerBenefits.categoryDiscountLimit -\n (customerBenefits.brandDiscounts.length +\n customerBenefits.categoryDiscounts.length)\n ).fill(0);\n\n return (\n \n {customerBenefits.brandDiscounts.map((brandDiscount) => (\n \n \n {brandDiscount.subjectName}\n \n -{brandDiscount.value}%\n \n ))}\n {customerBenefits.categoryDiscounts.map((brandDiscount) => (\n \n \n {brandDiscount.subjectName}\n \n -{brandDiscount.value}%\n \n ))}\n {availableDiscounts.map((_, i) => (\n // eslint-disable-next-line react/no-array-index-key\n \n \n \n \n 0%\n \n ))}\n \n );\n};\n","import { defineMessages } from 'react-intl';\n\nexport const benefitsModalMessages = defineMessages({\n header: {\n id: 'pd.benefits.modal.header',\n defaultMessage: 'Vaše oblíbené značky a kategorie se slevou',\n },\n});\n","import styled from 'styled-components';\n\nimport { breakpoints, theme } from '@notino/react-styleguide';\n\nexport const BenefitsModalContentWrapper = styled.div`\n margin-top: 1rem;\n @media (min-width: ${breakpoints.sm}) {\n width: 75%;\n }\n`;\n\nexport const ButtonWrapper = styled.div`\n margin: 1rem 0 0.5rem 0;\n display: flex;\n justify-content: center;\n`;\n\nexport const SettingsWrapper = styled.a`\n margin-top: 0.5rem;\n text-decoration: underline;\n text-align: center;\n cursor: pointer;\n color: ${theme.palette.basic};\n &:hover {\n text-decoration: none;\n }\n`;\n\nexport const FooterContent = styled.div`\n display: flex;\n flex-direction: column;\n padding: 0 1.25rem;\n button {\n width: 100%;\n }\n\n @media (min-width: ${breakpoints.md}) {\n padding: 0 2rem;\n align-items: flex-start;\n }\n`;\n","import * as React from 'react';\nimport { FormattedMessage, useIntl } from 'react-intl';\n\nimport { useQuery } from '@apollo/client';\n\nimport { ButtonModel, ModalContext } from '@notino/react-styleguide';\nimport { GetCustomerBenefitsQuery } from '@notino/shared/definitions/types';\nimport { trackEvent } from '@notino/web-tracking';\n\nimport { useFeatureFlags } from '@context/launch-darkly/LDProvider';\nimport { dispatchTrackingEvent } from '@context/tracking/utils';\n\nimport { MY_NOTINO_BENEFIT_PAGE } from '../../constants';\nimport getCustomerBenefitsQuery from '../../queries/getCustomerBenefits.graphql';\nimport {\n LoyaltyStatus,\n trackLoyaltyModalClose,\n trackLoyaltyModalDeprecated,\n trackLoyaltyModalOpen,\n} from '../../trackLoyaltyModal';\nimport { BaseLoyaltyModal } from '../BaseLoyaltyModal';\nimport { StyledButton } from '../styled';\n\nimport { BenefitsModalTable } from './BenefitsModalTable';\nimport { benefitsModalTableMessages } from './BenefitsModalTable/messages';\nimport { benefitsModalMessages } from './messages';\nimport {\n BenefitsModalContentWrapper,\n ButtonWrapper,\n FooterContent,\n SettingsWrapper,\n} from './styled';\n\nexport const BenefitsModal: React.FC<{ timing: number }> = ({ timing }) => {\n const { formatMessage } = useIntl();\n const { loyaltyGa4 } = useFeatureFlags();\n const { hideModal } = ModalContext.useModalContext();\n\n const { data } = useQuery(getCustomerBenefitsQuery);\n\n React.useEffect(() => {\n if (loyaltyGa4) {\n trackLoyaltyModalOpen(LoyaltyStatus.activated, timing);\n }\n }, [timing, loyaltyGa4]);\n\n React.useEffect(() => {\n if (!loyaltyGa4) {\n trackLoyaltyModalDeprecated(\n LoyaltyStatus.activated,\n '/overlay_vip-discount/confirmation/'\n );\n }\n }, [loyaltyGa4]);\n\n const handleSettingsClick = () => {\n if (loyaltyGa4) {\n dispatchTrackingEvent({\n event: 'vip_overlay_cta',\n vip: {\n action: 'click_on_overlay',\n name: 'change_settings',\n },\n });\n } else {\n trackEvent({\n eventCategory: 'Service',\n eventAction: 'LoyaltyOverlaySettings',\n eventLabel: undefined,\n eventValue: undefined,\n eventNonInteraction: undefined,\n });\n }\n };\n\n const handleOkClick = () => {\n if (loyaltyGa4) {\n trackLoyaltyModalClose();\n }\n hideModal();\n };\n\n return (\n \n \n \n \n \n \n \n \n \n \n }\n >\n \n {data?.customerBenefits && (\n \n \n \n )}\n
\n \n );\n};\n","import { defineMessages } from 'react-intl';\n\nexport const messages = defineMessages({\n howToGetLoyaltyDiscountHeader: {\n id: 'pd.loyalty.discount.how.to.header',\n defaultMessage: 'Kupujte oblíbené značky a kategorie se slevou',\n },\n registerStep: {\n id: 'pd.loyalty.discount.how.to.register',\n defaultMessage: 'Registrujte se',\n },\n newsletterStep: {\n id: 'pd.loyalty.discount.how.to.newsletter',\n defaultMessage: 'Odebírejte náš newsletter',\n },\n chooseBrandAndCategoryStep: {\n id: 'pd.loyalty.discount.how.to.choose.brand.category',\n defaultMessage: 'Vyberte si 3 značky a kategorii',\n },\n alreadyHaveAccount: {\n id: 'pd.loyalty.discount.how.to.already.have.account',\n defaultMessage: 'Již máte účet?',\n },\n logIn: {\n id: 'pd.loyalty.discount.how.to.log.in',\n defaultMessage: 'Přihlásit se',\n },\n registerNow: {\n id: 'pd.loyalty.discount.how.to.register.now',\n defaultMessage: 'Registrovat se',\n },\n setUpNow: {\n id: 'pd.loyalty.discount.how.to.set.up.now',\n defaultMessage: 'Nastavit',\n },\n});\n","import styled from 'styled-components';\n\nimport { breakpoints, theme } from '@notino/react-styleguide';\n\nexport const LoginWrapper = styled.div`\n text-align: center;\n margin-top: 1rem;\n color: ${theme.palette.neutralDarker};\n`;\n\nexport const LoginLink = styled.a`\n cursor: pointer;\n text-decoration: underline;\n color: ${theme.palette.neutralDarker};\n margin-left: 0.5rem;\n\n &:hover {\n text-decoration: none;\n }\n\n color: ${theme.palette.basic};\n font-weight: 500;\n`;\n\nexport const StepProgressCircle = styled.div<{\n isFulfilled: boolean;\n}>`\n display: flex;\n align-items: center;\n justify-content: center;\n width: 1.125rem;\n height: 1.125rem;\n background: ${(props) =>\n props.isFulfilled ? theme.palette.basic : theme.palette.neutralLight};\n border-radius: 50%;\n margin-right: 1.125rem;\n width: 1.25rem;\n height: 1.25rem;\n`;\n\nexport const StepWrapper = styled.span`\n display: flex;\n width: 100%;\n margin: 1rem auto;\n text-align: left;\n\n :first-child {\n margin: 0.75rem 0 0.5rem;\n }\n :last-child {\n margin: 0;\n }\n margin: 0 0 0.5rem;\n`;\n\nexport const FooterContent = styled.div`\n display: flex;\n flex-direction: column;\n padding: 0 1.25rem;\n @media (min-width: ${breakpoints.md}) {\n padding: 0 2rem;\n align-items: flex-start;\n }\n`;\n","import { trackEvent } from '@notino/web-tracking';\n\nimport { dispatchTrackingEvent } from '@context/tracking/utils';\n\nexport const trackLoyaltyCTA = (\n eventName: 'register' | 'log_in' | 'change_settings'\n) => {\n dispatchTrackingEvent({\n event: 'vip_overlay_cta',\n vip: {\n action: 'click_on_overlay',\n name: eventName,\n },\n });\n};\n\nexport const trackLoyaltyCTADeprecated = (eventLabel: string) => {\n trackEvent({\n eventCategory: 'Service',\n eventAction: 'LoyaltyCTA',\n eventLabel,\n eventValue: undefined,\n eventNonInteraction: undefined,\n });\n};\n","import * as React from 'react';\nimport { FormattedMessage, useIntl } from 'react-intl';\n\nimport { useQuery } from '@apollo/client';\n\nimport {\n ButtonModel,\n CheckIcon,\n CloseIcon,\n Colors,\n} from '@notino/react-styleguide';\nimport {\n Roles,\n GetCustomerBenefitsQuery,\n GetUserLoyaltyProgressQuery,\n} from '@notino/shared/definitions/types';\n\nimport { LOGIN_URL } from '@constants';\nimport { useFeatureFlags } from '@context/launch-darkly/LDProvider';\n\nimport { MY_NOTINO_BENEFIT_PAGE, REGISTER_URL } from '../../constants';\nimport getCustomerBenefitsQuery from '../../queries/getCustomerBenefits.graphql';\nimport {\n determineStatus,\n trackLoyaltyModalDeprecated,\n trackLoyaltyModalOpen,\n} from '../../trackLoyaltyModal';\nimport { BaseLoyaltyModal } from '../BaseLoyaltyModal';\nimport { StyledButton } from '../styled';\n\nimport { messages } from './messages';\nimport getUserLoyaltyProgressQuery from './queries/getUserLoyaltyProgress.graphql';\nimport {\n LoginWrapper,\n LoginLink,\n StepProgressCircle,\n StepWrapper,\n FooterContent,\n} from './styled';\nimport { trackLoyaltyCTA, trackLoyaltyCTADeprecated } from './trackLoyaltyCTA';\n\nenum HowToGetDiscountSteps {\n Register = 'registerStep',\n Newsletter = 'newsletterStep',\n ChooseBrandAndCategory = 'chooseBrandAndCategoryStep',\n}\n\nexport type FullfilledSteps = Record;\n\nexport const HowToLoyaltyModal: React.FC<{ timing: number }> = ({ timing }) => {\n const { formatMessage } = useIntl();\n const { loyaltyGa4 } = useFeatureFlags();\n const { data: getUser, loading: getUserLoading } =\n useQuery(getUserLoyaltyProgressQuery);\n\n const { data: checkCustomerBenefitsResult } =\n useQuery(getCustomerBenefitsQuery);\n\n const brandDiscountsSelected =\n checkCustomerBenefitsResult?.customerBenefits?.brandDiscounts?.length > 0;\n const categoryDiscountsSelected =\n checkCustomerBenefitsResult?.customerBenefits?.categoryDiscounts?.length >\n 0;\n\n const fulfilledSteps = React.useMemo(\n () => ({\n [HowToGetDiscountSteps.Register]:\n getUser && getUser.user ? getUser.user.role !== Roles.Anonymous : false,\n [HowToGetDiscountSteps.Newsletter]: getUser?.isUserSubscribed ?? false,\n [HowToGetDiscountSteps.ChooseBrandAndCategory]: Boolean(\n brandDiscountsSelected || categoryDiscountsSelected\n ),\n }),\n [brandDiscountsSelected, categoryDiscountsSelected, getUser]\n );\n\n const onButtonClick = () => {\n if (!fulfilledSteps.registerStep) {\n if (loyaltyGa4) {\n trackLoyaltyCTA('register');\n } else {\n trackLoyaltyCTADeprecated('Register');\n }\n location.href = REGISTER_URL;\n } else {\n if (loyaltyGa4) {\n trackLoyaltyCTA('change_settings');\n } else {\n trackLoyaltyCTADeprecated('ChangeSettings');\n }\n location.href = MY_NOTINO_BENEFIT_PAGE;\n }\n };\n\n const handleLoginClick = () => {\n if (loyaltyGa4) {\n trackLoyaltyCTA('log_in');\n } else {\n trackLoyaltyCTADeprecated('Log in');\n }\n };\n\n React.useEffect(() => {\n if (loyaltyGa4 && !getUserLoading) {\n trackLoyaltyModalOpen(determineStatus(fulfilledSteps), timing);\n }\n }, [fulfilledSteps, timing, loyaltyGa4, getUserLoading]);\n\n React.useEffect(() => {\n if (!loyaltyGa4 && !getUserLoading) {\n trackLoyaltyModalDeprecated(determineStatus(fulfilledSteps));\n }\n }, [fulfilledSteps, loyaltyGa4, getUserLoading]);\n\n return (\n \n \n {fulfilledSteps.registerStep ? (\n \n ) : (\n \n )}\n \n {!fulfilledSteps.registerStep && (\n \n \n \n \n \n \n )}\n \n )\n }\n >\n \n {Object.values(HowToGetDiscountSteps).map((step) => (\n \n \n {fulfilledSteps[step] ? (\n \n ) : (\n \n )}\n \n \n \n ))}\n
\n \n );\n};\n","import { defineMessages } from 'react-intl';\n\nexport const messages = defineMessages({\n possibleDiscount: {\n id: 'pd.loyalty.discount.possible.discount',\n defaultMessage: 'Chci slevu {value}',\n },\n customerDiscountBold: {\n id: 'pd.loyalty.discount.customer.discount.bold',\n defaultMessage: 'Vaše sleva {value}',\n },\n customerDiscount: {\n id: 'pd.loyalty.discount.customer.discount',\n defaultMessage:\n '{customerDiscountBold} na {subjectType} {discountSubject} se uplatní v košíku. {more}',\n },\n moreDiscountInfo: {\n id: 'pd.loyalty.discount.more.discount.info',\n defaultMessage: 'Více',\n },\n categoryDiscount: {\n id: 'pd.loyalty.discount.category.discount',\n defaultMessage: 'kategorii',\n },\n brandDiscount: {\n id: 'pd.loyalty.discount.brand.discount',\n defaultMessage: 'značku',\n },\n});\n","import styled, { css } from 'styled-components';\n\nimport { DiamondIcon, theme } from '@notino/react-styleguide';\n\ninterface LoyaltyDiscountStyledWrapperProps {\n isCustomerDiscount: boolean;\n newDesign: boolean;\n}\n\nexport const LoyaltyDiscountStyledWrapper = styled.button`\n display: flex;\n align-items: center;\n font-size: 0.9rem;\n text-decoration: ${({ isCustomerDiscount }) =>\n isCustomerDiscount ? 'none' : 'underline'};\n cursor: pointer;\n background: none;\n border: none;\n color: #000 !important;\n &:hover {\n text-decoration: none;\n }\n\n ${({ newDesign }) =>\n newDesign &&\n css`\n color: ${theme.palette.neutralDarker} !important;\n `}\n`;\n\nexport const CustomerDiscountWrapper = styled.span`\n text-decoration: underline;\n &:hover {\n text-decoration: none;\n }\n`;\n\nexport const StyledDiamondIcon = styled(DiamondIcon)`\n margin-right: 1rem;\n`;\n\nexport const CustomerDiscountBoldWrapper = styled.span`\n font-weight: 600;\n`;\n","import{_ as t}from\"./_rollupPluginBabelHelpers-fc5c8eb1.js\";import*as e from\"react\";import{Icon as r}from\"./index.js\";import\"./ThemeContext.js\";import\"styled-components\";var l=function(l){return e.createElement(r,t({},l,{viewBox:\"0 0 68 64\"}),e.createElement(\"path\",{fill:\"#505050\",d:\"M48.27,12.69H18.85L8.31,28.11,33.56,63.9,58.81,28.11ZM32.17,54.4l-17-24.14H35.51Zm7.72-24.14H52L36.93,51.6Zm12.17-4.34H39.4l-4-8.89H46ZM21.14,17h9.49l4,8.89H15.06Z\"}),e.createElement(\"rect\",{fill:\"#505050\",x:\"2.59\",y:\"13.57\",width:\"4.34\",height:\"8.78\",transform:\"translate(-11.31 8.63) rotate(-45)\"}),e.createElement(\"rect\",{fill:\"#dc0069\",x:\"14.34\",y:\"1.57\",width:\"4.34\",height:\"8.79\",transform:\"translate(-0.97 7.57) rotate(-25.1)\"}),e.createElement(\"rect\",{fill:\"#dc0069\",x:\"31.39\",y:\"0.07\",width:\"4.34\",height:\"8.78\"}),e.createElement(\"rect\",{fill:\"#dc0069\",x:\"57.97\",y:\"15.79\",width:\"8.78\",height:\"4.34\",transform:\"translate(5.56 49.36) rotate(-45)\"}),e.createElement(\"rect\",{fill:\"#505050\",x:\"46.22\",y:\"3.79\",width:\"8.78\",height:\"4.34\",transform:\"translate(23.75 49.27) rotate(-64.9)\"}))};export{l as DiamondIcon};\n//# sourceMappingURL=DiamondIcon.js.map\n","import { DiscountType, SubjectType } from '@notino/shared/definitions/types';\n\nimport { messages } from './messages';\n\nexport const getDiscountWithSymbol = (\n discountType: DiscountType,\n value: number\n) =>\n ({\n // In future there is open possibility for other DiscountTypes\n [DiscountType.Percentage]: `${value}%`,\n }[discountType]);\n\nexport const subjectTypeMessages: Record<\n SubjectType,\n { id: string; defaultMessage: string }\n> = {\n [SubjectType.BrandDiscount]: messages.brandDiscount,\n [SubjectType.CategoryDiscount]: messages.categoryDiscount,\n};\n","import * as React from 'react';\nimport { FormattedMessage } from 'react-intl';\n\nimport { useQuery } from '@apollo/client';\n\nimport { ModalModel, ModalContext, Colors } from '@notino/react-styleguide';\nimport {\n VariantFragmentFragment,\n GetCustomerBenefitsQuery,\n} from '@notino/shared/definitions/types';\nimport { resetDataLayer } from '@notino/web-tracking';\n\nimport { useFeatureFlags } from '@context/launch-darkly/LDProvider';\nimport {\n TrackingContextProvider,\n useTrackingContext,\n} from '@context/tracking/TrackingContext';\n\nimport { useNewPdDesignEnabled } from '../../hooks/useNewPdDesignEnabled';\nimport { trackPageReload } from '../../tracking';\n\nimport { BenefitsModal } from './components/BenefitsModal';\nimport { HowToLoyaltyModal } from './components/HowToLoyaltyDiscountModal';\nimport { messages } from './messages';\nimport getCustomerBenefitsQuery from './queries/getCustomerBenefits.graphql';\nimport {\n CustomerDiscountBoldWrapper,\n CustomerDiscountWrapper,\n LoyaltyDiscountStyledWrapper,\n StyledDiamondIcon,\n} from './styled';\nimport { trackLoyaltyModalClose } from './trackLoyaltyModal';\nimport { getDiscountWithSymbol, subjectTypeMessages } from './utils';\n\ninterface ILoyaltyDiscount {\n customerBenefits: VariantFragmentFragment['customerBenefits'];\n // We need these props to track pageView of PD after modal close\n gtmPageReloadProps: Parameters[0];\n}\n\nexport const LoyaltyDiscount: React.FC = ({\n customerBenefits,\n gtmPageReloadProps,\n}) => {\n const newDesign = useNewPdDesignEnabled();\n const { loyaltyGa4 } = useFeatureFlags();\n const { data, loading } = useQuery(\n getCustomerBenefitsQuery,\n {\n ssr: false,\n }\n );\n\n const { getTimeFromInit } = useTrackingContext();\n const { toggleModal, hideModal } = ModalContext.useModalContext();\n const modalLastClosedMs = React.useRef(0);\n TrackingContextProvider.useSubscribeToInitTimeReset(() => {\n modalLastClosedMs.current = 0;\n });\n\n const handleClick = () => {\n const hasBenefits = data?.customerBenefits?.hasActiveBenefits;\n const timing = getTimeFromInit() - modalLastClosedMs.current;\n toggleModal(\n hasBenefits ? (\n \n ) : (\n \n ),\n {\n noBorders: true,\n size: ModalModel.Sizes.large,\n showOverflow: true,\n positionBottomOnMobile: true,\n closeIconColor: !hasBenefits\n ? { xs: Colors.basicInverse, md: Colors.neutralDark }\n : undefined,\n onClose: () => {\n if (loyaltyGa4) {\n trackLoyaltyModalClose();\n }\n modalLastClosedMs.current = getTimeFromInit();\n resetDataLayer();\n trackPageReload(gtmPageReloadProps);\n hideModal();\n },\n }\n );\n };\n\n const formattedBenefits = React.useMemo(\n () => ({\n availableDiscount: customerBenefits.availableDiscount && {\n value: getDiscountWithSymbol(\n customerBenefits.availableDiscount.discountType,\n customerBenefits.availableDiscount.value\n ),\n },\n customerDiscount: customerBenefits.customerDiscount && {\n value: getDiscountWithSymbol(\n customerBenefits.customerDiscount.discountType,\n customerBenefits.customerDiscount.value\n ),\n },\n }),\n [customerBenefits.availableDiscount, customerBenefits.customerDiscount]\n );\n\n if (\n !formattedBenefits.availableDiscount &&\n !formattedBenefits.customerDiscount\n ) {\n return null;\n }\n\n const isCustomerDiscount = Boolean(formattedBenefits.customerDiscount);\n\n return (\n \n <>\n \n {customerBenefits.availableDiscount && (\n \n )}\n {customerBenefits.customerDiscount && (\n \n \n ),\n customerDiscountBold: (\n \n \n \n ),\n more: (\n \n \n \n ),\n }}\n />\n \n )}\n >\n \n );\n};\n","import { defineMessages } from 'react-intl';\n\nexport const messages = defineMessages({\n engravingMaxOrderQtyHeader: {\n id: 'engraving.max.order.quantity.header',\n defaultMessage: 'Lze objednat pouze {quantity} {unit}',\n },\n engravingMaxOrderQtyDesc: {\n id: 'engraving.max.order.quantity.description',\n defaultMessage:\n 'Aby se dostalo na co nejvíce zákazníku, z výrobních důvodů zatím neumožňujeme gravírování více kusů na jednu objednávku.',\n },\n addToCartQuantity: {\n id: 'pd.addToCart.quantity',\n defaultMessage: 'Množství',\n },\n});\n","import{a as o,_ as t}from\"./_rollupPluginBabelHelpers-fc5c8eb1.js\";import*as n from\"react\";import{T as r}from\"./index-f865d2ff.js\";import\"./TextAlign.js\";import\"./index-865c84cb.js\";import\"./utils-ec9be121.js\";import\"./StyledDropdown.js\";import\"./breakpoints.js\";import\"./theme.js\";import\"./styled.js\";import\"styled-components\";import\"./Colors.js\";import\"./CheckIcon.js\";import\"./index.js\";import\"./ThemeContext.js\";import\"./HorizontalChevron.js\";import\"./ChevronDownIcon.js\";import\"./ChevronUpIcon.js\";var e=[\"options\",\"unit\",\"handleOptionSelect\",\"currentOption\"],i=function(i){var p=i.options,s=i.unit,m=i.handleOptionSelect,c=void 0===m?function(){return null}:m,u=i.currentOption,l=void 0===u?0:u,a=o(i,e),d=n.useMemo((function(){var o=s?\" \"+s:\"\";return p.map((function(t){return{id:t,label:\"\"+t+o}}))}),[p,s]),j=n.useCallback((function(o){var t=o.id;c(Number(t))}),[c]),f=n.useMemo((function(){return l&&d.find((function(o){return o.id===l}))||d[0]}),[l,d]);return n.createElement(r,t({},a,{options:d,currentOption:f,handleOptionSelect:j}))};export{i as QuantitySelector};\n//# sourceMappingURL=QuantitySelector.js.map\n","import styled, { css } from 'styled-components';\n\nimport { breakpoints, QuantitySelector } from '@notino/react-styleguide';\n\nconst getGridTemplateAreas = (\n isClickAndCollect: boolean,\n isLoyaltyDiscount: boolean\n) => {\n if (isClickAndCollect && isLoyaltyDiscount) {\n return css`\n grid-template-areas:\n 'loyalty'\n 'addToCart'\n 'clickAndCollect';\n @media (min-width: ${breakpoints.sm}) {\n grid-template-columns: 1fr 1fr 1fr;\n grid-template-areas:\n 'loyalty loyalty loyalty'\n 'addToCart clickAndCollect _';\n }\n `;\n }\n if (isLoyaltyDiscount) {\n return css`\n grid-template-areas:\n 'loyalty'\n 'addToCart';\n `;\n }\n if (isClickAndCollect) {\n return css`\n grid-template-areas:\n 'addToCart'\n 'clickAndCollect';\n @media (min-width: ${breakpoints.sm}) {\n grid-template-columns: auto auto 1fr;\n grid-template-areas: 'addToCart clickAndCollect';\n }\n `;\n }\n return css`\n grid-template-areas: 'addToCart';\n `;\n};\n\nconst getNewGridTemplateAreas = (\n isClickAndCollect: boolean,\n isLoyaltyDiscount: boolean\n) => {\n if (isClickAndCollect && isLoyaltyDiscount) {\n return css`\n grid-template-areas:\n 'loyalty'\n 'addToCart'\n 'clickAndCollect';\n `;\n }\n if (isLoyaltyDiscount) {\n return css`\n grid-template-areas:\n 'loyalty'\n 'addToCart';\n `;\n }\n if (isClickAndCollect) {\n return css`\n grid-template-areas:\n 'addToCart'\n 'clickAndCollect';\n `;\n }\n return css`\n grid-template-areas: 'addToCart';\n `;\n};\n\nexport const BuyButtons = styled.div`\n display: grid;\n align-items: center;\n\n ${({ newDesign, isClickAndCollect, isLoyaltyDiscount, canBuy }) =>\n newDesign\n ? css`\n width: 100%;\n grid-gap: 0.5rem;\n padding: 1rem 0 1.5rem;\n ${getNewGridTemplateAreas(isClickAndCollect, isLoyaltyDiscount)}\n `\n : css`\n ${getGridTemplateAreas(isClickAndCollect, isLoyaltyDiscount)}\n grid-gap: 1rem;\n padding: 1rem 0;\n\n @media (min-width: ${breakpoints.sm}) {\n grid-gap: ${canBuy ? '2rem 1rem' : '0rem'};\n }\n `}\n`;\n\nexport const ButtonWrapper = styled.div<{ newDesign: boolean }>`\n & > button,\n form button {\n padding: 0 0.5625rem !important;\n height: 2.75rem;\n min-width: 7.5rem;\n text-transform: none !important;\n font-weight: normal !important;\n }\n flex: 1;\n min-width: 7.5rem;\n\n ${({ newDesign }) =>\n newDesign\n ? css`\n & > button,\n form button {\n height: 3.25rem;\n }\n `\n : css`\n @media (min-width: ${breakpoints.sm}) {\n flex: 0 1;\n }\n `}\n`;\n\nexport const Label = styled.label`\n display: none;\n`;\n\nexport const DropdownButtonSeparator = styled.div<{ newDesign: boolean }>`\n ${({ newDesign }) =>\n newDesign ? 'padding-right: 0.5rem' : 'padding-right: 1rem'}\n`;\n\nexport const QuantityWrapper = styled.div`\n width: 5.25rem;\n font-size: 0.875rem;\n & > div > div {\n min-height: 2.75rem;\n }\n`;\n\ninterface IWrapperProps {\n isClickAndCollect?: boolean;\n isLoyaltyDiscount?: boolean;\n canBuy?: boolean;\n newDesign: boolean;\n}\n\nexport const AddToCartWrapper = styled.div`\n grid-area: addToCart;\n\n display: flex;\n align-items: center;\n`;\n\nexport const ClickAndCollectWrapper = styled.div`\n grid-area: clickAndCollect;\n\n ${({ isClickAndCollect, newDesign }) =>\n isClickAndCollect &&\n css`\n margin-top: ${newDesign ? '-0.5rem' : '-1rem'};\n `}\n\n @media (min-width: ${breakpoints.sm}) {\n margin-top: 0;\n }\n`;\n\nexport const LoyaltyDiscountWrapper = styled.div`\n grid-area: loyalty;\n\n ${({ newDesign }) =>\n newDesign &&\n css`\n margin-bottom: 0.75rem;\n width: 100%;\n `}\n`;\n\nexport const StyledQuantitySelector = styled(QuantitySelector)<{\n newDesign: boolean;\n}>`\n ${({ newDesign }) =>\n newDesign &&\n css`\n [role='combobox'] {\n height: 3.25rem;\n }\n `}\n`;\n","import { snakeCase } from 'lodash';\n\nimport { GetProductViewQuery } from '@notino/shared/definitions/types';\nimport { CartType } from '@notino/web-tracking';\n\nimport { dispatchTrackingEvent } from '@context/tracking/utils';\nimport { ProductEventWither } from '@helpers/googleTagManager';\n\nexport const ENGRAVING_CODE = 'ENGXXXU_SENG01';\n\nexport const engravingPrefix = (text: string) => {\n return `Engraving - ${text}`;\n};\n\nexport const trackEngravingAddToCart = (\n productInfo: GetProductViewQuery['productDetailByMasterId'][number],\n variant: GetProductViewQuery['productDetailByMasterId'][number]['variants'][number],\n quantity,\n additionalPromoLabels: string[]\n) => {\n dispatchTrackingEvent({\n event: 'add_to_cart',\n product: ProductEventWither()\n .withProduct(productInfo)\n .withVariant(variant)\n .withAdditionalData({\n // use engraving product code instead of product code\n // more info in comments: https://notino.tpondemand.com/entity/132907-nushopweb-pd-trackovani-pridavani-gravirovani-do\n product_code: ENGRAVING_CODE,\n price: variant.engraving.config.price,\n brand_name: engravingPrefix(productInfo.brand),\n ...(variant.name && {\n name: engravingPrefix(variant.name),\n }),\n cart_type: snakeCase(CartType.cart),\n quantity,\n })\n .withAdditionalPromoLabels(additionalPromoLabels)\n .build(),\n _clear: true,\n });\n};\n","export const prepareOptions = (maxQuantity: number): number[] => {\n const options = [];\n\n for (let option = 1; option <= maxQuantity; option++) {\n options.push(option);\n }\n\n return options;\n};\n","import * as React from 'react';\nimport { FormattedMessage, useIntl } from 'react-intl';\n\nimport { snakeCase } from 'lodash';\n\nimport { ModalModel, TextAlign, ModalContext } from '@notino/react-styleguide';\nimport {\n CartServiceType,\n GetProductViewQuery,\n} from '@notino/shared/definitions/types';\nimport { AdditionalServicesAvailability, CartType } from '@notino/web-tracking';\nimport { Ga4Events } from '@notino/web-tracking/dist/types/package/ga4/events';\n\nimport ErrorBoundary from '@components/ErrorBoundary';\nimport { useTrackingDisplayType } from '@containers/ProductDetailContainer/utils';\nimport { useTrackingContext } from '@context/tracking/TrackingContext';\nimport { dispatchTrackingEvent } from '@context/tracking/utils';\nimport { ProductEventWither } from '@helpers/googleTagManager';\nimport { AddToCartButton } from '@sharedComponents/AddToCartButton/AddToCartButton';\n\nimport { ICurrency } from '../../../../App/model';\nimport { useNewPdDesignEnabled } from '../../hooks/useNewPdDesignEnabled';\nimport { usePriceLogic } from '../../hooks/usePriceLogic';\nimport {\n MIN_PRICE_PROMO_LABEL,\n RRP_PRICE_PROMO_LABEL,\n} from '../../hooks/usePriceLogic/tracking';\nimport { IModifaceEffectVariants } from '../../ModiFaceModal/model';\nimport { trackPageReload } from '../../tracking';\nimport { getLoyaltyDiscountState } from '../../utils';\nimport { ClickAndCollect } from '../ClickAndCollect';\nimport { useEngravingTryItFirstContext } from '../context/EngravingTryItFirst';\nimport { LoyaltyDiscount } from '../LoyaltyDiscount';\n\nimport { messages } from './addToCartMessages';\nimport {\n ButtonWrapper,\n BuyButtons,\n Label,\n DropdownButtonSeparator,\n QuantityWrapper,\n AddToCartWrapper,\n ClickAndCollectWrapper,\n LoyaltyDiscountWrapper,\n StyledQuantitySelector,\n} from './styled';\nimport { trackEngravingAddToCart } from './tracking';\nimport { prepareOptions } from './utils/prepareOptions';\n\ninterface IAddToCartProps {\n product: GetProductViewQuery['productDetailByMasterId'][number];\n variant: GetProductViewQuery['productDetailByMasterId'][number]['variants'][number];\n orderUnit: string;\n productCode: string;\n currency: ICurrency;\n onProductAdded: () => void;\n modifaceVariants: IModifaceEffectVariants;\n engravingAvailable: AdditionalServicesAvailability;\n tryItFirstAvailable: AdditionalServicesAvailability;\n giftAvailable: boolean;\n}\n\nconst QUANTITY_SELECTOR_KEYBOARD_CONFIG = {\n keyNavigation: true,\n};\n\nexport const AddToCart: React.FC = React.memo(\n ({\n variant,\n product,\n orderUnit,\n onProductAdded,\n modifaceVariants,\n engravingAvailable,\n tryItFirstAvailable,\n giftAvailable,\n }) => {\n const {\n state: { engravingInitials, withEngraving, withTryItFirst },\n } = useEngravingTryItFirstContext();\n const { hideModal, toggleModal } = ModalContext.useModalContext();\n const { getTimeFromInit } = useTrackingContext();\n const { formatMessage } = useIntl();\n const newDesign = useNewPdDesignEnabled();\n const [quantity, setQuantity] = React.useState(1);\n const { rrpShown, minimalPriceShown } = usePriceLogic(variant);\n\n const trackingDisplayType = useTrackingDisplayType(product);\n\n const onModalClose = React.useCallback(() => {\n setQuantity(1);\n hideModal();\n }, [hideModal]);\n\n const changeAmount = React.useCallback(\n (newQuantity: number): void => {\n if (withEngraving && newQuantity > 1) {\n const modalOptions = {\n header: (\n \n {formatMessage(messages.engravingMaxOrderQtyHeader, {\n quantity: 1,\n unit: orderUnit,\n })}\n
\n ),\n type: ModalModel.Types.default,\n onClose: onModalClose,\n };\n\n const modalContent = (\n \n );\n\n toggleModal(modalContent, modalOptions);\n }\n setQuantity(newQuantity);\n },\n [formatMessage, onModalClose, orderUnit, toggleModal, withEngraving]\n );\n\n const loyaltyDiscount = getLoyaltyDiscountState(variant.customerBenefits);\n\n const handleGtmPushProductDetail = React.useCallback(() => {\n trackPageReload({\n rrpShown,\n minimalPriceShown,\n variant,\n product,\n modifaceVariants,\n engravingAvailable,\n tryItFirstAvailable,\n giftAvailable,\n loyaltyDiscount,\n trackingDisplayType,\n });\n }, [\n variant,\n product,\n modifaceVariants,\n engravingAvailable,\n tryItFirstAvailable,\n giftAvailable,\n loyaltyDiscount,\n rrpShown,\n minimalPriceShown,\n trackingDisplayType,\n ]);\n\n const handleQuantitySelectorClick = () => {\n dispatchTrackingEvent({\n event: 'element_click',\n element: {\n timing: getTimeFromInit(),\n interaction: 'click',\n mode: undefined,\n name: 'quantity_selector',\n type: 'product_detail',\n action: 'click_on_element',\n promo_labels: undefined,\n },\n _clear: true,\n });\n };\n\n const services = React.useMemo(\n () =>\n withEngraving && [\n {\n type: CartServiceType.Engraving,\n value: engravingInitials,\n count: quantity,\n productId: variant.id,\n },\n ],\n [withEngraving, engravingInitials, quantity, variant.id]\n );\n\n const trackingProduct: Extract<\n Ga4Events,\n { event: 'add_to_cart' }\n >['product'] = React.useMemo(\n () =>\n ProductEventWither()\n .withProduct(product)\n .withVariant(variant)\n .withServices({\n modifaceVariants,\n tryItFirstAvailable,\n engravingAvailable,\n shadefinderAvailable: product.isShadeFinderAvailable,\n })\n .withAdditionalData({ quantity, cart_type: snakeCase(CartType.cart) })\n .withAdditionalPromoLabels([\n rrpShown && RRP_PRICE_PROMO_LABEL,\n minimalPriceShown && MIN_PRICE_PROMO_LABEL,\n ])\n .build(),\n [\n engravingAvailable,\n tryItFirstAvailable,\n product,\n minimalPriceShown,\n rrpShown,\n modifaceVariants,\n quantity,\n variant,\n ]\n );\n\n const handleProductAdded = React.useCallback(() => {\n onProductAdded();\n\n const additionalPromoLabels = [\n rrpShown && RRP_PRICE_PROMO_LABEL,\n minimalPriceShown && MIN_PRICE_PROMO_LABEL,\n ];\n dispatchTrackingEvent({\n event: 'add_to_cart',\n product: trackingProduct,\n _clear: true,\n });\n\n if (withEngraving) {\n trackEngravingAddToCart(\n product,\n variant,\n quantity,\n additionalPromoLabels\n );\n }\n }, [\n trackingProduct,\n product,\n variant,\n quantity,\n withEngraving,\n minimalPriceShown,\n rrpShown,\n onProductAdded,\n ]);\n\n const trackAddFailed = React.useCallback(\n (message: string) => {\n dispatchTrackingEvent({\n event: 'add_to_cart_failed',\n add: {\n products: [trackingProduct],\n },\n error: {\n status: message || '',\n },\n _clear: true,\n });\n },\n [trackingProduct]\n );\n\n const isClickAndCollect = variant.availablePickUpStores > 0;\n const isLoyaltyDiscount =\n Boolean(variant.customerBenefits?.availableDiscount) ||\n Boolean(variant.customerBenefits?.customerDiscount);\n\n return (\n \n \n {variant.canBuy && (\n \n \n \n \n \n \n (\n \n )}\n showAddToCartModal={product.config.showAddToCartModal}\n buttonElementId=\"pd-buy-button\"\n onProductAdded={handleProductAdded}\n onProductAddFailed={trackAddFailed}\n onClosingModal={handleGtmPushProductDetail}\n withLegacyAddToCart={true}\n />\n \n )}\n {isClickAndCollect && (\n \n 1}\n />\n \n )}\n {isLoyaltyDiscount && variant.canBuy && (\n \n \n \n )}\n \n \n );\n }\n);\n\nAddToCart.displayName = 'AddToCart';\n","import { defineMessages } from 'react-intl';\n\nexport const messages = defineMessages({\n taxIncludedDE: {\n id: 'pd.price.tax.included',\n defaultMessage: ', inkl. MwSt',\n },\n});\n","import styled from 'styled-components';\n\nimport { CurrencyStyled } from '@components/PriceLabel/components/styled';\n\nexport const PriceWrapper = styled.span`\n font-size: inherit;\n`;\n\nexport const CurrencyWrapper = styled(CurrencyStyled)`\n font-size: inherit;\n`;\n","import * as React from 'react';\nimport { FormattedMessage } from 'react-intl';\n\nimport { useQuery } from '@apollo/client';\n\nimport { IStock } from '@notino/shared/definitions/custom-definitions';\nimport { GetShowUnitPricesQuery } from '@notino/shared/definitions/types';\n\nimport PriceLabel from '@components/PriceLabel';\nimport { ILocale } from '@containers/App/model';\nimport { IPrice } from '@containers/ProductListing/model';\nimport getShowUnitPricesQuery from '@queries/showUnitPrices.graphql';\nimport { UnitPriceDecimalPlaces } from '@utils/constants';\n\nimport { messages } from './messages';\nimport { CurrencyWrapper, PriceWrapper } from './styled';\n\nexport interface IPriceRatioProps {\n stockAvailability: string;\n unitAmount: number;\n locale: ILocale;\n unit: string;\n unitPrice?: IPrice;\n settingsQuery?: GetShowUnitPricesQuery;\n prepend?: JSX.Element;\n}\n\nexport const showPriceRatio = (\n queryResult: GetShowUnitPricesQuery,\n stockAvailability: string,\n unitPrice: IPrice,\n unitAmount: number\n): boolean =>\n queryResult.Settings &&\n queryResult.Settings.showUnitPrices &&\n stockAvailability !== IStock.outOfStock &&\n unitPrice &&\n !!unitAmount;\n\nexport const PriceRatio: React.FC = ({\n unitAmount,\n unit,\n stockAvailability,\n unitPrice,\n settingsQuery: settingsQueryResultProps,\n prepend,\n}) => {\n const { data: settingsQueryResult } = useQuery(\n getShowUnitPricesQuery,\n {\n ssr: true,\n skip: Boolean(settingsQueryResultProps),\n }\n );\n\n const queryResult = settingsQueryResultProps || settingsQueryResult;\n\n if (!showPriceRatio(queryResult, stockAvailability, unitPrice, unitAmount)) {\n return null;\n }\n\n return (\n <>\n {prepend}\n \n {' '}\n / {`${unitAmount}\\u00A0${unit}`}\n \n \n >\n );\n};\n","import * as React from 'react';\nimport { FormattedMessage } from 'react-intl';\n\nimport { useQuery } from '@apollo/client';\n\nimport { Colors, Tooltip, TooltipModel } from '@notino/react-styleguide';\nimport { IStock } from '@notino/shared/definitions/custom-definitions';\nimport {\n Roles,\n VariantFragmentFragment,\n GetUserWithCartQuery,\n} from '@notino/shared/definitions/types';\n\nimport { useFormatPrice } from '@components/PriceLabel/useFormattedPrice';\nimport { ILocale } from '@containers/App/model';\nimport {\n IDamage,\n MaximumVolumeInPercent,\n} from '@containers/ProductListing/model';\nimport getUserWithCartQuery from '@queries/userWithCart.graphql';\n\nimport messages from '../../../messages';\nimport { useNewPdDesignEnabled } from '../../hooks/useNewPdDesignEnabled';\nimport { usePriceLogic } from '../../hooks/usePriceLogic';\nimport { PriceRatio } from '../VariantsInSelectBox/PriceRatio';\n\nimport {\n Wrapper,\n CodeBlock,\n CodeName,\n Separator,\n Availability,\n AdditionalDescriptionStyled,\n AdditionalText,\n StyledInfoIcon,\n TooltipContent,\n} from './styled';\n\nconst Damage: React.FC<{ damage: IDamage }> = ({ damage }) => {\n const message =\n damage.volumeInPercent === MaximumVolumeInPercent\n ? messages.productDamaged\n : messages.productReturned;\n\n return (\n \n \n \n );\n};\n\ninterface IBelowTheLineProps {\n variant: VariantFragmentFragment;\n locale: ILocale;\n}\n\nexport const BellowTheLine: React.FC = React.memo(\n ({ variant, locale }) => {\n const {\n stockAvailability,\n attributes,\n orderCode,\n productCode,\n id,\n annotation,\n additionalText,\n availablePickUpStores,\n canBuy,\n } = variant;\n\n const { data: { user: { role } = { role: null } } = {} } =\n useQuery(getUserWithCartQuery);\n\n const { bottomRecentPrice } = usePriceLogic(variant);\n const formatPrice = useFormatPrice();\n const newDesign = useNewPdDesignEnabled();\n\n const isImmediateCollection = availablePickUpStores > 0 && !canBuy;\n\n const isMaster = attributes && attributes.Master;\n\n return (\n \n \n {isImmediateCollection ? (\n \n ) : (\n \n )}\n \n\n {variant.unitPrice && (\n | }\n stockAvailability={variant.stockAvailability.code}\n unitAmount={variant.unitPrice.perAmount}\n locale={locale}\n unit={variant.unitPrice.unit}\n unitPrice={variant.unitPrice}\n />\n )}\n\n {bottomRecentPrice && (\n <>\n | \n {' '}\n {formatPrice(bottomRecentPrice.value)?.formattedPriceWithCurrency}\n {bottomRecentPrice.tooltip}\n }\n position={TooltipModel.Position.topLeft}\n >\n \n \n >\n )}\n\n | \n\n \n \n {' '}\n \n\n {role === Roles.Admin ? (\n \n {orderCode} {productCode} {id}\n {isMaster && M}\n \n ) : (\n orderCode\n )}\n \n\n {role === Roles.Admin && attributes.EAN && (\n \n EAN: {attributes.EAN}\n \n )}\n\n {attributes.IsForProfessionals && (\n \n \n \n )}\n\n {attributes.Damage && }\n\n {attributes.WithoutCellophane && (\n \n \n \n )}\n\n {attributes.DifferentPackaging && (\n \n \n \n )}\n\n {annotation && annotation.length > 0 && (\n \n {annotation}\n \n )}\n\n {additionalText && (\n \n )}\n \n );\n }\n);\n","import{_ as e}from\"./_rollupPluginBabelHelpers-fc5c8eb1.js\";import{c as t}from\"./createSvgIcon-89a7bcb8.js\";import\"react\";import\"./index.js\";import\"./ThemeContext.js\";import\"styled-components\";var o=function(o){return t(\"M497.535,14.465c-19.569-19.568-51.395-19.241-70.557,0.726L322.092,124.488L66.131,39.781L12.4,93.513l213.352,131.365L117.796,337.372l-69.231-11.366L0,374.571l101.78,35.649L137.429,512l48.565-48.565l-11.366-69.231l112.494-107.955L418.487,499.6l53.732-53.732l-84.706-255.961L496.808,85.022C516.776,65.86,517.103,34.034,497.535,14.465z\",e({viewBox:\"0 0 512 512\",\"data-testid\":\"plane-icon\"},o))};export{o as PlaneIcon};\n//# sourceMappingURL=PlaneIcon.js.map\n","import{c as e}from\"./createSvgIcon-89a7bcb8.js\";import\"react\";import\"./index.js\";import\"./_rollupPluginBabelHelpers-fc5c8eb1.js\";import\"./ThemeContext.js\";import\"styled-components\";var r=function(r){return e(\"M0,21.95,8,4h8.42L10.5,22ZM0,30V25H56v5H52.5V60H23V36H9V60H3.47V30Zm12.25-8L18.37,4H27V22ZM28.07,49H47V36H28.07ZM29,22V4h8.64l6.12,18ZM39.59,4H48l7.66,18H45.5Z\",r)};export{r as ShopIcon};\n//# sourceMappingURL=ShopIcon.js.map\n","import styled, { css } from 'styled-components';\n\nimport { breakpoints, theme } from '@notino/react-styleguide';\n\nexport const Container = styled.div<{ newDesign: boolean }>`\n padding: 1rem 0;\n\n ${({ newDesign }) =>\n newDesign &&\n css`\n padding: 1rem 0 2rem;\n @media (min-width: ${breakpoints.lg}) {\n padding: 1rem 0;\n }\n `}\n`;\n\nexport const SelectWrapper = styled.div``;\n\nexport const Select = styled.div`\n display: flex;\n justify-content: space-between;\n cursor: pointer;\n`;\n\nexport const DeliveryList = styled.div`\n flex: 10;\n padding-right: 0.5rem;\n`;\n\nexport const ChevronIconWrapper = styled.button<{ newDesign: boolean }>`\n cursor: pointer;\n width: 1rem;\n border: none;\n background-color: ${(props) => props.theme.palette.basicInverse};\n display: flex;\n justify-content: center;\n align-items: center;\n\n ${({ newDesign }) =>\n newDesign\n ? css`\n max-height: 6.5rem;\n `\n : css`\n max-height: 5.5rem;\n @media (min-width: ${breakpoints.sm}) {\n max-height: 3.1rem;\n }\n @media (min-width: ${breakpoints.md}) {\n max-height: 5.5rem;\n }\n @media (min-width: ${breakpoints.lg}) {\n max-height: 3.1rem;\n }\n `}\n`;\n\nexport const FlexRow = styled.div`\n align-items: flex-start;\n justify-content: space-between;\n color: ${(props) => props.theme.palette.neutralDarker};\n display: flex;\n flex-wrap: wrap;\n`;\n\nexport const FlexRowWithMargin = styled(FlexRow)<{ newDesign: boolean }>`\n &:last-child {\n margin-bottom: 0;\n }\n\n ${({ newDesign }) =>\n newDesign\n ? css`\n margin-bottom: 0.75rem;\n `\n : css`\n margin-bottom: 0.625rem;\n `}\n`;\n\nexport const DeliveryInfo = styled.div`\n display: flex;\n justify-content: space-between;\n flex-basis: 100%;\n align-items: center;\n`;\n\nexport const TopDeliveryInfo = styled(DeliveryInfo)<{ newDesign: boolean }>`\n flex-basis: 100%;\n\n &:last-child {\n margin-bottom: 0;\n }\n\n ${({ newDesign }) =>\n newDesign\n ? css`\n margin-bottom: 1.25rem;\n `\n : css`\n margin-bottom: 0.625rem;\n `}\n`;\n\nexport const DeliveryInfoWrapper = styled.div<{ newDesign: boolean }>`\n display: flex;\n width: 100%;\n\n ${({ newDesign }) =>\n newDesign\n ? css`\n flex-direction: column;\n `\n : css`\n flex-wrap: wrap;\n align-items: center;\n justify-content: space-between;\n gap: 0.25rem;\n `}\n`;\n\nexport interface IDeliveryTextColorProps {\n isInactive: boolean;\n newPdDesignEnabled: boolean;\n}\n\nconst deliveryTextColor = css`\n color: ${(props: IDeliveryTextColorProps) =>\n props.isInactive ? theme.palette.neutral : theme.palette.basic};\n`;\n\nexport const Name = styled.div`\n padding-right: 0.3125rem;\n min-width: 12.5rem;\n flex-basis: 100%;\n line-height: 1.2rem;\n ${deliveryTextColor};\n\n ${({ newPdDesignEnabled }) =>\n newPdDesignEnabled\n ? theme.typography.labelRegular\n : css`\n font-weight: bold;\n font-size: 0.875rem;\n `}\n\n @media (min-width: ${breakpoints.sm}) {\n flex-basis: auto;\n line-height: 1.3rem;\n }\n\n @media (min-width: ${breakpoints.md}) {\n flex-basis: 100%;\n line-height: 1.2rem;\n }\n\n @media (min-width: ${breakpoints.lg}) {\n flex-basis: auto;\n line-height: 1.3rem;\n }\n`;\n\nexport const DeliveryDate = styled.div`\n line-height: 1.2rem;\n ${deliveryTextColor}\n\n ${({ newPdDesignEnabled }) =>\n newPdDesignEnabled\n ? theme.typography.labelRegular400\n : css`\n font-weight: 300;\n font-size: 1rem;\n `}\n\n @media (min-width: ${breakpoints.sm}) {\n line-height: 1.3rem;\n }\n`;\n\nexport const TimeText = styled.span`\n color: ${(props) => props.theme.palette.successDark};\n`;\n\nexport const StyledHr = styled.hr<{ newDesign: boolean }>`\n height: 0.0625rem;\n border: none;\n background-color: ${(props) => props.theme.palette.neutralLighter};\n\n ${({ newDesign }) =>\n newDesign\n ? css`\n margin: 1.25rem 0 0.75rem 0;\n `\n : css`\n margin-top: 1rem;\n `}\n`;\n\nexport const DeliveryDescription = styled.div<{ newPdDesignEnabled: boolean }>`\n margin: 0.625rem 0 0.75rem 0;\n\n color: ${(props) => props.theme.palette.neutralDarker};\n\n ${({ newPdDesignEnabled }) =>\n newPdDesignEnabled\n ? theme.typography.bodySmall\n : css`\n font-size: 0.875rem;\n `}\n`;\n","import * as React from 'react';\n\nimport { Colors } from '@notino/react-styleguide';\n\nimport { useNewPdDesignEnabled } from '../../hooks/useNewPdDesignEnabled';\nimport { LeftIconWrapper } from '../styled';\n\nimport { DeliveryDate, DeliveryInfoWrapper, Name } from './styled';\n\ninterface IDeliveryLineProps {\n showIcon: boolean;\n icon: JSX.Element;\n name: string;\n description: JSX.Element | string;\n isInactive: boolean;\n}\n\nexport const DeliveryLine: React.FC = ({\n showIcon,\n icon,\n name,\n description,\n isInactive,\n}) => {\n const newPdDesignEnabled = useNewPdDesignEnabled();\n\n return (\n <>\n \n {showIcon &&\n React.cloneElement(icon, {\n color: isInactive ? Colors.neutral : Colors.basic,\n width: '20px',\n height: '20px',\n })}\n \n\n \n \n {name}\n \n\n \n {description}\n \n \n >\n );\n};\n","import { defineMessages } from 'react-intl';\n\nexport const messages = defineMessages({\n deliveryInfo: {\n id: 'pd.delivery.info',\n defaultMessage: 'Způsoby dopravy',\n },\n});\n","import * as React from 'react';\nimport { useIntl } from 'react-intl';\n\nimport styled, { css } from 'styled-components';\n\nimport { theme } from '@notino/react-styleguide';\n\nimport { useNewPdDesignEnabled } from '../../hooks/useNewPdDesignEnabled';\n\ninterface IFormattedDateProps {\n date: string;\n}\n\ninterface IFormattedTimeProps {\n from: string;\n to: string;\n}\n\nconst Span = styled.span<{ newDesign: boolean }>`\n ${({ newDesign }) =>\n newDesign &&\n css`\n color: ${theme.palette.neutralDarker};\n ${theme.typography.labelRegular400}\n `}\n`;\n\nexport const FormattedDate: React.FC = ({ date }) => {\n const { formatDate } = useIntl();\n const newDesign = useNewPdDesignEnabled();\n\n const dateObj = new Date(date);\n const dayName = formatDate(dateObj, { weekday: 'long' });\n const dayNumber = formatDate(dateObj, {\n day: 'numeric',\n month: 'numeric',\n year: 'numeric',\n });\n\n return {`${dayName} ${dayNumber}`};\n};\n\nexport const FormattedTime: React.FC = ({ from, to }) => {\n const { formatTime } = useIntl();\n const newDesign = useNewPdDesignEnabled();\n\n const fromObj = new Date(from);\n const fromNumber = formatTime(fromObj, {\n hour: '2-digit',\n minute: '2-digit',\n });\n\n const toObj = new Date(to);\n const toNumber = formatTime(toObj, {\n hour: '2-digit',\n minute: '2-digit',\n });\n\n return {`${fromNumber} - ${toNumber}`};\n};\n","import * as React from 'react';\nimport { FormattedMessage } from 'react-intl';\n\nimport messages from '../../../messages';\nimport { isTomorrow } from '../utils/dateUtils';\n\nimport { FormattedDate, FormattedTime } from './FormattedDate';\nimport { TimeText as StyledTimeText } from './styled';\n\ninterface ITimeTextProps {\n date: string;\n from?: string;\n to?: string;\n}\n\nexport const TimeText: React.FC = ({ date, from, to }) => {\n const dateToCompare = new Date(date);\n const nowDate = new Date().getDate();\n\n const isTomorrowDate = isTomorrow(dateToCompare);\n const isTodayDate = dateToCompare.getDate() === nowDate;\n\n const getFormattedDate = () => {\n if (from && to && (isTodayDate || isTomorrowDate)) {\n return ;\n }\n return ;\n };\n\n if (isTomorrowDate) {\n return (\n <>\n \n \n {' '}\n {getFormattedDate()}\n >\n );\n } else if (isTodayDate) {\n return (\n <>\n \n {' '}\n \n {from && to && getFormattedDate()}\n >\n );\n }\n\n return getFormattedDate();\n};\n","import * as React from 'react';\nimport { FormattedMessage, useIntl } from 'react-intl';\n\nimport {\n Colors,\n PlaneIcon,\n ShopIcon,\n TruckIcon,\n} from '@notino/react-styleguide';\nimport { GetProductViewQuery } from '@notino/shared/definitions/types';\n\nimport ErrorBoundary from '@components/ErrorBoundary';\nimport { useNewPdDesignEnabled } from '@containers/ProductDetailContainer/ProductDetail/hooks/useNewPdDesignEnabled';\nimport {\n DeliveryDisallowedReasons,\n IDelivery,\n} from '@containers/ProductListing/model';\nimport { useTrackingContext } from '@context/tracking/TrackingContext';\nimport { dispatchTrackingEvent } from '@context/tracking/utils';\nimport { HorizontalChevron } from '@sharedComponents/Icons/HorizontalChevron';\n\nimport messages from '../../../messages';\n\nimport { DeliveryLine } from './DeliveryLine';\nimport { messages as deliveryMessages } from './messages';\nimport {\n ChevronIconWrapper,\n Container,\n DeliveryDescription,\n DeliveryInfo,\n DeliveryList,\n FlexRow,\n FlexRowWithMargin,\n Select,\n SelectWrapper,\n StyledHr,\n TopDeliveryInfo,\n} from './styled';\nimport { TimeText } from './TimeText';\nimport {\n groupPossibleDeliveries,\n hasOneDeliveryTypeWithTwoOptions,\n} from './utils';\n\nexport enum DeliveriesDisplayMode {\n Static = 'Static',\n Expandable = 'Expandable',\n}\n\nexport interface IDeliveryTimeProps {\n deliveries: GetProductViewQuery['productDetailByMasterId'][number]['deliveries'];\n displayMode?: DeliveriesDisplayMode;\n}\n\nconst disallowedReasonMessageKeyMapping = {\n [DeliveryDisallowedReasons.AirTransportDisallowed]:\n messages.deliveryDisallowed,\n};\n\nconst getIconByDelivery = (delivery: IDelivery): JSX.Element => {\n if (delivery.isPersonal) {\n return ;\n } else if (delivery.isAirTransport) {\n return ;\n }\n\n return ;\n};\n\nexport const DeliveryTime: React.FC = React.memo(\n ({ deliveries, displayMode }) => {\n const { formatMessage } = useIntl();\n const { getTimeFromInit } = useTrackingContext();\n const isStatic = displayMode === DeliveriesDisplayMode.Static;\n const newPdDesignEnabled = useNewPdDesignEnabled();\n\n const [showAllDeliveriesMode, setShowAllDeliveriesMode] =\n React.useState(isStatic);\n\n const handleDeliveryListClick = (e: React.SyntheticEvent) => {\n e.preventDefault();\n\n dispatchTrackingEvent({\n event: 'element_click',\n element: {\n name: 'delivery_options',\n type: 'product_detail',\n mode: !showAllDeliveriesMode ? 'on' : 'off',\n timing: getTimeFromInit(),\n interaction: 'click',\n action: 'click_on_delivery',\n promo_labels: undefined,\n },\n _clear: true,\n });\n\n setShowAllDeliveriesMode((old) => !old);\n };\n\n const getDeliveryDescription = (delivery: IDelivery) => {\n if (delivery.disallowedReason) {\n const messageDescriptor =\n disallowedReasonMessageKeyMapping[delivery.disallowedReason];\n\n if (messageDescriptor) {\n return ;\n }\n }\n\n return (\n \n );\n };\n\n const groupedDeliveries = React.useMemo(\n () => groupPossibleDeliveries(deliveries),\n [deliveries]\n );\n\n const showAllDeliveries =\n showAllDeliveriesMode ||\n hasOneDeliveryTypeWithTwoOptions(groupedDeliveries);\n\n return (\n \n