diff --git a/public/images/back-icon.svg b/public/images/back-icon.svg new file mode 100644 index 00000000..a5e9fc9b --- /dev/null +++ b/public/images/back-icon.svg @@ -0,0 +1,3 @@ + + + diff --git a/public/images/checkboxChecked.svg b/public/images/checkboxChecked.svg deleted file mode 100644 index bcf8ad02..00000000 --- a/public/images/checkboxChecked.svg +++ /dev/null @@ -1,24 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - diff --git a/public/images/checkboxUnchecked.svg b/public/images/checkboxUnchecked.svg deleted file mode 100644 index e69c317e..00000000 --- a/public/images/checkboxUnchecked.svg +++ /dev/null @@ -1,17 +0,0 @@ - - - - - - - - - - - - - - - - diff --git a/public/images/preview2.png b/public/images/preview2.png new file mode 100644 index 00000000..a3fbe286 Binary files /dev/null and b/public/images/preview2.png differ diff --git a/public/images/radioChecked.svg b/public/images/radioChecked.svg deleted file mode 100644 index 27cf636c..00000000 --- a/public/images/radioChecked.svg +++ /dev/null @@ -1,34 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/public/images/radioUnchecked.svg b/public/images/radioUnchecked.svg deleted file mode 100644 index e14a682f..00000000 --- a/public/images/radioUnchecked.svg +++ /dev/null @@ -1,17 +0,0 @@ - - - - - - - - - - - - - - - - diff --git a/public/images/settings.svg b/public/images/settings.svg new file mode 100644 index 00000000..e04a2b84 --- /dev/null +++ b/public/images/settings.svg @@ -0,0 +1,11 @@ + + + + + + + + + + + diff --git a/src/config/lexics/indexLexics.tsx b/src/config/lexics/indexLexics.tsx index d22be254..0240c147 100644 --- a/src/config/lexics/indexLexics.tsx +++ b/src/config/lexics/indexLexics.tsx @@ -1,5 +1,25 @@ import { proceduresLexics } from './procedures' +const matchPopupLexics = { + apply: 13491, + episode_duration: 13410, + go_back_to_match: 13405, + match_interviews: 13031, + match_playlist_ball_in_play: 2489, + match_playlist_full_game: 13028, + match_playlist_goals: 3559, + match_playlist_highlights: 13033, + match_settings: 13490, + playlist_format: 13406, + playlist_format_all_actions: 13408, + playlist_format_all_match_time: 13407, + playlist_format_selected_acions: 13409, + sec_after: 13412, + sec_before: 13411, + selected_player_actions: 13413, + team_players: 13398, +} + export const indexLexics = { add_to_favorites: 1701, add_to_favorites_error: 12943, @@ -49,4 +69,5 @@ export const indexLexics = { watch_now: 13020, ...proceduresLexics, + ...matchPopupLexics, } diff --git a/src/config/procedures.tsx b/src/config/procedures.tsx index f152ea96..df44e040 100644 --- a/src/config/procedures.tsx +++ b/src/config/procedures.tsx @@ -20,6 +20,8 @@ export const PROCEDURES = { get_user_subscriptions: 'get_user_subscriptions', logout_user: 'logout_user', lst_c_country: 'lst_c_country', + ott_match_popup: 'ott_match_popup', + ott_match_popup_actions: 'ott_match_popup_actions', param_lexical: 'param_lexical', save_user_custom_subscription: 'save_user_custom_subscription', save_user_favorite: 'save_user_favorite', diff --git a/src/features/App/AuthenticatedApp.tsx b/src/features/App/AuthenticatedApp.tsx index e9edf544..e803a398 100644 --- a/src/features/App/AuthenticatedApp.tsx +++ b/src/features/App/AuthenticatedApp.tsx @@ -19,6 +19,7 @@ import { UserAccountForm } from 'features/UserAccount' import { MatchSwitchesStore } from 'features/MatchSwitches' import { UserFavoritesStore } from 'features/UserFavorites/store' +import { MatchPopupStore } from 'features/MatchPopup' export const AuthenticatedApp = () => { useLexicsConfig(indexLexics) @@ -27,31 +28,34 @@ export const AuthenticatedApp = () => { - {/* в Switch как прямой children можно рендерить только Route или Redirect */} - - - - - - - - - - - - - - - - - - - - - - - - + + + {/* в Switch как прямой children можно рендерить только Route или Redirect */} + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/features/Common/Button/styled.tsx b/src/features/Common/Button/styled.tsx index 9cfa7f83..b7543b77 100644 --- a/src/features/Common/Button/styled.tsx +++ b/src/features/Common/Button/styled.tsx @@ -35,6 +35,10 @@ export const solidButtonStyles = css` border-color: transparent; background: ${({ theme: { colors } }) => colors.primary}; + /* TODO: удалить медиа запросы из стайледа, + добавить специфичные юз кейсу правила + через враппер компоненты или др способом + */ @media ${devices.mobile} { width: 335px; height: 40px; diff --git a/src/features/Common/Checkbox/Icon.tsx b/src/features/Common/Checkbox/Icon.tsx new file mode 100644 index 00000000..40632c80 --- /dev/null +++ b/src/features/Common/Checkbox/Icon.tsx @@ -0,0 +1,53 @@ +import styled, { css } from 'styled-components/macro' + +import { devices } from 'config' + +type SvgColorStylesProps = { + checked?: boolean, +} + +export const svgColorStyles = css` + fill: ${({ checked }) => (checked ? '#ffffff' : '#B8C1CC')}; + + @media ${devices.mobile} { + fill: ${({ checked }) => (checked ? '#294FC4' : '#B8C1CC')} + } +` + +export const CheckboxSvg = styled.svg` + margin-right: 22px; + + ${svgColorStyles} +` + +type Props = { + checked?: boolean, +} + +export const Icon = ({ checked }: Props) => { + const id = checked ? '#checkbox-checked' : '#checkbox-unchecked' + return ( + + + + + + + + + ) +} diff --git a/src/features/Common/Checkbox/index.tsx b/src/features/Common/Checkbox/index.tsx index f4d1324d..9b41bd03 100644 --- a/src/features/Common/Checkbox/index.tsx +++ b/src/features/Common/Checkbox/index.tsx @@ -1,5 +1,9 @@ import { InputHTMLAttributes } from 'react' +import type { LexicsId } from 'features/LexicsStore/types' +import { T9n } from 'features/T9n' + +import { Icon } from './Icon' import { Wrapper, Input, @@ -8,28 +12,37 @@ import { type Props = Pick, ( | 'checked' + | 'className' | 'id' | 'name' | 'onClick' + | 'onChange' )> & { label?: string, + labelLexic?: LexicsId, } export const Checkbox = ({ checked, + className, id, label, + labelLexic, name, + onChange, onClick, }: Props) => ( - - - + + ) diff --git a/src/features/Common/Checkbox/styled.tsx b/src/features/Common/Checkbox/styled.tsx index b58f3d0b..ae0f8266 100644 --- a/src/features/Common/Checkbox/styled.tsx +++ b/src/features/Common/Checkbox/styled.tsx @@ -1,62 +1,27 @@ import styled from 'styled-components/macro' -import { devices } from 'config/devices' - -export const Wrapper = styled.div` - @media ${devices.tablet} { - position: absolute; - left: 0; - top: 0; - width: 162px; - height: 100px; - } -` +export const Wrapper = styled.span.attrs(() => ({ + role: 'checkbox', + tabIndex: 0, +}))`` export const Label = styled.label` display: flex; + align-items: center; color: ${({ theme: { colors } }) => colors.text}; font-style: normal; font-weight: bold; font-size: 18px; line-height: 21px; + cursor: pointer; ` -export const Input = styled.input` +export const Input = styled.input.attrs(() => ({ + 'aria-hidden': true, + tabIndex: -1, + type: 'checkbox', +}))` position: absolute; z-index: -1; opacity: 0; - - &+${Label} { - display: inline-flex; - align-items: center; - user-select: none; - } - - &+${Label}::before { - content: ''; - display: inline-block; - width: 24px; - height: 24px; - margin-right: 22px; - background-repeat: no-repeat; - background-position: center center; - background-image: url(/images/checkboxUnchecked.svg); - cursor: pointer; - } - - &:checked+${Label}::before { - background-image: url(/images/checkboxChecked.svg); - } - - @media ${devices.tablet} { - &+${Label}::before { - width: 288px; - height: 100px; - background-image: none; - } - &:checked+${Label}::before { - background-image: none; - } - - } ` diff --git a/src/features/Common/CloseButton/index.tsx b/src/features/Common/CloseButton/index.tsx deleted file mode 100644 index d4cb9d3e..00000000 --- a/src/features/Common/CloseButton/index.tsx +++ /dev/null @@ -1,18 +0,0 @@ -import styled from 'styled-components/macro' - -export const CloseButton = styled.button.attrs({ - 'aria-label': 'Close', -})` - position: absolute; - top: 0; - right: 0; - width: 10px; - height: 10px; - cursor: pointer; - border-style: none; - outline: none; - background: none; - background-image: url(/images/closeIcon.svg); - background-repeat: no-repeat; - background-size: contain; -` diff --git a/src/features/Common/Radio/Icon.tsx b/src/features/Common/Radio/Icon.tsx new file mode 100644 index 00000000..f2af3365 --- /dev/null +++ b/src/features/Common/Radio/Icon.tsx @@ -0,0 +1,39 @@ +import styled from 'styled-components/macro' + +import { svgColorStyles } from '../Checkbox/Icon' + +export const RadioSvg = styled.svg` + margin-right: 22px; + + ${svgColorStyles} +` + +type Props = { + checked?: boolean, +} + +export const Icon = ({ checked }: Props) => { + const id = checked ? '#radio-checked' : '#radio-unchecked' + return ( + + + + + + + + + ) +} diff --git a/src/features/Common/Radio/index.tsx b/src/features/Common/Radio/index.tsx index b7d91f8f..c34f028e 100644 --- a/src/features/Common/Radio/index.tsx +++ b/src/features/Common/Radio/index.tsx @@ -1,8 +1,8 @@ import { InputHTMLAttributes } from 'react' -import { useRouteMatch } from 'react-router-dom' -import { PAGES } from 'config' +import { T9n } from 'features/T9n' +import { Icon } from './Icon' import { Wrapper, Input, @@ -11,35 +11,37 @@ import { type Props = Pick, ( | 'checked' + | 'className' | 'id' | 'name' + | 'onChange' | 'onClick' )> & { label?: string, + labelLexic?: string, } export const Radio = ({ checked, + className, id, label = '', + labelLexic, name, + onChange, onClick, -}: Props) => { - const isUserAccountPage = useRouteMatch(PAGES.useraccount)?.isExact || false - - return ( - +}: Props) => ( + + - ) -} + + {labelLexic ? : label} + + +) diff --git a/src/features/Common/Radio/styled.tsx b/src/features/Common/Radio/styled.tsx index 0b6e23f2..beb17351 100644 --- a/src/features/Common/Radio/styled.tsx +++ b/src/features/Common/Radio/styled.tsx @@ -1,81 +1,27 @@ -import styled, { css } from 'styled-components/macro' +import styled from 'styled-components/macro' -import { devices } from 'config/devices' - -type WrapperProps = { - isUserAccountPage?: boolean, -} - -export const Wrapper = styled.div` - - @media ${devices.tablet} { - ${({ isUserAccountPage }) => (!isUserAccountPage - ? css` - position: absolute; - left: 0; - top: 0; - width: 163px; - height: 100px; - border-radius: 10px; - ` - : '')} - } -` +export const Wrapper = styled.span.attrs(() => ({ + role: 'radio', + tabIndex: 0, +}))`` export const Label = styled.label` + display: flex; + align-items: center; color: ${({ theme: { colors } }) => colors.text}; font-style: normal; font-weight: bold; font-size: 18px; line-height: 21px; + cursor: pointer; ` -type InputProps = { - isUserAccountPage?: boolean, -} -export const Input = styled.input` +export const Input = styled.input.attrs(() => ({ + 'aria-hidden': true, + tabIndex: -1, + type: 'radio', +}))` position: absolute; z-index: -1; opacity: 0; - - &+${Label} { - display: inline-flex; - align-items: center; - user-select: none; - } - - &+${Label}::before { - content: ''; - display: inline-block; - width: 26px; - height: 26px; - margin-right: 22px; - background-repeat: no-repeat; - background-position: center center; - background-image: url(/images/radioUnchecked.svg); - cursor: pointer; - } - - &:checked+${Label}::before { - background-image: url(/images/radioChecked.svg); - } - - @media ${devices.tablet} { - ${({ isUserAccountPage }) => (!isUserAccountPage - ? css` - &+${Label}::before { - width: 163px; - height: 100px; - border-radius: 10px; - margin-right: 0; - background-image: none; - } - - &:checked+${Label}::before { - background-image: none; - } - ` - : '')} - } - ` diff --git a/src/features/Common/index.tsx b/src/features/Common/index.tsx index 8e64ad19..a88b337d 100644 --- a/src/features/Common/index.tsx +++ b/src/features/Common/index.tsx @@ -3,7 +3,6 @@ export * from './Button' export * from './Radio' export * from './Checkbox' export * from './Arrows' -export * from './CloseButton' export * from './SportName' export * from './StarIcon' export * from './customScrollbar' diff --git a/src/features/HomePage/index.tsx b/src/features/HomePage/index.tsx index 65460d67..415bc96a 100644 --- a/src/features/HomePage/index.tsx +++ b/src/features/HomePage/index.tsx @@ -15,6 +15,7 @@ import { import { SportFilterWrapper } from 'features/ProfileHeader/styled' import { MainWrapper } from 'features/MainWrapper' import { UserFavorites } from 'features/UserFavorites' +import { MatchPopup } from 'features/MatchPopup' import { useHomePage } from './hooks' import { Header } from './components/Header' @@ -24,6 +25,7 @@ const Home = () => { const { fetchMatches } = useHomePage() return ( + diff --git a/src/features/Icons/Close/index.tsx b/src/features/Icons/Close/index.tsx new file mode 100644 index 00000000..54f86f76 --- /dev/null +++ b/src/features/Icons/Close/index.tsx @@ -0,0 +1,14 @@ +export const Close = () => ( + + + +) diff --git a/src/features/LexicsStore/hooks/useLexicsConfig.tsx b/src/features/LexicsStore/hooks/useLexicsConfig.tsx index 99a9640d..296f04c8 100644 --- a/src/features/LexicsStore/hooks/useLexicsConfig.tsx +++ b/src/features/LexicsStore/hooks/useLexicsConfig.tsx @@ -1,5 +1,7 @@ import { useState, useCallback } from 'react' +import isEmpty from 'lodash/isEmpty' + import { getObjectConfig } from 'features/LexicsStore/helpers' import type { LexicsConfig, LexicsId } from '../types' @@ -9,6 +11,8 @@ export const useLexicsConfig = () => { const addLexicsConfig = useCallback( (ids: Array | LexicsConfig) => { + if (isEmpty(ids)) return + const config = getObjectConfig(ids) setLexicsConfig((state) => ({ ...state, ...config })) }, diff --git a/src/features/LexicsStore/index.tsx b/src/features/LexicsStore/index.tsx index 9622b850..8089d8c4 100644 --- a/src/features/LexicsStore/index.tsx +++ b/src/features/LexicsStore/index.tsx @@ -5,7 +5,7 @@ import { useEffect, } from 'react' -import { LexicsConfig } from './types' +import type { LexicsConfig, LexicsId } from './types' import { useLexics } from './hooks' type Context = ReturnType @@ -20,7 +20,7 @@ export const LexicsStore = ({ children }: Props) => { export const useLexicsStore = () => useContext(LexicsContext) -export const useLexicsConfig = (config: LexicsConfig) => { +export const useLexicsConfig = (config: Array | LexicsConfig) => { const { addLexicsConfig } = useLexicsStore() useEffect(() => { diff --git a/src/features/LexicsStore/types.tsx b/src/features/LexicsStore/types.tsx index aaa77b05..f98655d0 100644 --- a/src/features/LexicsStore/types.tsx +++ b/src/features/LexicsStore/types.tsx @@ -1,3 +1,3 @@ -export type LexicsId = string +export type LexicsId = string | number export type LexicsConfig = {[lexicsId: string]: number} diff --git a/src/features/MatchCard/CardBackside/index.tsx b/src/features/MatchCard/CardBackside/index.tsx deleted file mode 100644 index a8ccfb1a..00000000 --- a/src/features/MatchCard/CardBackside/index.tsx +++ /dev/null @@ -1,58 +0,0 @@ -import type { MouseEvent } from 'react' - -import { Link } from 'react-router-dom' - -import type { Match } from 'features/Matches' -import { OutsideClick } from 'features/OutsideClick' - -import { - CardHoverInner, - CardHoverTitle, - CardHoverWrapper, - MoreVideo, - Row, - Rows, -} from '../styled' - -type Props = { - match: Match, - onClose: () => void, -} - -const stopProp = (e: MouseEvent) => { - e.stopPropagation() -} - -export const CardBackside = ({ - match: { - id, - sportName, - }, - onClose, -}: Props) => ( - - - - - - - - - - - - - - - - - - - - - - - - - -) diff --git a/src/features/MatchCard/hooks.tsx b/src/features/MatchCard/hooks.tsx index f3c1a49c..94f6e79f 100644 --- a/src/features/MatchCard/hooks.tsx +++ b/src/features/MatchCard/hooks.tsx @@ -1,33 +1,31 @@ import type { KeyboardEvent } from 'react' import { useCallback } from 'react' -import { useToggle } from 'hooks' - import type { Match } from 'features/Matches' +import { useMatchPopupStore } from 'features/MatchPopup' export const useCard = (match: Match) => { - const { - close, - isOpen, - open, - } = useToggle() + const { openPopup, setMatch } = useMatchPopupStore() - const flipCard = useCallback(() => { + const openMatchPopup = useCallback(() => { if (match.isClickable) { - open() + setMatch(match) + openPopup() } - }, [match, open]) + }, [ + match, + openPopup, + setMatch, + ]) const onKeyPress = useCallback((e: KeyboardEvent) => { if (e.key === 'Enter') { - flipCard() + openMatchPopup() } - }, [flipCard]) + }, [openMatchPopup]) return { - close, - flipCard, - isOpen, onKeyPress, + openMatchPopup, } } diff --git a/src/features/MatchCard/index.tsx b/src/features/MatchCard/index.tsx index 9846122f..46fdd3c4 100644 --- a/src/features/MatchCard/index.tsx +++ b/src/features/MatchCard/index.tsx @@ -5,7 +5,6 @@ import { PAGES } from 'config' import type { Match } from 'features/Matches' import { CardFrontside } from './CardFrontside' -import { CardBackside } from './CardBackside' import { useCard } from './hooks' type Props = { @@ -15,26 +14,15 @@ type Props = { export const MatchCard = ({ match }: Props) => { const isHomePage = useRouteMatch(PAGES.home)?.isExact const { - close, - flipCard, - isOpen, onKeyPress, + openMatchPopup, } = useCard(match) - if (isOpen) { - return ( - - ) - } - return ( ) diff --git a/src/features/MatchCard/styled.tsx b/src/features/MatchCard/styled.tsx index 13381153..24c7ad2e 100644 --- a/src/features/MatchCard/styled.tsx +++ b/src/features/MatchCard/styled.tsx @@ -2,7 +2,6 @@ import styled from 'styled-components/macro' import { devices } from 'config/devices' -import { T9n } from 'features/T9n' import { Name } from 'features/Name' import { ProfileLogo } from 'features/ProfileLogo' @@ -142,65 +141,6 @@ export const Score = styled.div` width: 10%; ` -export const Rows = styled.div` - width: fit-content; - margin-top: 20px; -` - -export const Row = styled.div` - white-space: nowrap; -` - -export const MoreVideo = styled(T9n)` - display: inline-block; - margin: 0 8px 8px 0; - padding: 8px; - border-radius: 2px; - font-weight: 500; - font-size: 11px; - text-align: center; - color: rgba(255, 255, 255, 0.5); - background: linear-gradient( - 180deg, - rgba(255, 255, 255, 0.1) 0%, - rgba(255, 255, 255, 0) 100% - ), - #5C5C5C; - box-shadow: 0px 1px 0px rgba(0, 0, 0, 0.1); - cursor: pointer; - - :hover { - background: rgba(153, 153, 153, 0.9); - color: #fff; - } -` - -export const CardHoverWrapper = styled(CardWrapper)` - padding: 16px 24px; - cursor: pointer; - height: 288px; - - @media ${devices.laptop} { - height: 279px; - } - @media ${devices.tablet} { - height: 299px; - } -` - -export const CardHoverInner = styled.div` - position: relative; - overflow: hidden; - width: fit-content; -` - -export const CardHoverTitle = styled(T9n)` - font-size: 10px; - letter-spacing: 0.03em; - text-transform: uppercase; - color: rgba(255, 255, 255, 0.5); -` - export const TeamLogos = styled.div` display: flex; padding-left: 24px; diff --git a/src/features/MatchPage/hooks/useVideoData.tsx b/src/features/MatchPage/hooks/useVideoData.tsx index 7c905f4e..92709609 100644 --- a/src/features/MatchPage/hooks/useVideoData.tsx +++ b/src/features/MatchPage/hooks/useVideoData.tsx @@ -4,9 +4,6 @@ import { useState, } from 'react' -import isEmpty from 'lodash/isEmpty' -import filter from 'lodash/filter' - import type { LiveVideos, Videos } from 'requests' import { getLiveVideos, getVideos } from 'requests' @@ -14,21 +11,14 @@ import { useSportNameParam, usePageId } from 'hooks' import { useLastPlayPosition } from './useLastPlayPosition' -const filterByIds = (videos: Videos) => { - const zeroIdVideos = filter(videos, { abc: '0' }) - return isEmpty(zeroIdVideos) ? videos : zeroIdVideos -} - export const useVideoData = () => { const [videos, setVideos] = useState([]) const [liveVideos, setLiveVideos] = useState([]) const { sportType } = useSportNameParam() const matchId = usePageId() - const fetchMatchVideos = useCallback(async () => { - const videosResponse = await getVideos(sportType, matchId) - const filteredVideosResponseByAbc = filterByIds(videosResponse) - setVideos(filteredVideosResponseByAbc) + const fetchMatchVideos = useCallback(() => { + getVideos(sportType, matchId).then(setVideos) }, [sportType, matchId]) useEffect(() => { diff --git a/src/features/MatchPopup/components/ApplyButton/index.tsx b/src/features/MatchPopup/components/ApplyButton/index.tsx new file mode 100644 index 00000000..42af40d9 --- /dev/null +++ b/src/features/MatchPopup/components/ApplyButton/index.tsx @@ -0,0 +1,28 @@ +import styled from 'styled-components/macro' + +import { T9n } from 'features/T9n' +import { ButtonSolid } from 'features/Common' + +const Button = styled(ButtonSolid)` + position: absolute; + bottom: 46px; + left: 12px; + width: calc(100% - 24px); + height: 44px; + background-color: #294FC4; + border-radius: 5px; + font-weight: 600; + font-size: 17px; + line-height: 22px; + letter-spacing: -0.408px; +` + +type Props = { + onClick: () => void, +} + +export const ApplyButton = ({ onClick }: Props) => ( + +) diff --git a/src/features/MatchPopup/components/BackButton/index.tsx b/src/features/MatchPopup/components/BackButton/index.tsx new file mode 100644 index 00000000..c495cf6a --- /dev/null +++ b/src/features/MatchPopup/components/BackButton/index.tsx @@ -0,0 +1,16 @@ +import styled from 'styled-components/macro' + +import { useMatchPopupStore } from 'features/MatchPopup/store' + +import { BaseButton } from '../../styled' + +const Button = styled(BaseButton)` + background-image: url(/images/back-icon.svg); +` + +export const BackButton = () => { + const { goBack } = useMatchPopupStore() + return ( +