diff --git a/.eslintrc b/.eslintrc
index ba2e2041..a3ce36ee 100644
--- a/.eslintrc
+++ b/.eslintrc
@@ -84,6 +84,7 @@
"indent": "off",
"no-unused-vars": "off",
"react/jsx-one-expression-per-line": "off",
+ "react/jsx-fragments": "off",
"semi": "off"
}
}
diff --git a/Makefile b/Makefile
index 918723a9..5e3ad19c 100644
--- a/Makefile
+++ b/Makefile
@@ -11,6 +11,7 @@ build:
.PHONY: build
stage: build
+ rsync -zavP build/ -e 'ssh -p 666' ott-staging@85.10.224.24:/usr/local/www/ott-staging
test:
npm test
diff --git a/package.json b/package.json
index 54a5925e..a1076adc 100644
--- a/package.json
+++ b/package.json
@@ -16,7 +16,8 @@
"lodash": "^4.17.15",
"react": "^16.13.1",
"react-dom": "^16.13.1",
- "react-scripts": "3.4.1"
+ "react-scripts": "3.4.1",
+ "styled-components": "^5.1.1"
},
"devDependencies": {
"@commitlint/cli": "^8.3.5",
@@ -36,6 +37,7 @@
"@types/node": "^12.0.0",
"@types/react": "^16.9.0",
"@types/react-dom": "^16.9.0",
+ "@types/styled-components": "^5.1.0",
"commitizen": "^4.1.2",
"eslint": "6.8.0",
"eslint-config-airbnb": "18.1.0",
diff --git a/public/images/arrowLeft.svg b/public/images/arrowLeft.svg
new file mode 100644
index 00000000..dfa7c5d0
--- /dev/null
+++ b/public/images/arrowLeft.svg
@@ -0,0 +1,6 @@
+
diff --git a/public/images/arrowRight.svg b/public/images/arrowRight.svg
new file mode 100644
index 00000000..7875155b
--- /dev/null
+++ b/public/images/arrowRight.svg
@@ -0,0 +1,6 @@
+
diff --git a/public/images/checkboxChecked.svg b/public/images/checkboxChecked.svg
new file mode 100644
index 00000000..bcf8ad02
--- /dev/null
+++ b/public/images/checkboxChecked.svg
@@ -0,0 +1,24 @@
+
diff --git a/public/images/checkboxUnchecked.svg b/public/images/checkboxUnchecked.svg
new file mode 100644
index 00000000..e69c317e
--- /dev/null
+++ b/public/images/checkboxUnchecked.svg
@@ -0,0 +1,17 @@
+
diff --git a/public/images/radioChecked.svg b/public/images/radioChecked.svg
new file mode 100644
index 00000000..27cf636c
--- /dev/null
+++ b/public/images/radioChecked.svg
@@ -0,0 +1,34 @@
+
diff --git a/public/images/radioUnchecked.svg b/public/images/radioUnchecked.svg
new file mode 100644
index 00000000..e14a682f
--- /dev/null
+++ b/public/images/radioUnchecked.svg
@@ -0,0 +1,17 @@
+
diff --git a/public/index.html b/public/index.html
index 4fb02265..e91d53dd 100644
--- a/public/index.html
+++ b/public/index.html
@@ -6,6 +6,7 @@
+
Instat TV
diff --git a/src/features/App/index.tsx b/src/features/App/index.tsx
index 9eded704..f9857eb4 100644
--- a/src/features/App/index.tsx
+++ b/src/features/App/index.tsx
@@ -1,7 +1,9 @@
import React from 'react'
+import { Theme } from 'features/Theme'
+
export const App = () => (
-
+
Instat TV
-
+
)
diff --git a/src/features/Button/index.tsx b/src/features/Button/index.tsx
deleted file mode 100644
index 3a2a000d..00000000
--- a/src/features/Button/index.tsx
+++ /dev/null
@@ -1,19 +0,0 @@
-import React, { ReactNode } from 'react'
-
-type ButtonProps = {
- children: ReactNode,
-
- /**
- * Simple click handler
- */
- onClick?: () => void,
-}
-
-/**
- * The world's most _basic_ button
- */
-export const Button = ({ children, onClick }: ButtonProps) => (
-
-)
diff --git a/src/features/Button/stories.tsx b/src/features/Button/stories.tsx
deleted file mode 100644
index bce6188c..00000000
--- a/src/features/Button/stories.tsx
+++ /dev/null
@@ -1,20 +0,0 @@
-import React from 'react'
-
-import { action } from '@storybook/addon-actions'
-
-import { Button } from 'features/Button'
-
-export default {
- component: Button,
- title: 'Button',
-}
-
-export const Text = () =>
-
-export const Emoji = () => (
-
-)
diff --git a/src/features/Common/Arrows/index.tsx b/src/features/Common/Arrows/index.tsx
new file mode 100644
index 00000000..4302a0c0
--- /dev/null
+++ b/src/features/Common/Arrows/index.tsx
@@ -0,0 +1,28 @@
+import styled from 'styled-components/macro'
+
+const ArrowStyled = styled.button`
+ width: 40px;
+ height: 40px;
+ padding: 0;
+ border: 1px solid transparent;
+ border-radius: 50%;
+ background-color: transparent;
+ background-position: center;
+ outline: none;
+
+ :active {
+ background-color: rgba(117, 117, 117, 1);
+ }
+
+ :hover, :focus {
+ background-color: rgba(117, 117, 117, 0.5);
+ }
+`
+
+export const ArrowLeft = styled(ArrowStyled)`
+ background-image: url(/images/arrowLeft.svg);
+`
+
+export const ArrowRight = styled(ArrowStyled)`
+ background-image: url(/images/arrowRight.svg);
+`
diff --git a/src/features/Common/Arrows/stories.tsx b/src/features/Common/Arrows/stories.tsx
new file mode 100644
index 00000000..f2e61f8a
--- /dev/null
+++ b/src/features/Common/Arrows/stories.tsx
@@ -0,0 +1,21 @@
+import React from 'react'
+
+import { ArrowLeft, ArrowRight } from 'features/Common'
+
+export default {
+ component: ArrowLeft,
+ title: 'Arrows',
+}
+
+const backgroundStyles = {
+ backgroundColor: '#333',
+ height: '200px',
+ padding: '20px',
+}
+
+export const Group = () => (
+
+)
diff --git a/src/features/Common/Button/index.tsx b/src/features/Common/Button/index.tsx
new file mode 100644
index 00000000..9bc65a52
--- /dev/null
+++ b/src/features/Common/Button/index.tsx
@@ -0,0 +1 @@
+export * from './styled'
diff --git a/src/features/Common/Button/stories.tsx b/src/features/Common/Button/stories.tsx
new file mode 100644
index 00000000..0c58bfca
--- /dev/null
+++ b/src/features/Common/Button/stories.tsx
@@ -0,0 +1,21 @@
+import React from 'react'
+
+import { action } from '@storybook/addon-actions'
+
+import { ButtonOutline, ButtonSolid } from 'features/Common'
+
+export default {
+ title: 'Button',
+}
+
+export const Solid = () => (
+
+ Solid
+
+)
+
+export const Outline = () => (
+
+ Outline
+
+)
diff --git a/src/features/Common/Button/styled.tsx b/src/features/Common/Button/styled.tsx
new file mode 100644
index 00000000..04a2c45a
--- /dev/null
+++ b/src/features/Common/Button/styled.tsx
@@ -0,0 +1,44 @@
+import styled, { css } from 'styled-components/macro'
+
+const baseButtonStyles = css`
+ width: 272px;
+ height: 48px;
+ border-width: 0.7px;
+ border-style: solid;
+ border-radius: 2px;
+ padding: 0 12px;
+
+ font-style: normal;
+ font-size: 20px;
+ outline-color: white;
+`
+
+export const outlineButtonStyles = css`
+ ${baseButtonStyles}
+
+ padding-top: 8.6px;
+ padding-bottom: 10.6px;
+ color: ${({ theme: { colors } }) => colors.secondary};
+ font-weight: normal;
+ border-color: ${({ theme: { colors } }) => colors.secondary};
+ background: transparent;
+`
+
+export const solidButtonStyles = css`
+ ${baseButtonStyles}
+
+ padding-top: 18.5px;
+ padding-bottom: 13px;
+ color: #FFFFFF;
+ font-weight: bold;
+ border-color: transparent;
+ background: ${({ theme: { colors } }) => colors.primary};
+`
+
+export const ButtonSolid = styled.button`
+ ${solidButtonStyles}
+`
+
+export const ButtonOutline = styled.button`
+ ${outlineButtonStyles}
+`
diff --git a/src/features/Common/Checkbox/index.tsx b/src/features/Common/Checkbox/index.tsx
new file mode 100644
index 00000000..b3c80f67
--- /dev/null
+++ b/src/features/Common/Checkbox/index.tsx
@@ -0,0 +1,38 @@
+import React, { InputHTMLAttributes } from 'react'
+
+import {
+ Wrapper,
+ Input,
+ Label,
+} from './styled'
+
+type TCheckbox = Pick, (
+ | 'checked'
+ | 'id'
+ | 'name'
+ | 'value'
+ | 'onChange'
+)> & {
+ label?: string,
+}
+
+export const Checkbox = ({
+ checked,
+ id,
+ label,
+ name,
+ onChange,
+ value,
+}: TCheckbox) => (
+
+
+
+
+)
diff --git a/src/features/Common/Checkbox/stories.tsx b/src/features/Common/Checkbox/stories.tsx
new file mode 100644
index 00000000..0721257d
--- /dev/null
+++ b/src/features/Common/Checkbox/stories.tsx
@@ -0,0 +1,28 @@
+import React from 'react'
+
+import { Checkbox } from 'features/Common'
+
+export default {
+ component: Checkbox,
+ title: 'Checkbox',
+}
+
+const backgroundStyles = {
+ backgroundColor: '#333',
+ height: '200px',
+ padding: '20px',
+}
+
+export const Group = () => (
+
+
+
+
+)
diff --git a/src/features/Common/Checkbox/styled.tsx b/src/features/Common/Checkbox/styled.tsx
new file mode 100644
index 00000000..6c62dff1
--- /dev/null
+++ b/src/features/Common/Checkbox/styled.tsx
@@ -0,0 +1,40 @@
+import styled from 'styled-components/macro'
+
+export const Wrapper = styled.div`
+
+`
+
+export const Label = styled.label`
+ color: ${({ theme: { colors } }) => colors.text};
+ font-style: normal;
+ font-weight: bold;
+ font-size: 18px;
+ line-height: 21px;
+`
+
+export const Input = styled.input`
+ 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);
+ }
+
+ &:checked+${Label}::before {
+ background-image: url(/images/checkboxChecked.svg);
+ }
+`
diff --git a/src/features/Common/Input/index.tsx b/src/features/Common/Input/index.tsx
new file mode 100644
index 00000000..55bbbc6b
--- /dev/null
+++ b/src/features/Common/Input/index.tsx
@@ -0,0 +1,58 @@
+import React from 'react'
+
+import {
+ TInputWrapper,
+ InputWrapper,
+ InputStyled,
+ Label,
+} from './styled'
+
+type TInput = {
+ defaultValue?: string,
+ id: string,
+ inputWidth?: number,
+ label: string,
+ labelWidth?: number,
+ maxLength?: number,
+ onChange?: () => void,
+ required?: boolean,
+ type?: string,
+ value?: string,
+} & TInputWrapper
+
+export const Input = ({
+ defaultValue,
+ id,
+ inputWidth,
+ label,
+ labelWidth,
+ maxLength,
+ onChange,
+ paddingX,
+ required,
+ type,
+ value,
+ wrapperWidth,
+}: TInput) => (
+
+
+
+
+)
diff --git a/src/features/Common/Input/stories.tsx b/src/features/Common/Input/stories.tsx
new file mode 100644
index 00000000..51bb517e
--- /dev/null
+++ b/src/features/Common/Input/stories.tsx
@@ -0,0 +1,35 @@
+import React, { Fragment } from 'react'
+
+import { Input } from 'features/Common'
+
+export default {
+ component: Input,
+ title: 'Input',
+}
+
+export const Empty = () => (
+
+)
+
+export const EmailPassword = () => (
+
+
+
+
+)
diff --git a/src/features/Common/Input/styled.tsx b/src/features/Common/Input/styled.tsx
new file mode 100644
index 00000000..708cd621
--- /dev/null
+++ b/src/features/Common/Input/styled.tsx
@@ -0,0 +1,70 @@
+import styled from 'styled-components/macro'
+
+export type TInputWrapper = {
+ paddingX?: number,
+ wrapperWidth?: number,
+}
+
+export const InputWrapper = styled.div`
+ width: ${({ wrapperWidth }) => (wrapperWidth ? `${wrapperWidth}px` : '100%')};
+ height: 48px;
+ margin: 20px 0;
+ padding-left: ${({ paddingX = 24 }) => (paddingX ? `${paddingX}px` : '')};
+ padding-right: ${({ paddingX = 24 }) => (paddingX ? `${paddingX}px` : '')};
+ padding-top: 13px;
+ padding-bottom: 11px;
+ display: flex;
+ align-items: center;
+ background-color: #3F3F3F;
+ box-shadow: 0px 1px 1px rgba(0, 0, 0, 0.3);
+ border-radius: 2px;
+`
+
+type TLabel = {
+ labelWidth?: number,
+}
+
+export const Label = styled.label`
+ font-style: normal;
+ font-weight: normal;
+ font-size: 16px;
+ line-height: 24px;
+ letter-spacing: -0.01em;
+ padding-top: 2px;
+ color: ${({ theme: { colors } }) => colors.secondary};
+ width: ${({ labelWidth }) => (labelWidth ? `${labelWidth}px` : '')};
+`
+
+type TInputStyled = {
+ inputWidth?: number,
+}
+
+export const InputStyled = styled.input`
+ flex-grow: 1;
+ font-weight: bold;
+ font-size: 20px;
+ line-height: 24px;
+ width: ${({ inputWidth }) => (inputWidth ? `${inputWidth}px` : '')};
+ background-color: transparent;
+ border: transparent;
+ margin-left: 24px;
+ color: ${({ theme: { colors } }) => colors.text};
+
+ &[type='password'] {
+ letter-spacing: 6px;
+ }
+
+ :focus {
+ border-color: transparent;
+ outline: none;
+ }
+
+ :-webkit-autofill,
+ :-webkit-autofill:hover,
+ :-webkit-autofill:focus,
+ :-webkit-autofill:active {
+ box-shadow: 0 0 0 30px #3F3F3F inset;
+ caret-color: ${({ theme: { colors } }) => colors.text};
+ -webkit-text-fill-color: ${({ theme: { colors } }) => colors.text};
+ }
+`
diff --git a/src/features/Common/Radio/index.tsx b/src/features/Common/Radio/index.tsx
new file mode 100644
index 00000000..7ef499cf
--- /dev/null
+++ b/src/features/Common/Radio/index.tsx
@@ -0,0 +1,38 @@
+import React, { InputHTMLAttributes } from 'react'
+
+import {
+ Wrapper,
+ Input,
+ Label,
+} from './styled'
+
+type TCheckbox = Pick, (
+ | 'checked'
+ | 'id'
+ | 'name'
+ | 'value'
+ | 'onChange'
+)> & {
+ label?: string,
+}
+
+export const Radio = ({
+ checked,
+ id,
+ label,
+ name,
+ onChange,
+ value,
+}: TCheckbox) => (
+
+
+
+
+)
diff --git a/src/features/Common/Radio/stories.tsx b/src/features/Common/Radio/stories.tsx
new file mode 100644
index 00000000..12d9cc66
--- /dev/null
+++ b/src/features/Common/Radio/stories.tsx
@@ -0,0 +1,30 @@
+import React from 'react'
+
+import { Radio } from 'features/Common'
+
+export default {
+ component: Radio,
+ title: 'Radio',
+}
+
+const backgroundStyles = {
+ backgroundColor: '#333',
+ height: '200px',
+ padding: '20px',
+}
+
+export const Group = () => (
+
+
+
+
+)
diff --git a/src/features/Common/Radio/styled.tsx b/src/features/Common/Radio/styled.tsx
new file mode 100644
index 00000000..9a98a8e6
--- /dev/null
+++ b/src/features/Common/Radio/styled.tsx
@@ -0,0 +1,40 @@
+import styled from 'styled-components/macro'
+
+export const Wrapper = styled.div`
+
+`
+
+export const Label = styled.label`
+ color: ${({ theme: { colors } }) => colors.text};
+ font-style: normal;
+ font-weight: bold;
+ font-size: 18px;
+ line-height: 21px;
+`
+
+export const Input = styled.input`
+ 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);
+ }
+
+ &:checked+${Label}::before {
+ background-image: url(/images/radioChecked.svg);
+ }
+`
diff --git a/src/features/Common/index.tsx b/src/features/Common/index.tsx
new file mode 100644
index 00000000..7161a69c
--- /dev/null
+++ b/src/features/Common/index.tsx
@@ -0,0 +1,5 @@
+export * from './Input'
+export * from './Button'
+export * from './Radio'
+export * from './Checkbox'
+export * from './Arrows'
diff --git a/src/features/GlobalStyles/index.tsx b/src/features/GlobalStyles/index.tsx
new file mode 100644
index 00000000..b281bcdf
--- /dev/null
+++ b/src/features/GlobalStyles/index.tsx
@@ -0,0 +1,46 @@
+import { createGlobalStyle } from 'styled-components/macro'
+
+export const GlobalStyles = createGlobalStyle`
+ *, *:before, *:after {
+ box-sizing: border-box;
+ }
+
+ body {
+ min-height: 100vh;
+ margin: 0;
+ padding: 0;
+ font-family: Montserrat, Tahoma, sans-serif;
+ font-size: 12px;
+ line-height: 12px;
+ color: #000;
+ }
+
+ h1, h2, h3, h4, h5, h6, p, ul, li {
+ margin: 0;
+ padding: 0;
+ font-size: 12px;
+ line-height: 12px;
+ }
+
+ ul, li {
+ list-style: none;
+ }
+
+ a {
+ text-decoration: none;
+ color: #000;
+ }
+
+ fieldset {
+ margin: 0;
+ min-width: 0;
+ padding: 0;
+ border: 0;
+ }
+
+ button, input, select, textarea {
+ font-family: inherit;
+ font-size: inherit;
+ line-height: inherit;
+ }
+`
diff --git a/src/features/Theme/config.tsx b/src/features/Theme/config.tsx
new file mode 100644
index 00000000..d5c52872
--- /dev/null
+++ b/src/features/Theme/config.tsx
@@ -0,0 +1,40 @@
+export const lightTheme = {
+ colors: {
+ background: '',
+ primary: '',
+ secondary: '',
+ text: '',
+ },
+ name: 'light' as TName,
+ switchTheme: () => {},
+}
+
+export const darkTheme = {
+ colors: {
+ background: `
+ radial-gradient(
+ 49.07% 49.07% at 50% 29.54%,
+ rgba(255, 255, 255, 0.14) 0%,
+ rgba(255, 255, 255, 0) 100%
+ ),
+ rgba(0, 0, 0, 0.95)
+ `,
+ primary: `
+ linear-gradient(
+ 180deg,
+ rgba(255, 255, 255, 0) 0%,
+ rgba(255, 255, 255, 0.1) 0.01%,
+ rgba(0, 0, 0, 0.1) 99.99%
+ ),
+ #0033CC
+ `,
+ secondary: '#999999',
+ text: '#fff',
+ },
+ name: 'dark' as TName,
+ switchTheme: () => {},
+}
+
+type TName = 'light' | 'dark'
+
+export type TCustomTheme = typeof lightTheme
diff --git a/src/features/Theme/index.tsx b/src/features/Theme/index.tsx
new file mode 100644
index 00000000..ff3afce0
--- /dev/null
+++ b/src/features/Theme/index.tsx
@@ -0,0 +1,39 @@
+import React, {
+ useState,
+ ReactNode,
+ useCallback,
+ useMemo,
+} from 'react'
+import { ThemeProvider } from 'styled-components'
+
+import {
+ TCustomTheme,
+ lightTheme,
+ darkTheme,
+} from './config'
+
+type TThemeProps = {
+ children: ReactNode,
+}
+
+export const Theme = ({ children }: TThemeProps) => {
+ const [theme, setTheme] = useState(darkTheme)
+
+ const switchTheme = useCallback(
+ () => {
+ setTheme(theme.name === 'light' ? darkTheme : lightTheme)
+ },
+ [theme],
+ )
+
+ const memoTheme = useMemo(() => ({
+ ...theme,
+ switchTheme,
+ }), [theme, switchTheme])
+
+ return (
+
+ {children}
+
+ )
+}
diff --git a/src/types/styled-components.d.ts b/src/types/styled-components.d.ts
new file mode 100644
index 00000000..19b1878f
--- /dev/null
+++ b/src/types/styled-components.d.ts
@@ -0,0 +1,5 @@
+import { TCustomTheme } from '../features/Theme/config'
+
+declare module 'styled-components' {
+ export interface DefaultTheme extends TCustomTheme {}
+}