diff --git a/package-lock.json b/package-lock.json index 75ec2dc..f92e3f2 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,11 +1,11 @@ { - "name": "movie-app", + "name": "search-movie", "version": "0.1.0", "lockfileVersion": 3, "requires": true, "packages": { "": { - "name": "movie-app", + "name": "search-movie", "version": "0.1.0", "dependencies": { "@fortawesome/fontawesome-svg-core": "^1.2.34", @@ -26,7 +26,15 @@ "swiper": "^6.5.0" }, "devDependencies": { - "gh-pages": "^6.1.0" + "@types/jest": "^29.5.11", + "@types/node": "^20.10.5", + "@types/react": "^18.2.45", + "@types/react-dom": "^18.2.18", + "@types/react-helmet": "^6.1.11", + "@types/react-router-dom": "^5.3.3", + "@types/styled-components": "^5.1.34", + "gh-pages": "^6.1.0", + "typescript": "^5.2.2" } }, "node_modules/@aashutoshrathi/word-wrap": { @@ -2857,6 +2865,27 @@ "@types/yargs-parser": "*" } }, + "node_modules/@jest/expect-utils": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/expect-utils/-/expect-utils-29.7.0.tgz", + "integrity": "sha512-GlsNBWiFQFCVi9QVSx7f5AgMeLxe9YCCs5PuP2O2LdjDAA8Jh9eX7lA1Jq/xdXw3Wb3hyvlFNfZIfcRetSzYcA==", + "dev": true, + "dependencies": { + "jest-get-type": "^29.6.3" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jest/expect-utils/node_modules/jest-get-type": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-29.6.3.tgz", + "integrity": "sha512-zrteXnqYxfQh7l5FHyL38jL39di8H8rHoecLH3JNxH3BwOrBsNeabdap5e0I23lD4HHI8W5VFBZqG4Eaq5LNcw==", + "dev": true, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, "node_modules/@jest/fake-timers": { "version": "27.5.1", "resolved": "https://registry.npmjs.org/@jest/fake-timers/-/fake-timers-27.5.1.tgz", @@ -4116,6 +4145,22 @@ "@types/node": "*" } }, + "node_modules/@types/history": { + "version": "4.7.11", + "resolved": "https://registry.npmjs.org/@types/history/-/history-4.7.11.tgz", + "integrity": "sha512-qjDJRrmvBMiTx+jyLxvLfJU7UznFuokDv4f3WRuriHKERccVpFU+8XMQUAbDzoiJCsmexxRExQeMwwCdamSKDA==", + "dev": true + }, + "node_modules/@types/hoist-non-react-statics": { + "version": "3.3.5", + "resolved": "https://registry.npmjs.org/@types/hoist-non-react-statics/-/hoist-non-react-statics-3.3.5.tgz", + "integrity": "sha512-SbcrWzkKBw2cdwRTwQAswfpB9g9LJWfjtUeW/jvNwbhC8cpmmNYVePa+ncbUe0rGTQ7G3Ff6mYUN2VMfLVr+Sg==", + "dev": true, + "dependencies": { + "@types/react": "*", + "hoist-non-react-statics": "^3.3.0" + } + }, "node_modules/@types/html-minifier-terser": { "version": "6.1.0", "resolved": "https://registry.npmjs.org/@types/html-minifier-terser/-/html-minifier-terser-6.1.0.tgz", @@ -4156,6 +4201,196 @@ "@types/istanbul-lib-report": "*" } }, + "node_modules/@types/jest": { + "version": "29.5.11", + "resolved": "https://registry.npmjs.org/@types/jest/-/jest-29.5.11.tgz", + "integrity": "sha512-S2mHmYIVe13vrm6q4kN6fLYYAka15ALQki/vgDC3mIukEOx8WJlv0kQPM+d4w8Gp6u0uSdKND04IlTXBv0rwnQ==", + "dev": true, + "dependencies": { + "expect": "^29.0.0", + "pretty-format": "^29.0.0" + } + }, + "node_modules/@types/jest/node_modules/@jest/schemas": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/@jest/schemas/-/schemas-29.6.3.tgz", + "integrity": "sha512-mo5j5X+jIZmJQveBKeS/clAueipV7KgiX1vMgCxam1RNYiqE1w62n0/tJJnHtjW8ZHcQco5gY85jA3mi0L+nSA==", + "dev": true, + "dependencies": { + "@sinclair/typebox": "^0.27.8" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@types/jest/node_modules/@jest/types": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/@jest/types/-/types-29.6.3.tgz", + "integrity": "sha512-u3UPsIilWKOM3F9CXtrG8LEJmNxwoCQC/XVj4IKYXvvpx7QIi/Kg1LI5uDmDpKlac62NUtX7eLjRh+jVZcLOzw==", + "dev": true, + "dependencies": { + "@jest/schemas": "^29.6.3", + "@types/istanbul-lib-coverage": "^2.0.0", + "@types/istanbul-reports": "^3.0.0", + "@types/node": "*", + "@types/yargs": "^17.0.8", + "chalk": "^4.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@types/jest/node_modules/@sinclair/typebox": { + "version": "0.27.8", + "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.27.8.tgz", + "integrity": "sha512-+Fj43pSMwJs4KRrH/938Uf+uAELIgVBmQzg/q1YG10djyfA3TnrU8N8XzqCh/okZdszqBQTZf96idMfE5lnwTA==", + "dev": true + }, + "node_modules/@types/jest/node_modules/@types/istanbul-reports": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/@types/istanbul-reports/-/istanbul-reports-3.0.4.tgz", + "integrity": "sha512-pk2B1NWalF9toCRu6gjBzR69syFjP4Od8WRAX+0mmf9lAjCRicLOWc+ZrxZHx/0XRjotgkF9t6iaMJ+aXcOdZQ==", + "dev": true, + "dependencies": { + "@types/istanbul-lib-report": "*" + } + }, + "node_modules/@types/jest/node_modules/@types/yargs": { + "version": "17.0.32", + "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.32.tgz", + "integrity": "sha512-xQ67Yc/laOG5uMfX/093MRlGGCIBzZMarVa+gfNKJxWAIgykYpVGkBdbqEzGDDfCrVUj6Hiff4mTZ5BA6TmAog==", + "dev": true, + "dependencies": { + "@types/yargs-parser": "*" + } + }, + "node_modules/@types/jest/node_modules/ansi-styles": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", + "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/@types/jest/node_modules/diff-sequences": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/diff-sequences/-/diff-sequences-29.6.3.tgz", + "integrity": "sha512-EjePK1srD3P08o2j4f0ExnylqRs5B9tJjcp9t1krH2qRi8CCdsYfwe9JgSLurFBWwq4uOlipzfk5fHNvwFKr8Q==", + "dev": true, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@types/jest/node_modules/expect": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/expect/-/expect-29.7.0.tgz", + "integrity": "sha512-2Zks0hf1VLFYI1kbh0I5jP3KHHyCHpkfyHBzsSXRFgl/Bg9mWYfMW8oD+PdMPlEwy5HNsR9JutYy6pMeOh61nw==", + "dev": true, + "dependencies": { + "@jest/expect-utils": "^29.7.0", + "jest-get-type": "^29.6.3", + "jest-matcher-utils": "^29.7.0", + "jest-message-util": "^29.7.0", + "jest-util": "^29.7.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@types/jest/node_modules/jest-diff": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-diff/-/jest-diff-29.7.0.tgz", + "integrity": "sha512-LMIgiIrhigmPrs03JHpxUh2yISK3vLFPkAodPeo0+BuF7wA2FoQbkEg1u8gBYBThncu7e1oEDUfIXVuTqLRUjw==", + "dev": true, + "dependencies": { + "chalk": "^4.0.0", + "diff-sequences": "^29.6.3", + "jest-get-type": "^29.6.3", + "pretty-format": "^29.7.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@types/jest/node_modules/jest-get-type": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-29.6.3.tgz", + "integrity": "sha512-zrteXnqYxfQh7l5FHyL38jL39di8H8rHoecLH3JNxH3BwOrBsNeabdap5e0I23lD4HHI8W5VFBZqG4Eaq5LNcw==", + "dev": true, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@types/jest/node_modules/jest-matcher-utils": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-matcher-utils/-/jest-matcher-utils-29.7.0.tgz", + "integrity": "sha512-sBkD+Xi9DtcChsI3L3u0+N0opgPYnCRPtGcQYrgXmR+hmt/fYfWAL0xRXYU8eWOdfuLgBe0YCW3AFtnRLagq/g==", + "dev": true, + "dependencies": { + "chalk": "^4.0.0", + "jest-diff": "^29.7.0", + "jest-get-type": "^29.6.3", + "pretty-format": "^29.7.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@types/jest/node_modules/jest-message-util": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-29.7.0.tgz", + "integrity": "sha512-GBEV4GRADeP+qtB2+6u61stea8mGcOT4mCtrYISZwfu9/ISHFJ/5zOMXYbpBE9RsS5+Gb63DW4FgmnKJ79Kf6w==", + "dev": true, + "dependencies": { + "@babel/code-frame": "^7.12.13", + "@jest/types": "^29.6.3", + "@types/stack-utils": "^2.0.0", + "chalk": "^4.0.0", + "graceful-fs": "^4.2.9", + "micromatch": "^4.0.4", + "pretty-format": "^29.7.0", + "slash": "^3.0.0", + "stack-utils": "^2.0.3" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@types/jest/node_modules/jest-util": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-29.7.0.tgz", + "integrity": "sha512-z6EbKajIpqGKU56y5KBUgy1dt1ihhQJgWzUlZHArA/+X2ad7Cb5iF+AK1EWVL/Bo7Rz9uurpqw6SiBCefUbCGA==", + "dev": true, + "dependencies": { + "@jest/types": "^29.6.3", + "@types/node": "*", + "chalk": "^4.0.0", + "ci-info": "^3.2.0", + "graceful-fs": "^4.2.9", + "picomatch": "^2.2.3" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@types/jest/node_modules/pretty-format": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-29.7.0.tgz", + "integrity": "sha512-Pdlw/oPxN+aXdmM9R00JVC9WVFoCLTKJvDVLgmJ+qAffBMxsV85l/Lu7sNx4zSzPyoL2euImuEwHhOXdEgNFZQ==", + "dev": true, + "dependencies": { + "@jest/schemas": "^29.6.3", + "ansi-styles": "^5.0.0", + "react-is": "^18.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, "node_modules/@types/json-schema": { "version": "7.0.15", "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz", @@ -4235,6 +4470,36 @@ "@types/react": "*" } }, + "node_modules/@types/react-helmet": { + "version": "6.1.11", + "resolved": "https://registry.npmjs.org/@types/react-helmet/-/react-helmet-6.1.11.tgz", + "integrity": "sha512-0QcdGLddTERotCXo3VFlUSWO3ztraw8nZ6e3zJSgG7apwV5xt+pJUS8ewPBqT4NYB1optGLprNQzFleIY84u/g==", + "dev": true, + "dependencies": { + "@types/react": "*" + } + }, + "node_modules/@types/react-router": { + "version": "5.1.20", + "resolved": "https://registry.npmjs.org/@types/react-router/-/react-router-5.1.20.tgz", + "integrity": "sha512-jGjmu/ZqS7FjSH6owMcD5qpq19+1RS9DeVRqfl1FeBMxTDQAGwlMWOcs52NDoXaNKyG3d1cYQFMs9rCrb88o9Q==", + "dev": true, + "dependencies": { + "@types/history": "^4.7.11", + "@types/react": "*" + } + }, + "node_modules/@types/react-router-dom": { + "version": "5.3.3", + "resolved": "https://registry.npmjs.org/@types/react-router-dom/-/react-router-dom-5.3.3.tgz", + "integrity": "sha512-kpqnYK4wcdm5UaWI3fLcELopqLrHgLqNsdpHauzlQktfkHL3npOSwtj1Uz9oKBAzs7lFtVkV8j83voAz2D8fhw==", + "dev": true, + "dependencies": { + "@types/history": "^4.7.11", + "@types/react": "*", + "@types/react-router": "*" + } + }, "node_modules/@types/resolve": { "version": "1.17.1", "resolved": "https://registry.npmjs.org/@types/resolve/-/resolve-1.17.1.tgz", @@ -4298,6 +4563,17 @@ "resolved": "https://registry.npmjs.org/@types/stack-utils/-/stack-utils-2.0.3.tgz", "integrity": "sha512-9aEbYZ3TbYMznPdcdr3SmIrLXwC/AKZXQeCf9Pgao5CKb8CyHuEX5jzWPTkvregvhRJHcpRO6BFoGW9ycaOkYw==" }, + "node_modules/@types/styled-components": { + "version": "5.1.34", + "resolved": "https://registry.npmjs.org/@types/styled-components/-/styled-components-5.1.34.tgz", + "integrity": "sha512-mmiVvwpYklFIv9E8qfxuPyIt/OuyIrn6gMOAMOFUO3WJfSrSE+sGUoa4PiZj77Ut7bKZpaa6o1fBKS/4TOEvnA==", + "dev": true, + "dependencies": { + "@types/hoist-non-react-statics": "*", + "@types/react": "*", + "csstype": "^3.0.2" + } + }, "node_modules/@types/testing-library__dom": { "version": "7.5.0", "resolved": "https://registry.npmjs.org/@types/testing-library__dom/-/testing-library__dom-7.5.0.tgz", @@ -16762,16 +17038,15 @@ } }, "node_modules/typescript": { - "version": "4.9.5", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.9.5.tgz", - "integrity": "sha512-1FXk9E2Hm+QzZQ7z+McJiHL4NW1F2EzMu9Nq9i3zAaGqibafqYwCVU6WyWAuyQRRzOlxou8xZSyXLEN8oKj24g==", - "peer": true, + "version": "5.3.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.3.3.tgz", + "integrity": "sha512-pXWcraxM0uxAS+tN0AG/BF2TyqmHO014Z070UsJ+pFvYuRSq8KH8DmWpnbXe0pEPDHXZV3FcAbJkijJ5oNEnWw==", "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" }, "engines": { - "node": ">=4.2.0" + "node": ">=14.17" } }, "node_modules/unbox-primitive": { diff --git a/package.json b/package.json index 45c89b0..9355e81 100644 --- a/package.json +++ b/package.json @@ -45,6 +45,17 @@ ] }, "devDependencies": { - "gh-pages": "^6.1.0" + "@types/jest": "^29.5.11", + "@types/node": "^20.10.5", + "@types/react": "^18.2.45", + "@types/react-dom": "^18.2.18", + "@types/react-helmet": "^6.1.11", + "@types/react-router-dom": "^5.3.3", + "@types/styled-components": "^5.1.34", + "gh-pages": "^6.1.0", + "typescript": "^5.2.2" + }, + "overrides": { + "typescript": "^5.2.2" } } diff --git a/src/Components/App.js b/src/Components/App.tsx similarity index 100% rename from src/Components/App.js rename to src/Components/App.tsx diff --git a/src/Components/Error.js b/src/Components/Error.tsx similarity index 80% rename from src/Components/Error.js rename to src/Components/Error.tsx index a8a40d5..a33b60a 100644 --- a/src/Components/Error.js +++ b/src/Components/Error.tsx @@ -13,7 +13,12 @@ const Text = styled.span` font-weight : 500 ; `; -const Error = ({ text, color }) => ( +type ErrorProps = { + text : string + color : string +} + +const Error = ({ text, color } : ErrorProps) => ( {text} diff --git a/src/Components/FixedMenu.js b/src/Components/FixedMenu.tsx similarity index 71% rename from src/Components/FixedMenu.js rename to src/Components/FixedMenu.tsx index 837593e..927af5b 100644 --- a/src/Components/FixedMenu.js +++ b/src/Components/FixedMenu.tsx @@ -15,7 +15,7 @@ const Container = styled.div` const FixedMenu = () => { - function onClickScrollTop(e) { + function onClickScrollTop(event : React.MouseEvent) { window.scroll({ behavior : 'smooth', top : 0 @@ -23,8 +23,8 @@ const FixedMenu = () => { } return ( - - + + ); }; diff --git a/src/Components/GlobalSytles.js b/src/Components/GlobalSytles.tsx similarity index 100% rename from src/Components/GlobalSytles.js rename to src/Components/GlobalSytles.tsx diff --git a/src/Components/Header.js b/src/Components/Header.tsx similarity index 72% rename from src/Components/Header.js rename to src/Components/Header.tsx index 227bd8c..ab3560a 100644 --- a/src/Components/Header.js +++ b/src/Components/Header.tsx @@ -25,11 +25,11 @@ const List = styled.ul` display : flex ; ` ; -const Item = styled.li` +const Item = styled.li<{current : boolean}>` width : 80px ; height : 50px ; border-bottom : 4px solid - ${ props => props.current ? "#00695c" : "transparent" } ; + ${props => props.current ? "#00695c" : "transparent"} ; transition : border-bottom 0.4s ease-in-out ; @media ${props => props.theme.mobileS} { @@ -50,14 +50,14 @@ export default () => { return (
- - Movie + + Movie - - TV + + TV - - Search + + Search
diff --git a/src/Components/Loader.js b/src/Components/Loader.tsx similarity index 87% rename from src/Components/Loader.js rename to src/Components/Loader.tsx index 8d2469d..b6b46b0 100644 --- a/src/Components/Loader.js +++ b/src/Components/Loader.tsx @@ -5,11 +5,11 @@ import { FontAwesomeIcon } from '@fortawesome/react-fontawesome' ; import { faSpinner } from '@fortawesome/free-solid-svg-icons' ; const Container = styled.div` - height : 100vh ; - width : 100vw ; + height : 95vh ; + width : 95vw ; display : flex ; justify-content : center ; - margin-top : 40px ; + padding-top : 50px ; `; export default () => ( diff --git a/src/Components/Poster.js b/src/Components/Poster.tsx similarity index 60% rename from src/Components/Poster.js rename to src/Components/Poster.tsx index 55a28c5..8e9420f 100644 --- a/src/Components/Poster.js +++ b/src/Components/Poster.tsx @@ -8,7 +8,7 @@ import { faStar } from '@fortawesome/free-solid-svg-icons' ; const Container = styled.div``; -const Image = styled.div` +const Image = styled.div<{ bgUrl : string }>` background-image : url(${props => props.bgUrl}) ; height : 180px ; background-size : cover ; @@ -57,25 +57,36 @@ const Year = styled.span` } `; -const Poster = ({ id, imageUrl, title, rating, year, isMovie = false, idList }) => - - - - - - -   {rating} / 10 - - - {title.length > 18 ? `${title.substring(0, 18)}...` : title} - {year} - - - ; +type PosterProps = { + id : string + imageUrl : string + title : string + rating : number + year : string + isMovie? : boolean + idList : number[] +} + +const Poster = ({ id, imageUrl, title, rating, year, isMovie = false, idList } : PosterProps) => { + return ( + + + + + + +   { `${rating.toFixed(1)} / 10` } + + + {title.length > 18 ? `${title.substring(0, 18)}...` : title} + {year} + + + ) ; +} ; Poster.propTypes = { imageUrl : PropTypes.string.isRequired, diff --git a/src/Components/Router.js b/src/Components/Router.js deleted file mode 100644 index bec6bc7..0000000 --- a/src/Components/Router.js +++ /dev/null @@ -1,24 +0,0 @@ -import React from 'react' ; -import { BrowserRouter, Route, Routes } from 'react-router-dom' ; - -import Home from '../Routes/Home' ; -import Search from '../Routes/Search' ; -import Tv from '../Routes/Tv' ; -import Detail from '../Routes/Detail' -import FixedMenu from '../Components/FixedMenu' ; - -import Header from './Header' ; - -export default () => ( - -
- - } /> - } /> - } /> - } /> - } /> - - - -) ; \ No newline at end of file diff --git a/src/Components/Router.tsx b/src/Components/Router.tsx new file mode 100644 index 0000000..8c72d57 --- /dev/null +++ b/src/Components/Router.tsx @@ -0,0 +1,26 @@ +import React from 'react' ; +import { BrowserRouter, Route, Routes, Router, useLocation } from 'react-router-dom' ; + +import Home from '../Routes/Home' ; +import Search from '../Routes/Search' ; +import Tv from '../Routes/Tv' ; +import Detail from '../Routes/Detail' +import FixedMenu from './FixedMenu' ; + +import Header from './Header' ; + +export default () => { + return ( + +
+ + } /> + } /> + } /> + } /> + } /> + + + + ) ; +} ; \ No newline at end of file diff --git a/src/Components/Section.js b/src/Components/Section.tsx similarity index 87% rename from src/Components/Section.js rename to src/Components/Section.tsx index 6ba2104..27660c2 100644 --- a/src/Components/Section.js +++ b/src/Components/Section.tsx @@ -29,7 +29,12 @@ const Grid = styled.div` } ` ; -const Section = ({ title, children }) => ( +type SectionProps = { + title : string + children : React.ReactNode[] +} + +const Section = ({ title, children } : SectionProps) => ( { title } { children } diff --git a/src/Routes/Detail/DetailContainer.js b/src/Routes/Detail/DetailContainer.tsx similarity index 74% rename from src/Routes/Detail/DetailContainer.js rename to src/Routes/Detail/DetailContainer.tsx index f0cd4b5..0da3a57 100644 --- a/src/Routes/Detail/DetailContainer.js +++ b/src/Routes/Detail/DetailContainer.tsx @@ -1,14 +1,43 @@ import React, { Component, memo } from 'react' ; import DetailPresenter from './DetailPresenter' ; -import { MoviesApi, TVApi } from '../../api'; +import { MoviesApi, TVApi } from '../../api' ; +import { NavigateFunction, useLocation, useNavigate } from 'react-router' ; -export default memo(class DetailContaniner extends Component { - constructor(props) { +type DetailContaninerProps = { + location : { + pathname : string + state : number[] + } + navigate : NavigateFunction +} + +type State = { + loading : boolean + error : string + isMovie : boolean + slideLoadData : any +} + +export default memo(() => { + + const location = useLocation() ; + const navigate = useNavigate() ; + + return +}) + + +class DetailContaniner extends Component { + + idList : number[] ; + state : State ; + + constructor(props : DetailContaninerProps) { super(props) ; const { location : { pathname, - state : { idList } + state : idList }, } = props ; @@ -16,13 +45,13 @@ export default memo(class DetailContaniner extends Component { this.state = { loading : true, - error : null, + error : '', isMovie : pathname.includes('/movie/'), slideLoadData : [], } ; } - getDataNotAsyncMovie = id => { + getDataNotAsyncMovie = (id : number) => { MoviesApi.movieDetail(id).then((e) => { const { data } = e ; @@ -44,7 +73,7 @@ export default memo(class DetailContaniner extends Component { return ; } - getDataNotAsyncTV = (id) => { + getDataNotAsyncTV = (id : number) => { TVApi.showDetail(id).then((e) => { const { data } = e ; @@ -70,24 +99,24 @@ export default memo(class DetailContaniner extends Component { const { isMovie, slideLoadData } = this.state ; const { idList } = this ; const { - match : { - params : { id } + location : { + pathname }, - history : { push } + navigate } = this.props ; + const parseId = parseInt(pathname.split("/")[2]) ; - const parseId = parseInt(id) ; - if(isNaN(parseId)) - return push("/") ; + if(isNaN(parseId)) return navigate("/") ; try { + if(isMovie) { const { data } = await MoviesApi.movieDetail(parseId) ; - + const { data : { results @@ -98,18 +127,17 @@ export default memo(class DetailContaniner extends Component { result : data, resultRent : results } - - this.setState({ - slideLoadData : [ ...slideLoadData, dataObj ], - }) ; - const indexId = idList.findIndex(idData => idData === Number(parseId)) + const indexId = idList.findIndex((idData : number) => idData === Number(parseId)) idList.splice(indexId, 1) ; for(let i = 0 ; i < idList.length ; i++) { - this.getDataNotAsyncMovie(idList[i], id) ; + this.getDataNotAsyncMovie(idList[i]) ; } - + + this.setState({ + slideLoadData : [ ...slideLoadData, dataObj ], + }) ; }else { @@ -127,20 +155,20 @@ export default memo(class DetailContaniner extends Component { result : data, resultRent : results } - - this.setState({ - slideLoadData : [ ...slideLoadData, dataObj ], - }) ; const indexId = idList.findIndex(idData => idData === Number(parseId)) idList.splice(indexId, 1) ; for(let i = 0 ; i < idList.length ; i++) { - this.getDataNotAsyncTV(idList[i]) ; + this.getDataNotAsyncTV(idList[i]) ; } + + this.setState({ + slideLoadData : [ ...slideLoadData, dataObj ], + }) ; } - }catch { + }catch(err) { this.setState({ error : "Can't find anything.", }) ; @@ -163,7 +191,6 @@ export default memo(class DetailContaniner extends Component { render() { const { loading, error } = this.state ; - const { idList } = this ; return ( ) ; } ; -}) \ No newline at end of file +} \ No newline at end of file diff --git a/src/Routes/Detail/DetailPresenter.js b/src/Routes/Detail/DetailPresenter.tsx similarity index 54% rename from src/Routes/Detail/DetailPresenter.js rename to src/Routes/Detail/DetailPresenter.tsx index 7d99990..eaee467 100644 --- a/src/Routes/Detail/DetailPresenter.js +++ b/src/Routes/Detail/DetailPresenter.tsx @@ -16,9 +16,9 @@ SwiperCore.use([]) ; const Container = styled.div` height : calc(100vh - 50px) ; - width : 100% ; - position : relative ; + width : 100vw ; padding : 50px ; + position : relative ; margin : 0 auto ; @@ -30,13 +30,9 @@ const Container = styled.div` `; const VideoContainer = styled.div` - position : absolute ; - width : 50% ; + width : 100% ; height : 50% ; padding : 0 5px ; - z-index : 0 ; - bottom : 0 ; - right : 120px ; @media ${props => props.theme.mobileS} { position : static ; @@ -54,7 +50,7 @@ const VideoContainer = styled.div` } `; -const Backdrop = styled.div` +const Backdrop = styled.div<{ bgImage : string }>` position : absolute ; top : 0 ; left : 0 ; @@ -68,31 +64,42 @@ const Backdrop = styled.div` opacity : 0.5 ; `; -const Data = styled.div` - width : 50% ; - margin-left : 10px ; - position : absolute ; - left : 600px ; - top : 0 ; - z-index : 2 ; +const DataContainer = styled.div<{ display : string, backgroundColor : string }>` + display : ${props => props.display} ; + flex : 1 ; + padding : 0 50px 0 50px ; + background-color : ${props => props.backgroundColor} ; - display : ${props => props.display} ; + flex-direction : column ; @media ${props => props.theme.mobileS} { - position : static ; + flex : 0 ; + padding : 10px ; - width : 100% ; - height : 100% ; + position : absolute ; float : left ; - padding : 10px ; + width : 340px ; + height : 500px ; + } +`; + +const Data = styled.div` + width : 100% ; + flex : 1 ; + margin-left : 10px ; + z-index : 2 ; + + display : block ; + + @media ${props => props.theme.mobileS} { margin-left : 0 ; } `; -const Cover = styled.div` +const Cover = styled.div<{ bgImage : string }>` width : 40% ; height : 100% ; background-image : url(${props => props.bgImage}) ; @@ -110,9 +117,10 @@ const Content = styled.div` display : flex ; width : 80% ; height : 100% ; - position : relative ; z-index : 1 ; + justify-content : center ; + margin : 0 auto ; @media ${props => props.theme.mobileS} { @@ -124,14 +132,22 @@ const Content = styled.div` `; const Title = styled.h3` - font-size : 32px ; + width : 100% ; + height : 52px ; + font-size : 48px ; margin-bottom : 20px ; position : relative ; z-index : 1 ; + overflow : hidden ; + text-overflow : ellipsis ; + white-space : nowrap ; + @media ${props => props.theme.mobileS} { font-size : 18px ; margin-bottom : 10px ; + + height : 32px ; } `; @@ -153,19 +169,31 @@ const Divider = styled.span` margin : 0 10px ; `; -const OverView = styled.p` - position : relative ; - font-size : 12px ; - line-height : 1.5 ; +const OverView = styled.p` + display : -webkit-box ; + display : -ms-flexbox ; + display : box ; + font-size : 16px ; + line-height : 150% ; margin-bottom : 10px ; - z-index : 1 ; + width : 100% ; + height : 35% ; + + overflow : hidden ; + vertical-align : top ; + text-overflow : ellipsis ; + word-break : break-all ; + -webkit-box-orient : vertical ; + -webkit-line-clamp : 6 ; @media ${props => props.theme.mobileS} { font-size : 12px ; + height : auto ; + -webkit-line-clamp : unset ; } `; -const Video = styled.iframe` +const Video = styled.iframe<{ src : string, frameborder : string, allowfullscreen : boolean }>` width : 100% ; height : 100% ; `; @@ -223,7 +251,7 @@ const LogoImageWrapper = styled.div` float : left ; `; -const LogoImage = styled.div` +const LogoImage = styled.div<{ bgImage : string }>` margin-top : 20px ; width : 70px ; @@ -245,11 +273,17 @@ const LogoImage = styled.div` } `; +type DetailPresenterProps = { + slideLoadData : any + loading : boolean + error : string +} + const DetailPresenter = ({ slideLoadData, loading, error - }) => { + } : DetailPresenterProps) => { const { mobileS } = size ; const [ mode468px, setMode468px ] = useState(false) ; @@ -257,31 +291,32 @@ const DetailPresenter = ({ const [ touchMove, setTouchMove ] = useState(false) ; const [ touchMoveData, setTouchMoveData ] = useState(false) ; - function touchMoveInit(e) { + function touchMoveInit(e : React.TouchEvent) { e.stopPropagation() ; setTouchMove(true) ; } - function touchMoveInitData(e) { + function touchMoveInitData(e : React.TouchEvent) { e.stopPropagation() ; setTouchMoveData(true) ; } - function clickCover(e) { + function clickCover(e : React.TouchEvent) { e.stopPropagation() ; touchMove ? setTouchMove(false) : setCss(true) ; } - function clickData(e) { + function clickData(e : React.TouchEvent) { e.stopPropagation() ; touchMoveData ? setTouchMoveData(false) : setCss(false) ; } - const viewContentNumCheck = innerWidth => { + const viewContentNumCheck = ( innerWidth : number ) => { + if( innerWidth <= mobileS ) { setMode468px(true) ; setCss(false) ; @@ -291,8 +326,9 @@ const DetailPresenter = ({ } } - const onResize = (e) => { - const { currentTarget : { innerWidth } } = e ; + const onResize = (e : UIEvent) => { + const { currentTarget } = e ; + const { innerWidth } = currentTarget as any ; viewContentNumCheck(innerWidth) ; } @@ -321,7 +357,7 @@ const DetailPresenter = ({ spaceBetween = { 30 } onSlideChangeTransitionStart = { () => { if(mode468px) setCss(false) } } > - { slideLoadData && slideLoadData.map(( data, index ) => { + { slideLoadData && slideLoadData.map(( data : any, index : number ) => { const { result, resultRent } = data ; let canSee = null ; @@ -335,7 +371,7 @@ const DetailPresenter = ({ } return ( - + - mode468px ? clickCover(e) : null } + /> + - {result.original_title ? result.original_title : result.original_name} - - - {result.release_date ? - result.release_date.substring(0, 7) - : result.first_air_date.substring(0, 7)} - - . - - {result.runtime ? - result.runtime - : result.episode_run_time}분 - - . - - {result.genres && - result.genres.map((genre, index) => - index === result.genres.length - 1 ? - genre.name - : `${genre.name} / ` - )} - - - {result.overview} - - {canSee && canSee.map((data, index) => - - {data.provider_name} - - - - )} - - - - { mode468px ? null : - } + mode468px ? clickData(e) : null } + > + {result.original_title ? result.original_title : result.original_name} + + + {result.release_date ? + result.release_date.substring(0, 7) + : result.first_air_date.substring(0, 7)} + + . + + {result.runtime ? + result.runtime + : result.episode_run_time}min + + . + + { + result.genres!.map((genre : any, index : number) => + index === result.genres.length - 1 ? + genre.name + : `${genre.name} / ` + ) + } + + + {result.overview} + + {canSee && canSee.map((data, index) => + + {data.provider_name} + + + + )} + + + { mode468px ? null : + } + - { mode468px ? - : null } + { mode468px ? ( + + ) : null} { error && } diff --git a/src/Routes/Detail/index.js b/src/Routes/Detail/index.tsx similarity index 100% rename from src/Routes/Detail/index.js rename to src/Routes/Detail/index.tsx diff --git a/src/Routes/Home/HomeContainer.js b/src/Routes/Home/HomeContainer.tsx similarity index 87% rename from src/Routes/Home/HomeContainer.js rename to src/Routes/Home/HomeContainer.tsx index d26ef99..fd9d0f8 100644 --- a/src/Routes/Home/HomeContainer.js +++ b/src/Routes/Home/HomeContainer.tsx @@ -4,15 +4,14 @@ import { MoviesApi } from '../../api' ; export default class extends Component { state = { - nowPlaying : null, - upcoming : null, - popular : null, - error : null, + nowPlaying : [], + upcoming : [], + popular : [], + error : "", loading : true, - nowPlayingIdList : null, - upcomingIdLlist : null, - popularIdList : null, - + nowPlayingIdList : [], + upcomingIdLlist : [], + popularIdList : [], } ; async componentDidMount() { @@ -48,8 +47,8 @@ export default class extends Component { } } ; - setIdArr = arr => { - return arr.map(data => data.id) ; + setIdArr = (arr : any) => { + return arr.map((data : any) => data.id) ; } render() { @@ -66,14 +65,14 @@ export default class extends Component { return ( ) ; } ; diff --git a/src/Routes/Home/HomePresenter.js b/src/Routes/Home/HomePresenter.js deleted file mode 100644 index 59572cf..0000000 --- a/src/Routes/Home/HomePresenter.js +++ /dev/null @@ -1,92 +0,0 @@ -import React from 'react'; -import PropTypes from 'prop-types'; -import styled from 'styled-components' ; -import Helmet from 'react-helmet' ; - -import Section from '../../Components/Section' ; -import Loader from '../../Components/Loader' ; -import Error from '../../Components/Error'; -import Poster from '../../Components/Poster' ; - -const Container = styled.div` - padding : 20px ; -`; - -const HomePresenter = ({ - nowPlaying, - upcoming, - popular, - error, - loading, - nowPlayingIdList, - upcomingIdLlist, - popularIdList }) => - (<> - - Movie | Search Media - - { loading ? : ( - - { nowPlaying && nowPlaying.length > 0 && ( -
- { nowPlaying.map(movie => ( - - ))} -
- )} - { popular && popular.length > 0 && ( -
- { popular.map(movie => ( - - ))} -
- )} - { upcoming && upcoming.length > 0 && ( -
- { upcoming.map(movie => ( - - ))} -
- )} - { error && } -
)} - -) ; - - -HomePresenter.propTypes = { - nowPlaying : PropTypes.array, - upcoming : PropTypes.array, - popular : PropTypes.array, - error : PropTypes.string, - loading : PropTypes.bool.isRequired, -}; - -export default HomePresenter; \ No newline at end of file diff --git a/src/Routes/Home/HomePresenter.tsx b/src/Routes/Home/HomePresenter.tsx new file mode 100644 index 0000000..db61dcd --- /dev/null +++ b/src/Routes/Home/HomePresenter.tsx @@ -0,0 +1,103 @@ +import React from 'react'; +import PropTypes from 'prop-types'; +import styled from 'styled-components' ; +import Helmet from 'react-helmet' ; + +import Section from '../../Components/Section' ; +import Loader from '../../Components/Loader' ; +import Error from '../../Components/Error' ; +import Poster from '../../Components/Poster' ; + +const Container = styled.div` + padding : 20px ; +`; + +type HomePresenterProps = { + nowPlaying : any[] + upcoming : any[] + popular : any[] + error : string + loading : boolean + nowPlayingIdList : number[] + upcomingIdLlist : number[] + popularIdList : number[] +} + +const HomePresenter = ({ + nowPlaying, + upcoming, + popular, + error, + loading, + nowPlayingIdList, + upcomingIdLlist, + popularIdList } : HomePresenterProps) => + (<> + + Movie | Search Media + + { loading ? : ( + + { nowPlaying!.length > 0 && ( +
+ { nowPlaying.map((movie : any) => ( + + ))} +
+ )} + { popular!.length > 0 && ( +
+ { popular.map((movie : any) => ( + + ))} +
+ )} + { upcoming!.length > 0 && ( +
+ { upcoming.map((movie : any) => ( + + ))} +
+ )} + { error && } +
)} + +) ; + + +HomePresenter.propTypes = { + nowPlaying : PropTypes.array, + upcoming : PropTypes.array, + popular : PropTypes.array, + error : PropTypes.string, + loading : PropTypes.bool.isRequired, +}; + +export default HomePresenter; \ No newline at end of file diff --git a/src/Routes/Home/index.js b/src/Routes/Home/index.tsx similarity index 100% rename from src/Routes/Home/index.js rename to src/Routes/Home/index.tsx diff --git a/src/Routes/Search/SearchContainer.js b/src/Routes/Search/SearchContainer.tsx similarity index 85% rename from src/Routes/Search/SearchContainer.js rename to src/Routes/Search/SearchContainer.tsx index 728d3a3..7bda3c0 100644 --- a/src/Routes/Search/SearchContainer.js +++ b/src/Routes/Search/SearchContainer.tsx @@ -4,19 +4,19 @@ import { MoviesApi, TVApi } from '../../api' ; export default class SearchContainer extends Component { state = { - movieResultsIdList : null, - showResultsIdList : null, - movieResults : null, - showResults : null, + movieResultsIdList : [], + showResultsIdList : [], + movieResults : [], + showResults : [], searchTerm : '', loading : false, - error : null + error : '' } ; - updateTerm = (e) => { + updateTerm = (e : React.ChangeEvent) => { const { value } = e.currentTarget ; this.setState({ searchTerm : value }) ; } - handleSubmit = (e) => { + handleSubmit = (e : React.FormEvent) => { e.preventDefault() ; const { searchTerm } = this.state ; if(searchTerm !== '') { @@ -52,8 +52,8 @@ export default class SearchContainer extends Component { } } - setIdArr = arr => { - return arr.map(data => data.id) ; + setIdArr = (arr : any) => { + return arr.map((data : any) => data.id) ; } render() { diff --git a/src/Routes/Search/SearchPresenter.js b/src/Routes/Search/SearchPresenter.tsx similarity index 85% rename from src/Routes/Search/SearchPresenter.js rename to src/Routes/Search/SearchPresenter.tsx index 3633adb..cd41f73 100644 --- a/src/Routes/Search/SearchPresenter.js +++ b/src/Routes/Search/SearchPresenter.tsx @@ -23,6 +23,17 @@ const Input = styled.input` width : 100% ; `; +type SearchPresenterProps = { + movieResultsIdList : number[] + showResultsIdList : number[] + movieResults : any + showResults : any + loading : boolean + error : string + handleSubmit : React.FormEventHandler + updateTerm : React.ChangeEventHandler +} + const SearchPresenter = ({ movieResultsIdList, showResultsIdList, @@ -31,18 +42,18 @@ const SearchPresenter = ({ loading, error, handleSubmit, - updateTerm }) => ( + updateTerm } : SearchPresenterProps) => ( Search | Search Media
- +
{ loading ? : <> {movieResults && movieResults.length >0 &&
- {movieResults.map(movie => ( + {movieResults.map((movie : any) => ( } {showResults && showResults.length >0 &&
- {showResults.map(show => ( + {showResults.map((show : any) => ( { - return arr.map(data => data.id) ; + setIdArr = (arr : any) => { + return arr.map((data : any) => data.id) ; } render() { diff --git a/src/Routes/Tv/TVPresenter.js b/src/Routes/Tv/TVPresenter.tsx similarity index 87% rename from src/Routes/Tv/TVPresenter.js rename to src/Routes/Tv/TVPresenter.tsx index bb21eb3..2744aa4 100644 --- a/src/Routes/Tv/TVPresenter.js +++ b/src/Routes/Tv/TVPresenter.tsx @@ -12,6 +12,17 @@ const Container = styled.div` padding : 20px ; `; +type TVPresenterProps = { + topRatedIdList : number[] + popularIdList : number[] + airingTodayIdList : number[] + topRated : any[] + popular : any[] + airingToday : any[] + loading : boolean + error : string +} + const TVPresenter = ({ topRatedIdList, popularIdList, @@ -20,7 +31,7 @@ const TVPresenter = ({ popular, airingToday, loading, - error }) => ( + error } : TVPresenterProps) => ( <> TV | Search Media @@ -29,7 +40,7 @@ const TVPresenter = ({ { topRated && topRated.length > 0 && (
- {topRated.map(show => ( + {topRated.map((show : any) => ( 0 && (
- {popular.map(show => ( + {popular.map((show : any) => ( 0 && (
- {airingToday.map(show => ( + {airingToday.map((show : any) => ( api.get(`movie/now_playing?api_key=${KEY}&language=ja-JP`), upcoming : () => api.get(`movie/upcoming?api_key=${KEY}&language=ja-JP`), popular : () => api.get(`movie/popular?api_key=${KEY}&language=ja-JP`), - movieDetail : id => api.get(`movie/${id}?api_key=${KEY}&language=en-US&append_to_response=videos`), - getRent : id => api.get(`movie/${id}/watch/providers?api_key=${KEY}&language=ja-JP`), - search : trem => api.get(`search/movie?api_key=${KEY}&language=ja-JP&query=${encodeURIComponent(trem)}`) + movieDetail : (id : number) => api.get(`movie/${id}?api_key=${KEY}&language=en-US&append_to_response=videos`), + getRent : (id : number) => api.get(`movie/${id}/watch/providers?api_key=${KEY}&language=ja-JP`), + search : (trem : any) => api.get(`search/movie?api_key=${KEY}&language=ja-JP&query=${encodeURIComponent(trem)}`) } ; export const TVApi = { topRated : () => api.get(`tv/top_rated?api_key=${KEY}&language=ja-JP`), popular : () => api.get(`tv/popular?api_key=${KEY}&language=ja-JP`), airingToday : () => api.get(`tv/airing_today?api_key=${KEY}&language=ja-JP`), - showDetail : id => api.get(`tv/${id}?api_key=${KEY}&language=en-US&append_to_response=videos`), - getRent : id => api.get(`tv/${id}/watch/providers?api_key=${KEY}&language=ja-JP`), - search : trem => api.get(`search/tv?api_key=${KEY}&language=ja-JP&query=${encodeURIComponent(trem)}`) + showDetail : (id : number) => api.get(`tv/${id}?api_key=${KEY}&language=en-US&append_to_response=videos`), + getRent : (id : number) => api.get(`tv/${id}/watch/providers?api_key=${KEY}&language=ja-JP`), + search : (trem : any) => api.get(`search/tv?api_key=${KEY}&language=ja-JP&query=${encodeURIComponent(trem)}`) } ; \ No newline at end of file diff --git a/src/index.js b/src/index.tsx similarity index 79% rename from src/index.js rename to src/index.tsx index b90c0b3..2994475 100644 --- a/src/index.js +++ b/src/index.tsx @@ -5,11 +5,11 @@ import { ThemeProvider } from 'styled-components' ; import theme from './theme' ; -const container = document.getElementById('root'); +const container = document.getElementById('root') as HTMLElement; const root = createRoot(container); root.render( - + ); diff --git a/src/theme.js b/src/theme.js deleted file mode 100644 index fbf3feb..0000000 --- a/src/theme.js +++ /dev/null @@ -1,15 +0,0 @@ -export const size = { - laptop : 1400, - tabletL : 1220, - tabletS : 1024, - mobileL : 768, - mobileS : 468 -} - -const theme = {} ; - -for( const key in size ) { - theme[key] = `(max-width : ${size[key]}px)` ; -} - -export default theme ; \ No newline at end of file diff --git a/src/theme.ts b/src/theme.ts new file mode 100644 index 0000000..6c93f17 --- /dev/null +++ b/src/theme.ts @@ -0,0 +1,17 @@ +export const size = { + laptop : 1400, + tabletL : 1220, + tabletS : 1024, + mobileL : 768, + mobileS : 468 +} + +const theme = { + laptop : `(max-width : ${size.laptop}px)`, + tabletL : `(max-width : ${size.tabletL}px)`, + tabletS : `(max-width : ${size.tabletS}px)`, + mobileL : `(max-width : ${size.mobileL}px)`, + mobileS : `(max-width : ${size.mobileS}px)`, +} ; + +export default theme ; \ No newline at end of file diff --git a/tsconfig.json b/tsconfig.json new file mode 100644 index 0000000..74caf0d --- /dev/null +++ b/tsconfig.json @@ -0,0 +1,26 @@ +{ + "compilerOptions": { + "target": "es5", + "lib": [ + "dom", + "dom.iterable", + "esnext" + ], + "allowJs": true, + "skipLibCheck": true, + "esModuleInterop": true, + "allowSyntheticDefaultImports": true, + "strict": true, + "forceConsistentCasingInFileNames": true, + "noFallthroughCasesInSwitch": true, + "module": "esnext", + "moduleResolution": "node", + "resolveJsonModule": true, + "isolatedModules": true, + "noEmit": true, + "jsx": "react-jsx" + }, + "include": [ + "src" + ] +} \ No newline at end of file