сhange event listeners to hook (#156)

* refactor: micro refactor of useEventListener hook

* refactor: changed addEventListener to useEventListener hook

* refactor: renamed props type names, TComponent -> Props

* refactor: removed customStyles prop from T9n

Co-authored-by: mirlan.maksitaliev <mirlan.maksitaliev@instatsport.com>
keep-around/af30b88d367751c9e05a735e4a0467a96238ef47
Mirlan 5 years ago committed by GitHub
parent 14cbfaf47e
commit e5e1c032fd
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 4
      src/features/Background/index.tsx
  2. 4
      src/features/Combobox/types.tsx
  3. 4
      src/features/Common/Checkbox/index.tsx
  4. 4
      src/features/Common/Radio/index.tsx
  5. 8
      src/features/Common/customStyles/index.tsx
  6. 4
      src/features/Logo/index.tsx
  7. 5
      src/features/Menu/index.tsx
  8. 4
      src/features/Menu/styled.tsx
  9. 4
      src/features/Modal/index.tsx
  10. 4
      src/features/MultiSourcePlayer/hooks/index.tsx
  11. 4
      src/features/MultiSourcePlayer/hooks/useProgressEvents.tsx
  12. 31
      src/features/OutsideClick/hooks/index.tsx
  13. 4
      src/features/Register/components/AdditionalSubscription/index.tsx
  14. 4
      src/features/Register/components/MainSubscription/index.tsx
  15. 4
      src/features/Register/components/Price/index.tsx
  16. 6
      src/features/Search/components/Header/index.tsx
  17. 6
      src/features/Search/components/Header/styled.tsx
  18. 98
      src/features/StreamPlayer/hooks/useSlider.tsx
  19. 16
      src/features/T9n/index.tsx
  20. 8
      src/features/Theme/config.tsx
  21. 8
      src/features/Theme/index.tsx
  22. 4
      src/features/UserAccount/CardNumber/index.tsx
  23. 12
      src/features/UserAccount/CardNumber/styled.tsx
  24. 4
      src/features/UserAccount/PageTitle/index.tsx
  25. 4
      src/features/UserAccount/TextNoBorder/index.tsx
  26. 4
      src/features/UserAccount/UserAccountSubscription/index.tsx
  27. 10
      src/features/UserAccount/UserAccountSubscription/styled.tsx
  28. 6
      src/features/UserAccount/VisaLogo/styled.tsx
  29. 35
      src/hooks/useEventListener.tsx
  30. 4
      src/types/styled-components.d.ts

@ -2,11 +2,11 @@ import React, { ReactNode } from 'react'
import { GradientBackground, ImageBackground } from './styled'
type TBackground = {
type Props = {
children: ReactNode,
}
export const Background = ({ children }: TBackground) => (
export const Background = ({ children }: Props) => (
<ImageBackground>
<GradientBackground>
{children}

@ -4,7 +4,7 @@ import type {
ReactNode,
} from 'react'
import type { TCustomStyles } from 'features/Common'
import type { CustomStyles } from 'features/Common'
export type Option = {
children?: ReactNode,
@ -22,7 +22,7 @@ export type Props<T> = Pick<InputHTMLAttributes<HTMLInputElement>, (
| 'placeholder'
| 'readOnly'
)> & {
customListStyles?: TCustomStyles,
customListStyles?: CustomStyles,
error?: string | null,
filterOptions?: boolean,
label?: string,

@ -6,7 +6,7 @@ import {
Label,
} from './styled'
type TCheckbox = Pick<InputHTMLAttributes<HTMLInputElement>, (
type Props = Pick<InputHTMLAttributes<HTMLInputElement>, (
| 'checked'
| 'id'
| 'name'
@ -23,7 +23,7 @@ export const Checkbox = ({
name,
onChange,
value,
}: TCheckbox) => (
}: Props) => (
<Wrapper>
<Input
id={id}

@ -6,7 +6,7 @@ import {
Label,
} from './styled'
type TCheckbox = Pick<InputHTMLAttributes<HTMLInputElement>, (
type Props = Pick<InputHTMLAttributes<HTMLInputElement>, (
| 'checked'
| 'id'
| 'name'
@ -23,7 +23,7 @@ export const Radio = ({
name,
onChange,
value,
}: TCheckbox) => (
}: Props) => (
<Wrapper>
<Input
id={id}

@ -1,11 +1,11 @@
import { css } from 'styled-components'
export type TCustomStyles = string | ReturnType<typeof css>
export type CustomStyles = string | ReturnType<typeof css>
type TCustomStylesMixin = {
customstyles?: TCustomStyles,
type CustomStylesMixin = {
customstyles?: CustomStyles,
}
export const customStylesMixin = css<TCustomStylesMixin>`
export const customStylesMixin = css<CustomStylesMixin>`
${({ customstyles }) => customstyles}
`

@ -2,12 +2,12 @@ import styled from 'styled-components/macro'
import { devices } from 'config/devices'
type TLogo = {
type Props = {
height?: number,
width?: number,
}
export const Logo = styled.div<TLogo>`
export const Logo = styled.div<Props>`
display: block;
width: ${({ width = 174 }) => width}px;
height: ${({ height = 40 }) => height}px;

@ -15,7 +15,7 @@ import {
MenuItem,
Icon,
StyledLink,
linkStyles,
Title,
} from './styled'
export const Menu = () => {
@ -43,10 +43,9 @@ export const Menu = () => {
</MenuItem>
<MenuItem>
<Icon image='logout' />
<T9n
<Title
t='logout'
onClick={logout}
customStyles={linkStyles}
/>
</MenuItem>
</MenuList>

@ -4,6 +4,8 @@ import styled, { css } from 'styled-components/macro'
import { devices } from 'config/devices'
import { T9n } from 'features/T9n'
export const Wrapper = styled.nav`
position: relative;
display: flex;
@ -88,4 +90,6 @@ export const linkStyles = css`
color: #ccc;
`
export const Title = styled(T9n)`${linkStyles}`
export const StyledLink = styled(Link)`${linkStyles}`

@ -11,7 +11,7 @@ import {
ModalCloseButton,
} from './styled'
type TModalProps = {
type Props = {
children: ReactNode,
close: () => void,
isOpen: boolean,
@ -21,7 +21,7 @@ export const Modal = ({
children,
close,
isOpen,
}: TModalProps) => {
}: Props) => {
const modalRoot = useRef(document.getElementById('modal-root'))
return isOpen

@ -131,13 +131,13 @@ export const useMultiSourcePlayer = ({
useEventListener({
callback: playNextChapter,
event: 'ended',
ref: videoRef,
target: videoRef,
})
useEventListener({
callback: onError,
event: 'error',
ref: videoRef,
target: videoRef,
})
useEffect(() => {

@ -42,7 +42,7 @@ export const useLoadedEvent = ({
useEventListener({
callback: listenLoadedProgress,
event: 'progress',
ref: videoRef,
target: videoRef,
})
}
@ -63,6 +63,6 @@ export const usePlayedEvent = ({
useEventListener({
callback: listenPlayedProgresses,
event: 'timeupdate',
ref: videoRef,
target: videoRef,
})
}

@ -1,30 +1,27 @@
import { useEffect, useRef } from 'react'
import { useRef } from 'react'
type TUseOutsideClickEffect = {
import { useEventListener } from 'hooks'
type Args = {
onClick: Function,
}
export const useOutsideClickEffect = ({
onClick,
}: TUseOutsideClickEffect) => {
}: Args) => {
const wrapperRef = useRef<HTMLDivElement>(null)
useEffect(() => {
const handleOutsideClick = ({ target }: MouseEvent) => {
const targetNode = target instanceof Node
? target
: null
const handleOutsideClick = ({ target }: MouseEvent) => {
const targetNode = target instanceof Node
? target
: null
/** деструктуризация wrapperRef не сработает, ссылка на реф теряется */
if (!wrapperRef.current?.contains(targetNode)) {
onClick()
}
/** деструктуризация wrapperRef не сработает, ссылка на реф теряется */
if (!wrapperRef.current?.contains(targetNode)) {
onClick()
}
}
window.addEventListener('click', handleOutsideClick)
useEventListener({ callback: handleOutsideClick, event: 'click' })
return () => {
window.removeEventListener('click', handleOutsideClick)
}
}, [onClick, wrapperRef])
return wrapperRef
}

@ -9,12 +9,12 @@ import {
PriceItemCol,
} from './styled'
type TAdditionalSubscription = {
type Props = {
price: number,
title: string,
}
export const AdditionalSubscription = ({ price, title }: TAdditionalSubscription) => (
export const AdditionalSubscription = ({ price, title }: Props) => (
<PriceItem>
<PriceItemCol>
<Checkbox name='additionalSubscription' id={title} />

@ -9,12 +9,12 @@ import {
Row,
} from '../SubscriptionsStep/styled'
type TMainSubscription = {
type Props = {
price: number,
title: string,
}
export const MainSubscription = ({ price, title }: TMainSubscription) => (
export const MainSubscription = ({ price, title }: Props) => (
<SubscriptionWrapper>
<Row>
<Radio name='mainSubscription' id={title} />

@ -7,7 +7,7 @@ import {
PriceDetails,
} from './styled'
type TPrice = {
type Props = {
amount: number,
currency?: string,
perPeriod?: string,
@ -17,7 +17,7 @@ export const Price = ({
amount,
currency = '₽',
perPeriod = 'month',
}: TPrice) => {
}: Props) => {
const { translate } = useLexicsStore()
const perPeriodTranslated = translate(perPeriod)

@ -1,11 +1,9 @@
import React from 'react'
import { T9n } from 'features/T9n'
import {
Wrapper,
Icon,
titleStyles,
Title,
} from './styled'
type HeaderProps = {
@ -16,6 +14,6 @@ type HeaderProps = {
export const Header = ({ image, title }: HeaderProps) => (
<Wrapper>
<Icon image={image} />
<T9n t={title} customStyles={titleStyles} />
<Title t={title} />
</Wrapper>
)

@ -1,4 +1,6 @@
import styled, { css } from 'styled-components/macro'
import styled from 'styled-components/macro'
import { T9n } from 'features/T9n'
export const Wrapper = styled.div`
display: flex;
@ -17,7 +19,7 @@ export const Icon = styled.div<{ image: string }>`
background-repeat: no-repeat;
`
export const titleStyles = css`
export const Title = styled(T9n)`
letter-spacing: 0.02em;
text-transform: uppercase;
color: #fff;

@ -1,8 +1,6 @@
import {
useCallback,
useEffect,
useRef,
} from 'react'
import { useRef } from 'react'
import { useEventListener, useToggle } from 'hooks'
const getNormalizedProgress = (fraction: number) => {
if (fraction > 1) return 1
@ -10,22 +8,6 @@ const getNormalizedProgress = (fraction: number) => {
return fraction
}
const useMouseState = () => {
const mouseDownRef = useRef(false)
const setMouseDown = useCallback(() => {
mouseDownRef.current = true
}, [])
const setMouseUp = useCallback(() => {
mouseDownRef.current = false
}, [])
return {
mouseDownRef,
setMouseDown,
setMouseUp,
}
}
type Args = {
onChange: (progress: number) => void,
}
@ -33,49 +15,45 @@ type Args = {
export const useSlider = ({ onChange }: Args) => {
const ref = useRef<HTMLDivElement>(null)
const {
mouseDownRef,
setMouseDown,
setMouseUp,
} = useMouseState()
useEffect(() => {
const handleProgress = (mouseX: number) => {
const track = ref.current
if (!mouseDownRef.current || !track) return
const x = mouseX - track.getBoundingClientRect().left
const progress = getNormalizedProgress(x / track.offsetWidth)
onChange(progress)
}
close: setMouseDown,
isOpen: isMouseUp,
open: setMouseUp,
} = useToggle(true)
const handleProgress = (mouseX: number) => {
const track = ref.current
if (!track) return
const x = mouseX - track.getBoundingClientRect().left
const progress = getNormalizedProgress(x / track.offsetWidth)
onChange(progress)
}
const mouseDownListener = ({ clientX, target }: MouseEvent) => {
const track = ref.current
if (target === track || track?.contains(target as Node)) {
setMouseDown()
handleProgress(clientX)
}
const mouseDownListener = ({ clientX, target }: MouseEvent) => {
const track = ref.current
if (target === track || track?.contains(target as Node)) {
setMouseDown()
handleProgress(clientX)
}
}
const mouseUpListener = setMouseUp
const mouseMoveListener = (e: MouseEvent) => {
handleProgress(e.clientX)
}
const mouseMoveListener = (e: MouseEvent) => {
if (isMouseUp) return
handleProgress(e.clientX)
}
window.addEventListener('mousedown', mouseDownListener)
window.addEventListener('mouseup', mouseUpListener)
window.addEventListener('mousemove', mouseMoveListener)
return () => {
window.removeEventListener('mousedown', mouseDownListener)
window.removeEventListener('mouseup', mouseUpListener)
window.removeEventListener('mousemove', mouseMoveListener)
}
}, [
onChange,
mouseDownRef,
setMouseDown,
setMouseUp,
])
useEventListener({
callback: mouseDownListener,
event: 'mousedown',
})
useEventListener({
callback: setMouseUp,
event: 'mouseup',
})
useEventListener({
callback: mouseMoveListener,
event: 'mousemove',
})
return ref
}

@ -1,32 +1,26 @@
import React from 'react'
import styled, { css } from 'styled-components/macro'
import styled from 'styled-components/macro'
import { useLexicsStore } from 'features/LexicsStore'
type TCustomStyles = { customStyles?: string | ReturnType<typeof css> }
const Text = styled.span``
const Text = styled.span<TCustomStyles>`
${({ customStyles }) => customStyles}
`
type T9nProps = {
type Props = {
className?: string,
onClick?: () => void,
t: string | number,
} & TCustomStyles
}
export const T9n = ({
className,
customStyles,
onClick,
t,
}: T9nProps) => {
}: Props) => {
const { translate } = useLexicsStore()
return (
<Text
onClick={onClick}
customStyles={customStyles}
className={className}
>
{translate(String(t))}

@ -5,7 +5,7 @@ export const lightTheme = {
secondary: '',
text: '',
},
name: 'light' as TName,
name: 'light' as Name,
switchTheme: () => {},
}
@ -31,10 +31,10 @@ export const darkTheme = {
secondary: '#999999',
text: '#fff',
},
name: 'dark' as TName,
name: 'dark' as Name,
switchTheme: () => {},
}
type TName = 'light' | 'dark'
type Name = 'light' | 'dark'
export type TCustomTheme = typeof lightTheme
export type CustomTheme = typeof lightTheme

@ -7,17 +7,17 @@ import React, {
import { ThemeProvider } from 'styled-components'
import {
TCustomTheme,
CustomTheme,
lightTheme,
darkTheme,
} from './config'
type TThemeProps = {
type Props = {
children: ReactNode,
}
export const Theme = ({ children }: TThemeProps) => {
const [theme, setTheme] = useState<TCustomTheme>(darkTheme)
export const Theme = ({ children }: Props) => {
const [theme, setTheme] = useState<CustomTheme>(darkTheme)
const switchTheme = useCallback(
() => {

@ -6,7 +6,7 @@ import { T9n } from 'features/T9n'
import { VisaLogoWrapper } from '../VisaLogo'
import { CardNumberWrapper, CardNumberTextWrapper } from './styled'
type TUserAccountInput = {
type Props = {
checked?: boolean,
label: string,
visa?: boolean,
@ -16,7 +16,7 @@ export const CardNumber = ({
checked,
label,
visa,
}: TUserAccountInput) => (
}: Props) => (
<CardNumberWrapper>
<Radio
label={label}

@ -12,11 +12,11 @@ export const CardNumberWrapper = styled.div`
border-radius: 2px;
margin: 20px 0;
width: 100%;
${Label} {
font-size: 20px;
line-height: 24px;
&::before {
margin-left: 22px;
}
@ -32,7 +32,7 @@ export const TextWrapper = styled.p`
color: #666666;
white-space: nowrap;
margin-right: 0;
&:hover {
cursor: pointer;
}
@ -42,11 +42,11 @@ export const CardNumberTextWrapper = styled(TextWrapper)`
margin-right: 24px;
`
export type TPriceWrapper = {
export type Props = {
noMarginRight?: boolean,
}
export const priceWrapperStyles = css<TPriceWrapper>`
export const priceWrapperStyles = css<Props>`
margin-left: auto;
margin-right: 24px;
@ -54,7 +54,7 @@ export const priceWrapperStyles = css<TPriceWrapper>`
font-size: 24px;
line-height: 21px;
}
${PriceDetails} {
font-size: 12px;
line-height: 12px;

@ -5,11 +5,11 @@ import { BlockTitle } from 'features/Login/styled'
import { PageTitleWrapper, TitleTextWrapper } from './styled'
type TPageTitle = {
type Props = {
titleText: string,
}
export const PageTitle = ({ titleText }: TPageTitle) => (
export const PageTitle = ({ titleText }: Props) => (
<PageTitleWrapper>
<Logo />
<TitleTextWrapper>

@ -5,7 +5,7 @@ import { Price } from 'features/Register/components/Price'
import { TextNoBorderWrapper, TextNoBorderTextWrapper } from './styled'
import { PriceWrapper } from '../CardNumber/styled'
type TTextNoBorder = {
type Props = {
amount: number,
text: string,
}
@ -13,7 +13,7 @@ type TTextNoBorder = {
export const TextNoBorder = ({
amount,
text,
}: TTextNoBorder) => (
}: Props) => (
<TextNoBorderWrapper>
<TextNoBorderTextWrapper>{text}</TextNoBorderTextWrapper>
<PriceWrapper>

@ -12,7 +12,7 @@ import {
} from './styled'
import { PriceWrapper } from '../CardNumber/styled'
type TUserAccountSubscription = {
type Props = {
amount: number,
checked?: boolean,
inputType?: string,
@ -32,7 +32,7 @@ export const UserAccountSubscription = ({
noMarginTop,
packageAction,
packageName,
}: TUserAccountSubscription) => (
}: Props) => (
<UserAccountSubscriptionWrapper
noMarginTop={noMarginTop}
noMarginBottom={noMarginBottom}

@ -5,12 +5,12 @@ import { Label as RadioLabel } from 'features/Common/Radio/styled'
import { TextWrapper } from '../CardNumber/styled'
export type TUserAccountSubscriptionWrapper = {
type Props = {
noMarginBottom?: boolean,
noMarginTop?: boolean,
}
export const UserAccountSubscriptionWrapperStyles = css<TUserAccountSubscriptionWrapper>`
export const UserAccountSubscriptionWrapperStyles = css<Props>`
display: flex;
align-items: center;
justify-content: flex-start;
@ -33,7 +33,7 @@ export const UserAccountSubscriptionWrapperStyles = css<TUserAccountSubscription
margin-top: ${({ noMarginTop }) => (noMarginTop ? '0' : '20px')};
margin-bottom: ${({ noMarginBottom }) => (noMarginBottom ? '0' : '20px')};
width: 100%;
${RadioLabel}::before {
margin-left: 22px;
}
@ -41,7 +41,7 @@ export const UserAccountSubscriptionWrapperStyles = css<TUserAccountSubscription
export const UserAccountSubscriptionWrapper = styled.div`
${UserAccountSubscriptionWrapperStyles};
&:nth-child(n+1) {
border-bottom: ${({ noMarginBottom }) => (noMarginBottom ? '1px solid #000' : '')};
border-top: ${({ noMarginBottom, noMarginTop }) => (noMarginBottom && noMarginTop ? 'none' : '')};
@ -53,7 +53,7 @@ export const CheckboxWrapper = styled.div`
font-weight: bold;
font-size: 14px;
line-height: 24px;
&::before {
margin-left: 22px;
}

@ -1,16 +1,16 @@
import styled, { css } from 'styled-components/macro'
type TCardLogoStyled = {
type Props = {
visa?: boolean,
}
export const CardLogoStyles = css<TCardLogoStyled>`
export const CardLogoStyles = css<Props>`
width: ${({ visa }) => `${visa ? 37 : 30}px`};
height: ${({ visa }) => `${visa ? 12 : 19}px`};
margin-right: 82px;
background: ${({ visa }) => `url(/images/${visa ? 'visaLogo.png' : 'masterLogo.png'}) no-repeat`};
`
export const VisaLogoWrapper = styled.span<TCardLogoStyled>`
export const VisaLogoWrapper = styled.span<Props>`
${CardLogoStyles}
`

@ -1,22 +1,24 @@
import type { RefObject } from 'react'
import { useEffect, useRef } from 'react'
type Args<E> = {
callback: (e: Event) => void,
element?: HTMLElement,
type EventMap = HTMLElementEventMap | WindowEventMap
type Target = RefObject<HTMLElement> | HTMLElement | Window
type Args<E extends keyof EventMap> = {
callback: (e: EventMap[E]) => void,
event: E,
ref?: RefObject<HTMLElement>,
target?: Target,
}
/**
* Хук для подписки и отписки на события
* на html element, window и react ref
* на html element и window
*/
export const useEventListener = <E extends keyof HTMLElementEventMap>({
export const useEventListener = <E extends keyof EventMap>({
callback,
element,
event,
ref,
target = window,
}: Args<E>) => {
const callbackRef = useRef(callback)
@ -25,15 +27,20 @@ export const useEventListener = <E extends keyof HTMLElementEventMap>({
}, [callback])
useEffect(() => {
const htmlElement = ref?.current || element || window
if (!htmlElement) return undefined
const windowOrElement: HTMLElement | Window | null = 'current' in target
? target.current
: target
const listener = (e: Event) => {
// типизировать сложно без any из за того что
// у window есть события которых нет у html element
// и наоборот
const listener = (e: any) => {
callbackRef.current(e)
}
htmlElement.addEventListener(event, listener)
windowOrElement?.addEventListener(event, listener)
return () => {
htmlElement.removeEventListener(event, listener)
windowOrElement?.removeEventListener(event, listener)
}
}, [event, ref, element])
}, [event, target])
}

@ -1,5 +1,5 @@
import { TCustomTheme } from '../features/Theme/config'
import { CustomTheme } from '../features/Theme/config'
declare module 'styled-components' {
export interface DefaultTheme extends TCustomTheme {}
export interface DefaultTheme extends CustomTheme {}
}

Loading…
Cancel
Save