From 1be633ae161bae1dd83c4f17538fa6d84fc26314 Mon Sep 17 00:00:00 2001 From: GilPOP Date: Sat, 4 Dec 2021 10:23:53 +0900 Subject: [PATCH 01/24] restart --- package-lock.json | 60 +++++++++ package.json | 2 + src/App.tsx | 73 +---------- src/Router.tsx | 20 --- src/Routes/Chart.tsx | 92 -------------- src/Routes/Coin.tsx | 296 ------------------------------------------- src/Routes/Coins.tsx | 115 ----------------- src/Routes/Price.tsx | 188 --------------------------- src/api.ts | 25 ---- src/index.tsx | 76 ++++++++++- 10 files changed, 133 insertions(+), 814 deletions(-) delete mode 100644 src/Router.tsx delete mode 100644 src/Routes/Chart.tsx delete mode 100644 src/Routes/Coin.tsx delete mode 100644 src/Routes/Coins.tsx delete mode 100644 src/Routes/Price.tsx delete mode 100644 src/api.ts diff --git a/package-lock.json b/package-lock.json index 0df0eec..25bbf74 100644 --- a/package-lock.json +++ b/package-lock.json @@ -20,9 +20,11 @@ "react-apexcharts": "^1.3.9", "react-dom": "^17.0.2", "react-helmet": "^6.1.0", + "react-hook-form": "^7.20.5", "react-query": "^3.33.5", "react-router-dom": "^5.3.0", "react-scripts": "4.0.3", + "recoil": "^0.5.2", "styled-components": "^5.3.3", "typescript": "^4.5.2", "web-vitals": "^1.1.2" @@ -9850,6 +9852,11 @@ "node": ">=6" } }, + "node_modules/hamt_plus": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/hamt_plus/-/hamt_plus-1.0.2.tgz", + "integrity": "sha1-4hwlKWjH4zsg9qGwlM2FeHomVgE=" + }, "node_modules/handle-thing": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/handle-thing/-/handle-thing-2.0.1.tgz", @@ -16604,6 +16611,21 @@ "react": ">=16.3.0" } }, + "node_modules/react-hook-form": { + "version": "7.20.5", + "resolved": "https://registry.npmjs.org/react-hook-form/-/react-hook-form-7.20.5.tgz", + "integrity": "sha512-xYeBmQW6oqxDYAYVWIoZYC7tRD6lJBJt9b8Rr1Mv/VhZ+ZUCy2IuXlm2YA9i0/zl0TKKxqBWQOxGEb3qyVIhMg==", + "engines": { + "node": ">=12.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/react-hook-form" + }, + "peerDependencies": { + "react": "^16.8.0 || ^17" + } + }, "node_modules/react-is": { "version": "17.0.2", "resolved": "https://registry.npmjs.org/react-is/-/react-is-17.0.2.tgz", @@ -16928,6 +16950,25 @@ "node": ">=8.10.0" } }, + "node_modules/recoil": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/recoil/-/recoil-0.5.2.tgz", + "integrity": "sha512-Edibzpu3dbUMLy6QRg73WL8dvMl9Xqhp+kU+f2sJtXxsaXvAlxU/GcnDE8HXPkprXrhHF2e6SZozptNvjNF5fw==", + "dependencies": { + "hamt_plus": "1.0.2" + }, + "peerDependencies": { + "react": ">=16.13.1" + }, + "peerDependenciesMeta": { + "react-dom": { + "optional": true + }, + "react-native": { + "optional": true + } + } + }, "node_modules/recursive-readdir": { "version": "2.2.2", "resolved": "https://registry.npmjs.org/recursive-readdir/-/recursive-readdir-2.2.2.tgz", @@ -29570,6 +29611,11 @@ "pify": "^4.0.1" } }, + "hamt_plus": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/hamt_plus/-/hamt_plus-1.0.2.tgz", + "integrity": "sha1-4hwlKWjH4zsg9qGwlM2FeHomVgE=" + }, "handle-thing": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/handle-thing/-/handle-thing-2.0.1.tgz", @@ -34770,6 +34816,12 @@ "react-side-effect": "^2.1.0" } }, + "react-hook-form": { + "version": "7.20.5", + "resolved": "https://registry.npmjs.org/react-hook-form/-/react-hook-form-7.20.5.tgz", + "integrity": "sha512-xYeBmQW6oqxDYAYVWIoZYC7tRD6lJBJt9b8Rr1Mv/VhZ+ZUCy2IuXlm2YA9i0/zl0TKKxqBWQOxGEb3qyVIhMg==", + "requires": {} + }, "react-is": { "version": "17.0.2", "resolved": "https://registry.npmjs.org/react-is/-/react-is-17.0.2.tgz", @@ -35031,6 +35083,14 @@ "picomatch": "^2.2.1" } }, + "recoil": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/recoil/-/recoil-0.5.2.tgz", + "integrity": "sha512-Edibzpu3dbUMLy6QRg73WL8dvMl9Xqhp+kU+f2sJtXxsaXvAlxU/GcnDE8HXPkprXrhHF2e6SZozptNvjNF5fw==", + "requires": { + "hamt_plus": "1.0.2" + } + }, "recursive-readdir": { "version": "2.2.2", "resolved": "https://registry.npmjs.org/recursive-readdir/-/recursive-readdir-2.2.2.tgz", diff --git a/package.json b/package.json index 8ac911a..deec3d2 100644 --- a/package.json +++ b/package.json @@ -15,9 +15,11 @@ "react-apexcharts": "^1.3.9", "react-dom": "^17.0.2", "react-helmet": "^6.1.0", + "react-hook-form": "^7.20.5", "react-query": "^3.33.5", "react-router-dom": "^5.3.0", "react-scripts": "4.0.3", + "recoil": "^0.5.2", "styled-components": "^5.3.3", "typescript": "^4.5.2", "web-vitals": "^1.1.2" diff --git a/src/App.tsx b/src/App.tsx index 03074c7..92d8d01 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -1,75 +1,4 @@ -import Router from "./Router"; -import { createGlobalStyle } from "styled-components"; -import { ReactQueryDevtools } from "react-query/devtools"; - -const GlobalStyle = createGlobalStyle` -@import url('https://fonts.googleapis.com/css2?family=Source+Sans+Pro:wght@300;400&display=swap'); -html, body, div, span, applet, object, iframe, -h1, h2, h3, h4, h5, h6, p, blockquote, pre, -a, abbr, acronym, address, big, cite, code, -del, dfn, em, img, ins, kbd, q, s, samp, -small, strike, strong, sub, sup, tt, var, -b, u, i, center, -dl, dt, dd, ol, ul, li, -fieldset, form, label, legend, -table, caption, tbody, tfoot, thead, tr, th, td, -article, aside, canvas, details, embed, -figure, figcaption, footer, header, hgroup, -menu, nav, output, ruby, section, summary, -time, mark, audio, video { - margin: 0; - padding: 0; - border: 0; - font-size: 100%; - font: inherit; - vertical-align: baseline; -} -/* HTML5 display-role reset for older browsers */ -article, aside, details, figcaption, figure, -footer, header, hgroup, menu, nav, section { - display: block; -} -body { - line-height: 1; -} -ol, ul { - list-style: none; -} -blockquote, q { - quotes: none; -} -blockquote:before, blockquote:after, -q:before, q:after { - content: ''; - content: none; -} -table { - border-collapse: collapse; - border-spacing: 0; -} -*{ - box-sizing: border-box; -} -a{ - text-decoration: none; - color:inherit; -} - -body{ - font-family: 'Source Sans Pro', sans-serif; - background-color: ${(props) => props.theme.bgColor}; - color:${(props) => props.theme.textColor} -} -`; - function App() { - return ( - <> - - - - - ); + return null; } - export default App; diff --git a/src/Router.tsx b/src/Router.tsx deleted file mode 100644 index 8c86fa2..0000000 --- a/src/Router.tsx +++ /dev/null @@ -1,20 +0,0 @@ -import { BrowserRouter, Switch, Route } from "react-router-dom"; -import Coin from "./Routes/Coin"; -import Coins from "./Routes/Coins"; - -function Router() { - return ( - - - - - - - - - - - ); -} - -export default Router; diff --git a/src/Routes/Chart.tsx b/src/Routes/Chart.tsx deleted file mode 100644 index 1635057..0000000 --- a/src/Routes/Chart.tsx +++ /dev/null @@ -1,92 +0,0 @@ -import { useQuery } from "react-query"; -import { fetchOHLCInfo } from "../api"; -import ApexChart from "react-apexcharts"; -import styled from "styled-components"; - -interface ChartProps { - coinId: string; -} - -interface IHistorical { - time_open: string; - time_close: string; - open: number; - high: number; - low: number; - close: number; - volume: number; - market_cap: number; -} - -const Container = styled.div` - margin-top: 30px; -`; - -function Chart({ coinId }: ChartProps) { - const { isLoading, data } = useQuery( - ["ohlc", coinId], - () => fetchOHLCInfo(coinId), - { - refetchInterval: 10000, - } - ); - // console.log( - // data?.map((item) => [item.open, item.high, item.low, item.close]) - // ); - // console.log(data?.map((item) => item.time_close.slice(5, 10))); - //console.log(data?.map((item) => item.time_close[1])); - //console.log(data); - const arr = () => - data?.map((item) => { - let x = item.time_close.slice(5, 10); - let y = [ - item.open.toFixed(2), - item.high.toFixed(2), - item.low.toFixed(2), - item.close.toFixed(2), - ]; - //console.log(x); - return { x: x, y: y }; - }); - // console.log(arr()); - return ( - - {isLoading ? ( - "Loading Chart ..." - ) : ( - - )} - - ); -} - -export default Chart; diff --git a/src/Routes/Coin.tsx b/src/Routes/Coin.tsx deleted file mode 100644 index 2e53017..0000000 --- a/src/Routes/Coin.tsx +++ /dev/null @@ -1,296 +0,0 @@ -import { useEffect, useState } from "react"; -import { useQuery } from "react-query"; -import { Helmet } from "react-helmet"; -import { - Route, - Switch, - useLocation, - useParams, - useRouteMatch, -} from "react-router"; -import { Link } from "react-router-dom"; -import styled from "styled-components"; -import { fetchCoinInfo, fetchTickersInfo } from "../api"; -import Chart from "./Chart"; -import Price from "./Price"; - -interface routeParams { - coinId: string; -} - -const Container = styled.div` - padding: 0px 20px; - max-width: 480px; - margin: 0 auto; -`; - -const Loading = styled.span` - text-align: center; - display: block; -`; - -const Header = styled.header` - display: flex; - justify-content: space-between; - align-items: center; - height: 15vh; -`; - -const Title = styled.div` - font-size: 48px; - color: ${(props) => props.theme.accentColor}; - text-align: center; -`; - -interface RouteState { - name: string; -} - -interface ICoinInfo { - id: string; - name: string; - symbol: string; - rank: number; - is_new: boolean; - is_active: boolean; - type: string; - contract: string; - platform: string; - description: string; - message: string; - open_source: boolean; - started_at: string; - development_status: string; - hardware_wallet: boolean; - proof_type: string; - org_structure: string; - hash_algorithm: string; - first_data_at: string; - last_data_at: string; -} - -interface ITickersInfo { - id: string; - name: string; - symbol: string; - rank: number; - circulating_supply: number; - total_supply: number; - max_supply: number; - beta_value: number; - first_data_at: string; - last_updated: string; - quotes: { - USD: { - ath_date: string; - ath_price: number; - market_cap: number; - market_cap_change_24h: number; - percent_change_1h: number; - percent_change_1y: number; - percent_change_6h: number; - percent_change_7d: number; - percent_change_12h: number; - percent_change_15m: number; - percent_change_24h: number; - percent_change_30d: number; - percent_change_30m: number; - percent_from_price_ath: number; - price: number; - volume_24h: number; - volume_24h_change_24h: number; - }; - }; -} - -const ContentContainer = styled.div` - display: flex; - flex-direction: column; - align-items: center; -`; - -const Article = styled.div` - background-color: ${(props) => props.theme.textColor}; - color: ${(props) => props.theme.bgColor}; - padding: 20px 20px; - border-radius: 15px; - width: 100%; - display: flex; - align-items: center; - justify-content: space-between; - margin-bottom: 30px; -`; - -const MiniContentDiv = styled.div` - display: flex; - align-items: center; - flex-direction: column; -`; - -const ContentTitle = styled.span` - font-size: 12px; - text-align: center; - margin-bottom: 5px; -`; - -const ContentText = styled.span` - font-size: 16px; - text-align: center; - line-height: 20px; -`; - -const Tab = styled.div` - width: 100%; - display: grid; - grid-template-columns: 1fr 1fr; - gap: 10px; -`; - -const Tabs = styled.span<{ isActive: boolean }>` - text-align: center; - font-size: 16px; - padding: 10px; - background-color: ${(props) => props.theme.textColor}; - color: ${(props) => - props.isActive ? props.theme.accentColor : props.theme.bgColor}; - border-radius: 10px; - a { - display: block; - } -`; - -const Home = styled.div` - text-align: center; - color: ${(props) => props.theme.bgColor}; - background-color: ${(props) => props.theme.textColor}; - border-radius: 5px; - padding: 10px; - right: 200px; - &:hover { - color: ${(props) => props.theme.accentColor}; - transition: color 0.2s ease-in-out; - } - a { - display: block; - } -`; - -function Coin() { - const { coinId } = useParams(); - const { state } = useLocation(); - const priceMatch = useRouteMatch(`/${coinId}/price`); - const chartMatch = useRouteMatch(`/${coinId}/chart`); - const { isLoading: CoinInfoLoading, data: CoinInfoData } = - useQuery(["coinInfo", coinId], () => fetchCoinInfo(coinId), { - refetchInterval: 10000, - }); - const { isLoading: TickersInfoLoading, data: TickersInfoData } = - useQuery( - ["tickersInfo", coinId], - () => fetchTickersInfo(coinId), - { - refetchInterval: 10000, - } - ); - const loading = CoinInfoLoading || TickersInfoLoading; - /* - const [loading, setLoading] = useState(true); - const [info, setInfo] = useState(); - const [priceInfo, setPriceInfo] = useState(); - console.log(chartMatch); - useEffect(() => { - (async () => { - const infoData = await ( - await fetch(`https://api.coinpaprika.com/v1/coins/${coinId}`) - ).json(); - const priceData = await ( - await fetch(`https://api.coinpaprika.com/v1/tickers/${coinId}`) - ).json(); - setInfo(infoData); - setPriceInfo(priceData); - setLoading(false); - // console.log(state); - // console.log(coinId); - })(); - }, []);*/ - return ( - - - - {state?.name - ? state.name - : loading - ? "Loading..." - : CoinInfoData?.name} - - -
- - Home - - - {state?.name - ? state.name - : loading - ? "Loading..." - : CoinInfoData?.name} - -
-
-
- {loading ? ( - Loading... - ) : ( - -
- - RANK: - {CoinInfoData?.rank} - - - SYMBOL: - {CoinInfoData?.symbol} - - - OPEN SOURCE: - - {CoinInfoData?.open_source ? "Yes" : "No"} - - -
-
- {CoinInfoData?.description} -
-
- - TOTAL SUPPLY: - {TickersInfoData?.total_supply} - - - MAX SUPPLY: - {TickersInfoData?.max_supply} - -
- - - Chart - - - Price - - - - - - - - - - -
- )} -
- ); -} - -export default Coin; diff --git a/src/Routes/Coins.tsx b/src/Routes/Coins.tsx deleted file mode 100644 index f591d08..0000000 --- a/src/Routes/Coins.tsx +++ /dev/null @@ -1,115 +0,0 @@ -import { useEffect, useState } from "react"; -import { useQuery } from "react-query"; -import { Link } from "react-router-dom"; -import styled from "styled-components"; -import { fetchCoins } from "../api"; -import { Helmet } from "react-helmet"; - -const Container = styled.div` - padding: 0px 20px; - max-width: 480px; - margin: 0 auto; -`; - -const Loading = styled.span` - text-align: center; - display: block; -`; - -const Header = styled.header` - display: flex; - justify-content: center; - align-items: center; - height: 15vh; -`; - -const Title = styled.div` - font-size: 48px; - color: ${(props) => props.theme.accentColor}; -`; - -const Img = styled.img` - width: 35px; - height: 35px; - margin-right: 10px; -`; - -const CoinsList = styled.ul``; - -const Coin = styled.li` - background-color: ${(props) => props.theme.textColor}; - color: ${(props) => props.theme.bgColor}; - margin-bottom: 10px; - border-radius: 15px; - a { - padding: 20px; - display: flex; - align-items: center; - transition: color 0.2s ease-in-out; - } - &:hover { - a { - color: ${(props) => props.theme.accentColor}; - } - } -`; - -interface ICoin { - id: string; - name: string; - symbol: string; - rank: number; - is_new: boolean; - is_active: boolean; - type: string; -} - -function Coins() { - /* - const [loading, setLoading] = useState(true); - const [coins, setCoins] = useState([]); - useEffect(() => { - (async () => { - const response = await fetch("https://api.coinpaprika.com/v1/coins"); - const json = await response.json(); - setCoins(json.slice(0, 100)); - //console.log(json); - setLoading(false); - })(); - }, []);*/ - const { isLoading, data } = useQuery("allcoins", fetchCoins); - - return ( - - - 코인 - -
- 코인 -
- {isLoading ? ( - Loading... - ) : ( - - {data?.slice(0, 100).map((item) => ( - - - - {item.name} → - - - ))} - - )} -
- ); -} - -export default Coins; diff --git a/src/Routes/Price.tsx b/src/Routes/Price.tsx deleted file mode 100644 index 1aedfc9..0000000 --- a/src/Routes/Price.tsx +++ /dev/null @@ -1,188 +0,0 @@ -import { useQuery } from "react-query"; -import styled, { keyframes } from "styled-components"; -import { fetchTickersInfo } from "../api"; - -interface ICoin { - coinId: string; -} - -interface ICoinInfo { - id: string; - name: string; - symbol: string; - rank: number; - is_new: boolean; - is_active: boolean; - type: string; - contract: string; - platform: string; - description: string; - message: string; - open_source: boolean; - started_at: string; - development_status: string; - hardware_wallet: boolean; - proof_type: string; - org_structure: string; - hash_algorithm: string; - first_data_at: string; - last_data_at: string; -} - -interface ITickersInfo { - id: string; - name: string; - symbol: string; - rank: number; - circulating_supply: number; - total_supply: number; - max_supply: number; - beta_value: number; - first_data_at: string; - last_updated: string; - quotes: { - USD: { - ath_date: string; - ath_price: number; - market_cap: number; - market_cap_change_24h: number; - percent_change_1h: number; - percent_change_1y: number; - percent_change_6h: number; - percent_change_7d: number; - percent_change_12h: number; - percent_change_15m: number; - percent_change_24h: number; - percent_change_30d: number; - percent_change_30m: number; - percent_from_price_ath: number; - price: number; - volume_24h: number; - volume_24h_change_24h: number; - }; - }; -} - -const centerAni = keyframes` - 0%{ - opacity:0; - }50%{ - - opacity:0.5; - }100%{ - - opacity:1; - } -`; - -const ContainerDiv = styled.div` - width: 100%; -`; - -const Container = styled.div` - width: 100%; -`; -const Tags = styled.div` - display: flex; - justify-content: space-between; - align-items: center; - width: 100%; - background-color: ${(props) => props.theme.textColor}; - color: ${(props) => props.theme.bgColor}; - padding: 10px 15px; - margin-top: 10px; - border-radius: 15px; - animation: ${centerAni} 0.5s ease-in-out; - &:first-child { - margin-top: 30px; - } -`; - -const TagTitle = styled.span` - font-size: 12px; -`; -const TagContent = styled.span<{ isMinus: boolean }>` - color: ${(props) => - props.isMinus ? props.theme.plusColor : props.theme.minusColor}; - font-size: 18px; -`; - -function Price({ coinId }: ICoin) { - const { isLoading, data } = useQuery( - ["price", coinId], - () => fetchTickersInfo(coinId), - { - refetchInterval: 10000, - } - ); - return ( - - {isLoading ? ( - "Loading Price..." - ) : ( - - - Current Prices : - {`$${data?.quotes.USD.price.toFixed(3)}`} - - - Percent Change 12 Hours : - {`${data?.quotes.USD.percent_change_12h}%`} - - - Percent Change 24 Hourss : - {`${data?.quotes.USD.percent_change_24h}%`} - - - Percent Change 7 days : - {`${data?.quotes.USD.percent_change_7d}%`} - - - Volume Change 24hours : - {`$${data?.quotes.USD.volume_24h_change_24h}`} - - - Maximum Price : - {`$${data?.quotes.USD.ath_price.toFixed(3)}`} - - - Maximum Price Date : - {`${data?.quotes.USD.ath_date.slice( - 0, - 10 - )}`} - - - )} - - ); -} - -export default Price; diff --git a/src/api.ts b/src/api.ts deleted file mode 100644 index 0f43c8c..0000000 --- a/src/api.ts +++ /dev/null @@ -1,25 +0,0 @@ -const BASE_URL = "https://api.coinpaprika.com/v1"; - -export function fetchCoins() { - return fetch(`${BASE_URL}/coins`).then((response) => response.json()); -} - -export function fetchCoinInfo(coinId: string) { - return fetch(`${BASE_URL}/coins/${coinId}`).then((response) => - response.json() - ); -} - -export function fetchTickersInfo(coinId: string) { - return fetch(`${BASE_URL}/tickers/${coinId}`).then((response) => - response.json() - ); -} - -export function fetchOHLCInfo(coinId: string) { - const endDate = Math.floor(Date.now() / 1000); - const startDate = endDate - 60 * 60 * 24 * 14; - return fetch( - `${BASE_URL}/coins/${coinId}/ohlcv/historical?start=${startDate}&end=${endDate}` - ).then((response) => response.json()); -} diff --git a/src/index.tsx b/src/index.tsx index c6c1c70..7644622 100644 --- a/src/index.tsx +++ b/src/index.tsx @@ -1,19 +1,83 @@ import React from "react"; import ReactDOM from "react-dom"; import { QueryClient, QueryClientProvider } from "react-query"; -import { ThemeProvider } from "styled-components"; +import { RecoilRoot } from "recoil"; +import { createGlobalStyle, ThemeProvider } from "styled-components"; import App from "./App"; import { theme } from "./theme"; const queryClient = new QueryClient(); +const GlobalStyle = createGlobalStyle` +@import url('https://fonts.googleapis.com/css2?family=Source+Sans+Pro:wght@300;400&display=swap'); +html, body, div, span, applet, object, iframe, +h1, h2, h3, h4, h5, h6, p, blockquote, pre, +a, abbr, acronym, address, big, cite, code, +del, dfn, em, img, ins, kbd, q, s, samp, +small, strike, strong, sub, sup, tt, var, +b, u, i, center, +dl, dt, dd, ol, ul, li, +fieldset, form, label, legend, +table, caption, tbody, tfoot, thead, tr, th, td, +article, aside, canvas, details, embed, +figure, figcaption, footer, header, hgroup, +menu, nav, output, ruby, section, summary, +time, mark, audio, video { + margin: 0; + padding: 0; + border: 0; + font-size: 100%; + font: inherit; + vertical-align: baseline; +} +/* HTML5 display-role reset for older browsers */ +article, aside, details, figcaption, figure, +footer, header, hgroup, menu, nav, section { + display: block; +} +body { + line-height: 1; +} +ol, ul { + list-style: none; +} +blockquote, q { + quotes: none; +} +blockquote:before, blockquote:after, +q:before, q:after { + content: ''; + content: none; +} +table { + border-collapse: collapse; + border-spacing: 0; +} +*{ + box-sizing: border-box; +} +a{ + text-decoration: none; + color:inherit; +} + +body{ + font-family: 'Source Sans Pro', sans-serif; + background-color: ${(props) => props.theme.bgColor}; + color:${(props) => props.theme.textColor} +} +`; + ReactDOM.render( - - - - - + + + + + + + + , document.getElementById("root") ); From 55009a6d550ff9f2bb45b41d4ad7216cfd7ade94 Mon Sep 17 00:00:00 2001 From: GilPOP Date: Sat, 4 Dec 2021 10:32:47 +0900 Subject: [PATCH 02/24] react beautiful dnd start --- package-lock.json | 200 +++++++++++++++++++++++++++++++++++++++++++++- package.json | 2 + src/App.tsx | 22 ++++- 3 files changed, 221 insertions(+), 3 deletions(-) diff --git a/package-lock.json b/package-lock.json index 25bbf74..56696db 100644 --- a/package-lock.json +++ b/package-lock.json @@ -18,6 +18,7 @@ "apexcharts": "^3.31.0", "react": "^17.0.2", "react-apexcharts": "^1.3.9", + "react-beautiful-dnd": "^13.1.0", "react-dom": "^17.0.2", "react-helmet": "^6.1.0", "react-hook-form": "^7.20.5", @@ -30,6 +31,7 @@ "web-vitals": "^1.1.2" }, "devDependencies": { + "@types/react-beautiful-dnd": "^13.1.2", "@types/react-helmet": "^6.1.4", "@types/react-router-dom": "^5.3.2", "@types/styled-components": "^5.1.15" @@ -3507,7 +3509,6 @@ "version": "3.3.1", "resolved": "https://registry.npmjs.org/@types/hoist-non-react-statics/-/hoist-non-react-statics-3.3.1.tgz", "integrity": "sha512-iMIqiko6ooLrTh1joXodJK5X9xeEALT1kM5G3ZLhD3hszxBdIEd5C75U834D9mLcINgD4OyZf5uQXjkuYydWvA==", - "dev": true, "dependencies": { "@types/react": "*", "hoist-non-react-statics": "^3.3.0" @@ -3745,6 +3746,15 @@ "csstype": "^3.0.2" } }, + "node_modules/@types/react-beautiful-dnd": { + "version": "13.1.2", + "resolved": "https://registry.npmjs.org/@types/react-beautiful-dnd/-/react-beautiful-dnd-13.1.2.tgz", + "integrity": "sha512-+OvPkB8CdE/bGdXKyIhc/Lm2U7UAYCCJgsqmopFmh9gbAudmslkI8eOrPDjg4JhwSE6wytz4a3/wRjKtovHVJg==", + "dev": true, + "dependencies": { + "@types/react": "*" + } + }, "node_modules/@types/react-dom": { "version": "17.0.11", "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-17.0.11.tgz", @@ -3762,6 +3772,17 @@ "@types/react": "*" } }, + "node_modules/@types/react-redux": { + "version": "7.1.20", + "resolved": "https://registry.npmjs.org/@types/react-redux/-/react-redux-7.1.20.tgz", + "integrity": "sha512-q42es4c8iIeTgcnB+yJgRTTzftv3eYYvCZOh1Ckn2eX/3o5TdsQYKUWpLoLuGlcY/p+VAhV9IOEZJcWk/vfkXw==", + "dependencies": { + "@types/hoist-non-react-statics": "^3.3.0", + "@types/react": "*", + "hoist-non-react-statics": "^3.3.0", + "redux": "^4.0.0" + } + }, "node_modules/@types/react-router": { "version": "5.1.17", "resolved": "https://registry.npmjs.org/@types/react-router/-/react-router-5.1.17.tgz", @@ -6579,6 +6600,14 @@ "node": ">=6.0.0" } }, + "node_modules/css-box-model": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/css-box-model/-/css-box-model-1.2.1.tgz", + "integrity": "sha512-a7Vr4Q/kd/aw96bnJG332W9V9LkJO69JRcaCYDUqjp6/z0w6VcZjgAcTbgFxEPfBgdnAwlh3iwu+hLopa+flJw==", + "dependencies": { + "tiny-invariant": "^1.0.6" + } + }, "node_modules/css-color-keywords": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/css-color-keywords/-/css-color-keywords-1.0.0.tgz", @@ -13448,6 +13477,11 @@ "node": ">= 0.6" } }, + "node_modules/memoize-one": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/memoize-one/-/memoize-one-5.2.1.tgz", + "integrity": "sha512-zYiwtZUcYyXKo/np96AGZAckk+FWWsUdJ3cHGGmld7+AhvcWmQyGCYUh1hc4Q/pkOhb65dQR/pqCyK0cOaHz4Q==" + }, "node_modules/memory-fs": { "version": "0.4.1", "resolved": "https://registry.npmjs.org/memory-fs/-/memory-fs-0.4.1.tgz", @@ -16368,6 +16402,11 @@ "performance-now": "^2.1.0" } }, + "node_modules/raf-schd": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/raf-schd/-/raf-schd-4.0.3.tgz", + "integrity": "sha512-tQkJl2GRWh83ui2DiPTJz9wEiMN20syf+5oKfB03yYP7ioZcJwsIK8FjrtLwH1m7C7e+Tt2yYBlrOpdT+dyeIQ==" + }, "node_modules/randombytes": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz", @@ -16455,6 +16494,24 @@ "node": ">=10" } }, + "node_modules/react-beautiful-dnd": { + "version": "13.1.0", + "resolved": "https://registry.npmjs.org/react-beautiful-dnd/-/react-beautiful-dnd-13.1.0.tgz", + "integrity": "sha512-aGvblPZTJowOWUNiwd6tNfEpgkX5OxmpqxHKNW/4VmvZTNTbeiq7bA3bn5T+QSF2uibXB0D1DmJsb1aC/+3cUA==", + "dependencies": { + "@babel/runtime": "^7.9.2", + "css-box-model": "^1.2.0", + "memoize-one": "^5.1.1", + "raf-schd": "^4.0.2", + "react-redux": "^7.2.0", + "redux": "^4.0.4", + "use-memo-one": "^1.1.1" + }, + "peerDependencies": { + "react": "^16.8.5 || ^17.0.0", + "react-dom": "^16.8.5 || ^17.0.0" + } + }, "node_modules/react-dev-utils": { "version": "11.0.4", "resolved": "https://registry.npmjs.org/react-dev-utils/-/react-dev-utils-11.0.4.tgz", @@ -16656,6 +16713,41 @@ } } }, + "node_modules/react-redux": { + "version": "7.2.6", + "resolved": "https://registry.npmjs.org/react-redux/-/react-redux-7.2.6.tgz", + "integrity": "sha512-10RPdsz0UUrRL1NZE0ejTkucnclYSgXp5q+tB5SWx2qeG2ZJQJyymgAhwKy73yiL/13btfB6fPr+rgbMAaZIAQ==", + "dependencies": { + "@babel/runtime": "^7.15.4", + "@types/react-redux": "^7.1.20", + "hoist-non-react-statics": "^3.3.2", + "loose-envify": "^1.4.0", + "prop-types": "^15.7.2", + "react-is": "^17.0.2" + }, + "peerDependencies": { + "react": "^16.8.3 || ^17" + }, + "peerDependenciesMeta": { + "react-dom": { + "optional": true + }, + "react-native": { + "optional": true + } + } + }, + "node_modules/react-redux/node_modules/@babel/runtime": { + "version": "7.16.3", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.16.3.tgz", + "integrity": "sha512-WBwekcqacdY2e9AF/Q7WLFUWmdJGJTkbjqTjoMDgXkVZ3ZRUvOPsLb5KdwISoQVsbP+DQzVZW4Zhci0DvpbNTQ==", + "dependencies": { + "regenerator-runtime": "^0.13.4" + }, + "engines": { + "node": ">=6.9.0" + } + }, "node_modules/react-refresh": { "version": "0.8.3", "resolved": "https://registry.npmjs.org/react-refresh/-/react-refresh-0.8.3.tgz", @@ -16992,6 +17084,14 @@ "node": ">=8" } }, + "node_modules/redux": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/redux/-/redux-4.1.2.tgz", + "integrity": "sha512-SH8PglcebESbd/shgf6mii6EIoRM0zrQyjcuQ+ojmfxjTtE0z9Y8pa62iA/OJ58qjP6j27uyW4kUF4jl/jd6sw==", + "dependencies": { + "@babel/runtime": "^7.9.2" + } + }, "node_modules/regenerate": { "version": "1.4.2", "resolved": "https://registry.npmjs.org/regenerate/-/regenerate-1.4.2.tgz", @@ -20225,6 +20325,14 @@ "node": ">=0.10.0" } }, + "node_modules/use-memo-one": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/use-memo-one/-/use-memo-one-1.1.2.tgz", + "integrity": "sha512-u2qFKtxLsia/r8qG0ZKkbytbztzRb317XCkT7yP8wxL0tZ/CzK2G+WWie5vWvpyeP7+YoPIwbJoIHJ4Ba4k0oQ==", + "peerDependencies": { + "react": "^16.8.0 || ^17.0.0" + } + }, "node_modules/util": { "version": "0.11.1", "resolved": "https://registry.npmjs.org/util/-/util-0.11.1.tgz", @@ -24641,7 +24749,6 @@ "version": "3.3.1", "resolved": "https://registry.npmjs.org/@types/hoist-non-react-statics/-/hoist-non-react-statics-3.3.1.tgz", "integrity": "sha512-iMIqiko6ooLrTh1joXodJK5X9xeEALT1kM5G3ZLhD3hszxBdIEd5C75U834D9mLcINgD4OyZf5uQXjkuYydWvA==", - "dev": true, "requires": { "@types/react": "*", "hoist-non-react-statics": "^3.3.0" @@ -24841,6 +24948,15 @@ "csstype": "^3.0.2" } }, + "@types/react-beautiful-dnd": { + "version": "13.1.2", + "resolved": "https://registry.npmjs.org/@types/react-beautiful-dnd/-/react-beautiful-dnd-13.1.2.tgz", + "integrity": "sha512-+OvPkB8CdE/bGdXKyIhc/Lm2U7UAYCCJgsqmopFmh9gbAudmslkI8eOrPDjg4JhwSE6wytz4a3/wRjKtovHVJg==", + "dev": true, + "requires": { + "@types/react": "*" + } + }, "@types/react-dom": { "version": "17.0.11", "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-17.0.11.tgz", @@ -24858,6 +24974,17 @@ "@types/react": "*" } }, + "@types/react-redux": { + "version": "7.1.20", + "resolved": "https://registry.npmjs.org/@types/react-redux/-/react-redux-7.1.20.tgz", + "integrity": "sha512-q42es4c8iIeTgcnB+yJgRTTzftv3eYYvCZOh1Ckn2eX/3o5TdsQYKUWpLoLuGlcY/p+VAhV9IOEZJcWk/vfkXw==", + "requires": { + "@types/hoist-non-react-statics": "^3.3.0", + "@types/react": "*", + "hoist-non-react-statics": "^3.3.0", + "redux": "^4.0.0" + } + }, "@types/react-router": { "version": "5.1.17", "resolved": "https://registry.npmjs.org/@types/react-router/-/react-router-5.1.17.tgz", @@ -27133,6 +27260,14 @@ "postcss": "^7.0.5" } }, + "css-box-model": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/css-box-model/-/css-box-model-1.2.1.tgz", + "integrity": "sha512-a7Vr4Q/kd/aw96bnJG332W9V9LkJO69JRcaCYDUqjp6/z0w6VcZjgAcTbgFxEPfBgdnAwlh3iwu+hLopa+flJw==", + "requires": { + "tiny-invariant": "^1.0.6" + } + }, "css-color-keywords": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/css-color-keywords/-/css-color-keywords-1.0.0.tgz", @@ -32299,6 +32434,11 @@ "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", "integrity": "sha1-hxDXrwqmJvj/+hzgAWhUUmMlV0g=" }, + "memoize-one": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/memoize-one/-/memoize-one-5.2.1.tgz", + "integrity": "sha512-zYiwtZUcYyXKo/np96AGZAckk+FWWsUdJ3cHGGmld7+AhvcWmQyGCYUh1hc4Q/pkOhb65dQR/pqCyK0cOaHz4Q==" + }, "memory-fs": { "version": "0.4.1", "resolved": "https://registry.npmjs.org/memory-fs/-/memory-fs-0.4.1.tgz", @@ -34622,6 +34762,11 @@ "performance-now": "^2.1.0" } }, + "raf-schd": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/raf-schd/-/raf-schd-4.0.3.tgz", + "integrity": "sha512-tQkJl2GRWh83ui2DiPTJz9wEiMN20syf+5oKfB03yYP7ioZcJwsIK8FjrtLwH1m7C7e+Tt2yYBlrOpdT+dyeIQ==" + }, "randombytes": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz", @@ -34692,6 +34837,20 @@ "whatwg-fetch": "^3.4.1" } }, + "react-beautiful-dnd": { + "version": "13.1.0", + "resolved": "https://registry.npmjs.org/react-beautiful-dnd/-/react-beautiful-dnd-13.1.0.tgz", + "integrity": "sha512-aGvblPZTJowOWUNiwd6tNfEpgkX5OxmpqxHKNW/4VmvZTNTbeiq7bA3bn5T+QSF2uibXB0D1DmJsb1aC/+3cUA==", + "requires": { + "@babel/runtime": "^7.9.2", + "css-box-model": "^1.2.0", + "memoize-one": "^5.1.1", + "raf-schd": "^4.0.2", + "react-redux": "^7.2.0", + "redux": "^4.0.4", + "use-memo-one": "^1.1.1" + } + }, "react-dev-utils": { "version": "11.0.4", "resolved": "https://registry.npmjs.org/react-dev-utils/-/react-dev-utils-11.0.4.tgz", @@ -34837,6 +34996,29 @@ "match-sorter": "^6.0.2" } }, + "react-redux": { + "version": "7.2.6", + "resolved": "https://registry.npmjs.org/react-redux/-/react-redux-7.2.6.tgz", + "integrity": "sha512-10RPdsz0UUrRL1NZE0ejTkucnclYSgXp5q+tB5SWx2qeG2ZJQJyymgAhwKy73yiL/13btfB6fPr+rgbMAaZIAQ==", + "requires": { + "@babel/runtime": "^7.15.4", + "@types/react-redux": "^7.1.20", + "hoist-non-react-statics": "^3.3.2", + "loose-envify": "^1.4.0", + "prop-types": "^15.7.2", + "react-is": "^17.0.2" + }, + "dependencies": { + "@babel/runtime": { + "version": "7.16.3", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.16.3.tgz", + "integrity": "sha512-WBwekcqacdY2e9AF/Q7WLFUWmdJGJTkbjqTjoMDgXkVZ3ZRUvOPsLb5KdwISoQVsbP+DQzVZW4Zhci0DvpbNTQ==", + "requires": { + "regenerator-runtime": "^0.13.4" + } + } + } + }, "react-refresh": { "version": "0.8.3", "resolved": "https://registry.npmjs.org/react-refresh/-/react-refresh-0.8.3.tgz", @@ -35108,6 +35290,14 @@ "strip-indent": "^3.0.0" } }, + "redux": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/redux/-/redux-4.1.2.tgz", + "integrity": "sha512-SH8PglcebESbd/shgf6mii6EIoRM0zrQyjcuQ+ojmfxjTtE0z9Y8pa62iA/OJ58qjP6j27uyW4kUF4jl/jd6sw==", + "requires": { + "@babel/runtime": "^7.9.2" + } + }, "regenerate": { "version": "1.4.2", "resolved": "https://registry.npmjs.org/regenerate/-/regenerate-1.4.2.tgz", @@ -37637,6 +37827,12 @@ "resolved": "https://registry.npmjs.org/use/-/use-3.1.1.tgz", "integrity": "sha512-cwESVXlO3url9YWlFW/TA9cshCEhtu7IKJ/p5soJ/gGpj7vbvFrAY/eIioQ6Dw23KjZhYgiIo8HOs1nQ2vr/oQ==" }, + "use-memo-one": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/use-memo-one/-/use-memo-one-1.1.2.tgz", + "integrity": "sha512-u2qFKtxLsia/r8qG0ZKkbytbztzRb317XCkT7yP8wxL0tZ/CzK2G+WWie5vWvpyeP7+YoPIwbJoIHJ4Ba4k0oQ==", + "requires": {} + }, "util": { "version": "0.11.1", "resolved": "https://registry.npmjs.org/util/-/util-0.11.1.tgz", diff --git a/package.json b/package.json index deec3d2..b36c117 100644 --- a/package.json +++ b/package.json @@ -13,6 +13,7 @@ "apexcharts": "^3.31.0", "react": "^17.0.2", "react-apexcharts": "^1.3.9", + "react-beautiful-dnd": "^13.1.0", "react-dom": "^17.0.2", "react-helmet": "^6.1.0", "react-hook-form": "^7.20.5", @@ -49,6 +50,7 @@ ] }, "devDependencies": { + "@types/react-beautiful-dnd": "^13.1.2", "@types/react-helmet": "^6.1.4", "@types/react-router-dom": "^5.3.2", "@types/styled-components": "^5.1.15" diff --git a/src/App.tsx b/src/App.tsx index 92d8d01..e62a99b 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -1,4 +1,24 @@ +import { DragDropContext, Draggable, Droppable } from "react-beautiful-dnd"; + function App() { - return null; + const onDragEnd = () => {}; + return ( + +
+ + {() => ( +
    + + {() =>
  • One
  • } +
    + + {() =>
  • Two
  • } +
    +
+ )} +
+
+
+ ); } export default App; From 8d6fb9753e315d2fd1f9539e9b6769d5aa052cd4 Mon Sep 17 00:00:00 2001 From: GilPOP Date: Mon, 6 Dec 2021 22:06:45 +0900 Subject: [PATCH 03/24] trello react-beautiful-dnd finish --- src/App.tsx | 145 ++++++++++++++++++++++++---- src/Components/Board.tsx | 150 +++++++++++++++++++++++++++++ src/Components/CreateDraggable.tsx | 43 +++++++++ src/atoms.ts | 19 ++++ src/index.tsx | 2 +- src/styled.d.ts | 6 +- src/theme.ts | 8 +- 7 files changed, 345 insertions(+), 28 deletions(-) create mode 100644 src/Components/Board.tsx create mode 100644 src/Components/CreateDraggable.tsx create mode 100644 src/atoms.ts diff --git a/src/App.tsx b/src/App.tsx index e62a99b..e432ad9 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -1,24 +1,133 @@ -import { DragDropContext, Draggable, Droppable } from "react-beautiful-dnd"; +import React from "react"; +import { DragDropContext, Droppable, DropResult } from "react-beautiful-dnd"; +import { Helmet } from "react-helmet"; +import { useForm } from "react-hook-form"; +import { useRecoilState } from "recoil"; +import styled from "styled-components"; +import { IToDoProps, toDoState, USERTODOLIST_KEY } from "./atoms"; +import Board from "./Components/Board"; +import CreateDraggable from "./Components/CreateDraggable"; + +const Wrapper = styled.div` + display: flex; + margin: 0 auto; + max-width: 700px; + width: 100%; + justify-content: center; + align-items: center; + height: 100vh; + flex-wrap: wrap; +`; + +const Boards = styled.div` + display: grid; + grid-template-columns: repeat(3, 1fr); + grid-auto-rows: minmax(250px, 1fr); + gap: 10px; + width: 100%; +`; + +const Form = styled.form` + max-width: 300px; + display: flex; + flex-direction: column; + background-color: black; + padding: 20px; + border-radius: 15px; +`; + +const Input = styled.input``; + +const Button = styled.button``; function App() { - const onDragEnd = () => {}; + const [toDos, setToDos] = useRecoilState(toDoState); + const { register, handleSubmit, setValue } = useForm(); + const onDragEnd = (info: DropResult) => { + // console.log(info); + const { draggableId, destination, source } = info; + if (destination === null) { + setToDos((allBoards) => { + const newBoard = [...allBoards[source.droppableId]]; + newBoard.splice(source.index, 1); + return { ...allBoards, [source.droppableId]: newBoard }; + }); + } + if (!destination) return; + //console.log(toDos); + if (destination?.droppableId === source.droppableId) { + setToDos((allBoards) => { + const newBoard = [...allBoards[source.droppableId]]; + //console.log(newBoard); + const taskObj = newBoard[source.index]; + //console.log(taskObj); + newBoard.splice(source.index, 1); + newBoard.splice(destination?.index, 0, taskObj); + //console.log(newBoard); + return { ...allBoards, [source.droppableId]: newBoard }; + }); + } + if (destination?.droppableId !== source.droppableId) { + setToDos((allBoards) => { + const newBoard = [...allBoards[destination.droppableId]]; + const removeBoard = [...allBoards[source.droppableId]]; + const taskObj = removeBoard[source.index]; + removeBoard.splice(source.index, 1); + newBoard.splice(destination.index, 0, taskObj); + //console.log(removeBoard); + //console.log(toDos); + + return { + ...allBoards, + [source.droppableId]: removeBoard, + [destination.droppableId]: newBoard, + }; + }); + } + }; + + interface IAddBoard { + board: string; + } + + const onVaild = ({ board }: IAddBoard) => { + console.log(board); + const newBoard = { [board]: [] }; + console.log(newBoard); + setToDos((allBoards) => { + return { ...allBoards, ...newBoard }; + }); + console.log(localStorage.getItem(USERTODOLIST_KEY)); + setValue("board", ""); + }; + localStorage.setItem(USERTODOLIST_KEY, JSON.stringify(toDos)); return ( - -
- - {() => ( -
    - - {() =>
  • One
  • } -
    - - {() =>
  • Two
  • } -
    -
- )} -
-
-
+ <> + + + +
(onVaild)}> + + +
+ + + + {Object.keys(toDos).map((boardId) => ( + + ))} + + + + ); } export default App; diff --git a/src/Components/Board.tsx b/src/Components/Board.tsx new file mode 100644 index 0000000..03aa50e --- /dev/null +++ b/src/Components/Board.tsx @@ -0,0 +1,150 @@ +import React, { useRef } from "react"; +import { Droppable } from "react-beautiful-dnd"; +import { useForm } from "react-hook-form"; +import { useRecoilValue, useSetRecoilState } from "recoil"; +import styled from "styled-components"; +import { IToDoProps, toDoState, USERTODOLIST_KEY } from "../atoms"; +import CreateDraggable from "./CreateDraggable"; + +const Wrapper = styled.div` + padding: 10px 0px; + background-color: ${(props) => props.theme.boardColor}; + width: 300px; + border-radius: 5px; + min-height: 300px; + display: flex; + flex-direction: column; +`; + +const Title = styled.div` + text-align: center; + font-weight: 600; + font-size: 18px; +`; + +interface IAreaProps { + isDraggingOver: boolean; + draggingFromThisWith: boolean; +} + +const Form = styled.form` + width: 100%; + input { + width: 100%; + } +`; + +const TitleDiv = styled.div` + display: flex; + justify-content: space-between; + align-items: center; + margin-bottom: 10px; + padding: 0px 10px; +`; + +const Button = styled.button` + border: none; + background-color: ${(props) => props.theme.boardColor}; + color: black; + padding: 0; + font-size: 28px; + position: relative; + i { + border: none; + background-color: none; + padding: none; + &:hover { + color: #f783ac; + } + } +`; + +const Area = styled.div` + background-color: ${(props) => + props.isDraggingOver + ? "#FCD1D1" + : props.draggingFromThisWith + ? "#D3E0DC" + : "#ECE2E1"}; + flex-grow: 1; + transition: background-color 0.3s ease-in-out; + padding: 20px; +`; + +interface IBoardProps { + toDos: IToDoProps[]; + boardId: string; +} + +interface IFormProps { + toDo: string; +} + +function Board({ toDos, boardId }: IBoardProps) { + const { register, setValue, handleSubmit } = useForm(); + const toDosa = useRecoilValue(toDoState); + const SetToDos = useSetRecoilState(toDoState); + const onClick = (event: React.FormEvent) => { + //console.log(event); + //console.log(toDos); + //console.log(boardId); + //console.log(toDosa); + SetToDos((allboards) => { + const newBoards = { ...allboards }; + delete newBoards[boardId]; + //console.log(boardId.toString()); + //console.log(newBoards); + return newBoards; + }); + }; + const onVaild = ({ toDo }: IFormProps) => { + //console.log(toDo); + const newToDos = { id: Date.now(), text: toDo }; + SetToDos((allBoards) => { + return { ...allBoards, [boardId]: [newToDos, ...allBoards[boardId]] }; + }); + setValue("toDo", ""); + }; + localStorage.setItem(USERTODOLIST_KEY, JSON.stringify(toDosa)); + //console.log(JSON.parse("asdf")); + return ( + + +
+ {boardId} + +
+
+ +
+ + {(magic, snapshot) => ( + + {toDos.map((toDo, index) => ( + + ))} + {magic.placeholder} + + )} + +
+ ); +} + +export default Board; diff --git a/src/Components/CreateDraggable.tsx b/src/Components/CreateDraggable.tsx new file mode 100644 index 0000000..45dc699 --- /dev/null +++ b/src/Components/CreateDraggable.tsx @@ -0,0 +1,43 @@ +import React from "react"; +import { Draggable } from "react-beautiful-dnd"; +import styled from "styled-components"; + +interface ISnapshot { + isDragging: boolean; +} + +const Card = styled.div` + padding: 10px; + border-radius: 5px; + margin-bottom: 5px; + background-color: ${(props) => + props.isDragging ? "#AEE1E1" : props.theme.cardColor}; + box-shadow: ${(props) => + props.isDragging ? "0px 2px 5px rgba(0,0,0,0.5)" : "none"}; +`; + +interface IDraggableProps { + toDoId: number; + toDoText: string; + index: number; +} + +function CreateDraggable({ toDoId, toDoText, index }: IDraggableProps) { + //console.log(toDo, "has been rendered"); + return ( + + {(magic, snapshot) => ( + + {toDoText} + + )} + + ); +} + +export default React.memo(CreateDraggable); diff --git a/src/atoms.ts b/src/atoms.ts new file mode 100644 index 0000000..51dd3ca --- /dev/null +++ b/src/atoms.ts @@ -0,0 +1,19 @@ +import { atom } from "recoil"; + +export interface ItoDoStateProps { + [key: string]: IToDoProps[]; +} + +export interface IToDoProps { + id: number; + text: string; +} + +export const USERTODOLIST_KEY = "todos"; +const userGetTodo = localStorage.getItem(USERTODOLIST_KEY); +const parseTodo = JSON.parse(userGetTodo as string); + +export const toDoState = atom({ + key: "toDos", + default: userGetTodo !== null ? parseTodo : {}, +}); diff --git a/src/index.tsx b/src/index.tsx index 7644622..44edaca 100644 --- a/src/index.tsx +++ b/src/index.tsx @@ -64,7 +64,7 @@ a{ body{ font-family: 'Source Sans Pro', sans-serif; background-color: ${(props) => props.theme.bgColor}; - color:${(props) => props.theme.textColor} + color: black; } `; diff --git a/src/styled.d.ts b/src/styled.d.ts index 452018c..79f5e85 100644 --- a/src/styled.d.ts +++ b/src/styled.d.ts @@ -5,9 +5,7 @@ import "styled-components"; declare module "styled-components" { export interface DefaultTheme { bgColor: string; - textColor: string; - accentColor: string; - plusColor: string; - minusColor: string; + boardColor: string; + cardColor: string; } } diff --git a/src/theme.ts b/src/theme.ts index fe97910..58cef16 100644 --- a/src/theme.ts +++ b/src/theme.ts @@ -1,9 +1,7 @@ import { DefaultTheme } from "styled-components"; export const theme: DefaultTheme = { - bgColor: "#7f8fa6", - textColor: "#353b48", - accentColor: "#e1b12c", - plusColor: "#A4F0A4", - minusColor: "#FE1F1F", + bgColor: "#3F8CF2", + boardColor: "#DADFE9", + cardColor: "white", }; From c328e8a2a6b3449ec6ccea7688147205818d92e5 Mon Sep 17 00:00:00 2001 From: GilPOP Date: Mon, 6 Dec 2021 22:36:43 +0900 Subject: [PATCH 04/24] css update && focus Blur update --- src/App.tsx | 38 +++++++++++++++++++++++++++----------- src/Components/Board.tsx | 13 ++++++++++++- 2 files changed, 39 insertions(+), 12 deletions(-) diff --git a/src/App.tsx b/src/App.tsx index e432ad9..2d92158 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -1,4 +1,4 @@ -import React from "react"; +import React, { useRef } from "react"; import { DragDropContext, Droppable, DropResult } from "react-beautiful-dnd"; import { Helmet } from "react-helmet"; import { useForm } from "react-hook-form"; @@ -14,15 +14,16 @@ const Wrapper = styled.div` max-width: 700px; width: 100%; justify-content: center; - align-items: center; + align-items: flex-start; height: 100vh; flex-wrap: wrap; `; const Boards = styled.div` display: grid; - grid-template-columns: repeat(3, 1fr); + grid-template-columns: repeat(4, 1fr); grid-auto-rows: minmax(250px, 1fr); + place-content: center center; gap: 10px; width: 100%; `; @@ -31,12 +32,17 @@ const Form = styled.form` max-width: 300px; display: flex; flex-direction: column; - background-color: black; - padding: 20px; - border-radius: 15px; + background-color: ${(props) => props.theme.boardColor}; + padding: 20px 10px; + margin-left: 20px; + margin-top: 20px; + border-radius: 20px; + box-shadow: 2px 5px 5px rgba(0, 0, 0, 0.3); `; -const Input = styled.input``; +const Input = styled.input` + text-align: center; +`; const Button = styled.button``; @@ -91,15 +97,21 @@ function App() { } const onVaild = ({ board }: IAddBoard) => { - console.log(board); + //console.log(board); const newBoard = { [board]: [] }; - console.log(newBoard); + //console.log(newBoard); setToDos((allBoards) => { return { ...allBoards, ...newBoard }; }); - console.log(localStorage.getItem(USERTODOLIST_KEY)); + //console.log(localStorage.getItem(USERTODOLIST_KEY)); setValue("board", ""); }; + + const onFocusClick = (event: React.FocusEvent) => { + setTimeout(() => { + event.target.blur(); + }, 3000); + }; localStorage.setItem(USERTODOLIST_KEY, JSON.stringify(toDos)); return ( <> @@ -111,10 +123,14 @@ function App() {
(onVaild)}>
diff --git a/src/Components/Board.tsx b/src/Components/Board.tsx index 03aa50e..a32e8c1 100644 --- a/src/Components/Board.tsx +++ b/src/Components/Board.tsx @@ -71,6 +71,10 @@ const Area = styled.div` padding: 20px; `; +const Input = styled.input` + text-align: center; +`; + interface IBoardProps { toDos: IToDoProps[]; boardId: string; @@ -105,6 +109,12 @@ function Board({ toDos, boardId }: IBoardProps) { }); setValue("toDo", ""); }; + const onFocusClick = (event: React.FocusEvent) => { + setTimeout(() => { + event.target.blur(); + }, 3000); + }; + localStorage.setItem(USERTODOLIST_KEY, JSON.stringify(toDosa)); //console.log(JSON.parse("asdf")); return ( @@ -117,7 +127,8 @@ function Board({ toDos, boardId }: IBoardProps) {
- Date: Tue, 7 Dec 2021 15:35:41 +0900 Subject: [PATCH 05/24] board move update very hard --- src/App.tsx | 99 ++++++++++++++++++++++++++++++++++++---- src/Components/Board.tsx | 96 +++++++++++++++++++++----------------- 2 files changed, 143 insertions(+), 52 deletions(-) diff --git a/src/App.tsx b/src/App.tsx index 2d92158..0134440 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -1,10 +1,20 @@ import React, { useRef } from "react"; -import { DragDropContext, Droppable, DropResult } from "react-beautiful-dnd"; +import { + DragDropContext, + Draggable, + Droppable, + DropResult, +} from "react-beautiful-dnd"; import { Helmet } from "react-helmet"; import { useForm } from "react-hook-form"; import { useRecoilState } from "recoil"; import styled from "styled-components"; -import { IToDoProps, toDoState, USERTODOLIST_KEY } from "./atoms"; +import { + IToDoProps, + ItoDoStateProps, + toDoState, + USERTODOLIST_KEY, +} from "./atoms"; import Board from "./Components/Board"; import CreateDraggable from "./Components/CreateDraggable"; @@ -50,8 +60,9 @@ function App() { const [toDos, setToDos] = useRecoilState(toDoState); const { register, handleSubmit, setValue } = useForm(); const onDragEnd = (info: DropResult) => { - // console.log(info); - const { draggableId, destination, source } = info; + console.log(info); + const { type, draggableId, destination, source } = info; + if (destination === null) { setToDos((allBoards) => { const newBoard = [...allBoards[source.droppableId]]; @@ -59,9 +70,62 @@ function App() { return { ...allBoards, [source.droppableId]: newBoard }; }); } + if (!destination) return; //console.log(toDos); - if (destination?.droppableId === source.droppableId) { + + if (type === "board") { + if (destination.index === source.index) return; + setToDos((allBoards) => { + const keyList = Object.keys(toDos); + //console.log(allBoards); + const newBoards: ItoDoStateProps = {}; + //console.log(keyList); + // keyList.map((item) => { + // console.log(item); + // newBoards[item] = { ...allBoards[item] }; + // }); + let sourceKey: string, destKey: string; + keyList.map((item, index) => { + if (index === source.index) sourceKey = item; + if (index === destination.index) destKey = item; + }); + + keyList.map((item) => { + if (item === sourceKey) return; + if (item === destKey && source.index > destination.index) { + newBoards[sourceKey] = [...allBoards[sourceKey]]; + } + newBoards[item] = [...allBoards[item]]; + if (item === destKey && source.index < destination.index) { + newBoards[sourceKey] = [...allBoards[sourceKey]]; + } + }); + console.log(newBoards); + // // keyList.forEach((item) => { + // // //console.log(item); + // // if (item === sourceKey) { + // // newToDos[destKey] = [...allBoards[destKey]]; + // // newToDosArr.push((newToDos[destKey] = [...allBoards[destKey]])); + // // } else if (item === destKey) { + // // newToDos[sourceKey] = [...allBoards[sourceKey]]; + // // newToDosArr.push((newToDos[sourceKey] = [...allBoards[sourceKey]])); + // // } else { + // // newToDos[item] = [...allBoards[item]]; + // // newToDosArr.push((newToDos[item] = [...allBoards[item]])); + // // } + // // console.log({ ...newToDosArr }); + // // //console.log(newToDos); + // // }); + return newBoards; + }); + return; + } + + if ( + destination?.droppableId === source.droppableId && + source.droppableId !== "droppableBoards" + ) { setToDos((allBoards) => { const newBoard = [...allBoards[source.droppableId]]; //console.log(newBoard); @@ -73,6 +137,7 @@ function App() { return { ...allBoards, [source.droppableId]: newBoard }; }); } + if (destination?.droppableId !== source.droppableId) { setToDos((allBoards) => { const newBoard = [...allBoards[destination.droppableId]]; @@ -136,11 +201,25 @@ function App() {
- - {Object.keys(toDos).map((boardId) => ( - - ))} - + + {(magic) => ( + + {Object.keys(toDos).map((boardId, index) => ( + + ))} + {magic.placeholder} + + )} + diff --git a/src/Components/Board.tsx b/src/Components/Board.tsx index a32e8c1..1ec083a 100644 --- a/src/Components/Board.tsx +++ b/src/Components/Board.tsx @@ -1,19 +1,22 @@ import React, { useRef } from "react"; -import { Droppable } from "react-beautiful-dnd"; +import { Draggable, Droppable } from "react-beautiful-dnd"; import { useForm } from "react-hook-form"; import { useRecoilValue, useSetRecoilState } from "recoil"; import styled from "styled-components"; import { IToDoProps, toDoState, USERTODOLIST_KEY } from "../atoms"; import CreateDraggable from "./CreateDraggable"; -const Wrapper = styled.div` +const Wrapper = styled.div<{ isDragging: boolean }>` padding: 10px 0px; - background-color: ${(props) => props.theme.boardColor}; + background-color: ${(props) => + props.isDragging ? "#FCD1D1" : props.theme.boardColor}; width: 300px; border-radius: 5px; min-height: 300px; display: flex; flex-direction: column; + transform: none; + flex-grow: 1; `; const Title = styled.div` @@ -78,13 +81,14 @@ const Input = styled.input` interface IBoardProps { toDos: IToDoProps[]; boardId: string; + boardIndex: number; } interface IFormProps { toDo: string; } -function Board({ toDos, boardId }: IBoardProps) { +function Board({ toDos, boardId, boardIndex }: IBoardProps) { const { register, setValue, handleSubmit } = useForm(); const toDosa = useRecoilValue(toDoState); const SetToDos = useSetRecoilState(toDoState); @@ -118,44 +122,52 @@ function Board({ toDos, boardId }: IBoardProps) { localStorage.setItem(USERTODOLIST_KEY, JSON.stringify(toDosa)); //console.log(JSON.parse("asdf")); return ( - - -
- {boardId} - -
-
- -
- - {(magic, snapshot) => ( - - {toDos.map((toDo, index) => ( - - ))} - {magic.placeholder} - - )} - -
+ + {(magic, snapshot) => ( + + +
+ {boardId} + +
+
+ +
+ + {(magic, snapshot) => ( + + {toDos.map((toDo, index) => ( + + ))} + {magic.placeholder} + + )} + +
+ )} +
); } -export default Board; +export default React.memo(Board); From 33539eb1e8226c3f98cffc2af7d4a2d45e8a81ae Mon Sep 17 00:00:00 2001 From: GilPOP Date: Tue, 7 Dec 2021 20:38:33 +0900 Subject: [PATCH 06/24] animation start --- src/App.tsx | 226 +---------------------------- src/Components/Board.tsx | 173 ---------------------- src/Components/CreateDraggable.tsx | 43 ------ src/atoms.ts | 18 --- 4 files changed, 1 insertion(+), 459 deletions(-) delete mode 100644 src/Components/Board.tsx delete mode 100644 src/Components/CreateDraggable.tsx diff --git a/src/App.tsx b/src/App.tsx index 0134440..92d8d01 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -1,228 +1,4 @@ -import React, { useRef } from "react"; -import { - DragDropContext, - Draggable, - Droppable, - DropResult, -} from "react-beautiful-dnd"; -import { Helmet } from "react-helmet"; -import { useForm } from "react-hook-form"; -import { useRecoilState } from "recoil"; -import styled from "styled-components"; -import { - IToDoProps, - ItoDoStateProps, - toDoState, - USERTODOLIST_KEY, -} from "./atoms"; -import Board from "./Components/Board"; -import CreateDraggable from "./Components/CreateDraggable"; - -const Wrapper = styled.div` - display: flex; - margin: 0 auto; - max-width: 700px; - width: 100%; - justify-content: center; - align-items: flex-start; - height: 100vh; - flex-wrap: wrap; -`; - -const Boards = styled.div` - display: grid; - grid-template-columns: repeat(4, 1fr); - grid-auto-rows: minmax(250px, 1fr); - place-content: center center; - gap: 10px; - width: 100%; -`; - -const Form = styled.form` - max-width: 300px; - display: flex; - flex-direction: column; - background-color: ${(props) => props.theme.boardColor}; - padding: 20px 10px; - margin-left: 20px; - margin-top: 20px; - border-radius: 20px; - box-shadow: 2px 5px 5px rgba(0, 0, 0, 0.3); -`; - -const Input = styled.input` - text-align: center; -`; - -const Button = styled.button``; - function App() { - const [toDos, setToDos] = useRecoilState(toDoState); - const { register, handleSubmit, setValue } = useForm(); - const onDragEnd = (info: DropResult) => { - console.log(info); - const { type, draggableId, destination, source } = info; - - if (destination === null) { - setToDos((allBoards) => { - const newBoard = [...allBoards[source.droppableId]]; - newBoard.splice(source.index, 1); - return { ...allBoards, [source.droppableId]: newBoard }; - }); - } - - if (!destination) return; - //console.log(toDos); - - if (type === "board") { - if (destination.index === source.index) return; - setToDos((allBoards) => { - const keyList = Object.keys(toDos); - //console.log(allBoards); - const newBoards: ItoDoStateProps = {}; - //console.log(keyList); - // keyList.map((item) => { - // console.log(item); - // newBoards[item] = { ...allBoards[item] }; - // }); - let sourceKey: string, destKey: string; - keyList.map((item, index) => { - if (index === source.index) sourceKey = item; - if (index === destination.index) destKey = item; - }); - - keyList.map((item) => { - if (item === sourceKey) return; - if (item === destKey && source.index > destination.index) { - newBoards[sourceKey] = [...allBoards[sourceKey]]; - } - newBoards[item] = [...allBoards[item]]; - if (item === destKey && source.index < destination.index) { - newBoards[sourceKey] = [...allBoards[sourceKey]]; - } - }); - console.log(newBoards); - // // keyList.forEach((item) => { - // // //console.log(item); - // // if (item === sourceKey) { - // // newToDos[destKey] = [...allBoards[destKey]]; - // // newToDosArr.push((newToDos[destKey] = [...allBoards[destKey]])); - // // } else if (item === destKey) { - // // newToDos[sourceKey] = [...allBoards[sourceKey]]; - // // newToDosArr.push((newToDos[sourceKey] = [...allBoards[sourceKey]])); - // // } else { - // // newToDos[item] = [...allBoards[item]]; - // // newToDosArr.push((newToDos[item] = [...allBoards[item]])); - // // } - // // console.log({ ...newToDosArr }); - // // //console.log(newToDos); - // // }); - return newBoards; - }); - return; - } - - if ( - destination?.droppableId === source.droppableId && - source.droppableId !== "droppableBoards" - ) { - setToDos((allBoards) => { - const newBoard = [...allBoards[source.droppableId]]; - //console.log(newBoard); - const taskObj = newBoard[source.index]; - //console.log(taskObj); - newBoard.splice(source.index, 1); - newBoard.splice(destination?.index, 0, taskObj); - //console.log(newBoard); - return { ...allBoards, [source.droppableId]: newBoard }; - }); - } - - if (destination?.droppableId !== source.droppableId) { - setToDos((allBoards) => { - const newBoard = [...allBoards[destination.droppableId]]; - const removeBoard = [...allBoards[source.droppableId]]; - const taskObj = removeBoard[source.index]; - removeBoard.splice(source.index, 1); - newBoard.splice(destination.index, 0, taskObj); - //console.log(removeBoard); - //console.log(toDos); - - return { - ...allBoards, - [source.droppableId]: removeBoard, - [destination.droppableId]: newBoard, - }; - }); - } - }; - - interface IAddBoard { - board: string; - } - - const onVaild = ({ board }: IAddBoard) => { - //console.log(board); - const newBoard = { [board]: [] }; - //console.log(newBoard); - setToDos((allBoards) => { - return { ...allBoards, ...newBoard }; - }); - //console.log(localStorage.getItem(USERTODOLIST_KEY)); - setValue("board", ""); - }; - - const onFocusClick = (event: React.FocusEvent) => { - setTimeout(() => { - event.target.blur(); - }, 3000); - }; - localStorage.setItem(USERTODOLIST_KEY, JSON.stringify(toDos)); - return ( - <> - - - -
(onVaild)}> - - -
- - - - {(magic) => ( - - {Object.keys(toDos).map((boardId, index) => ( - - ))} - {magic.placeholder} - - )} - - - - - ); + return null; } export default App; diff --git a/src/Components/Board.tsx b/src/Components/Board.tsx deleted file mode 100644 index 1ec083a..0000000 --- a/src/Components/Board.tsx +++ /dev/null @@ -1,173 +0,0 @@ -import React, { useRef } from "react"; -import { Draggable, Droppable } from "react-beautiful-dnd"; -import { useForm } from "react-hook-form"; -import { useRecoilValue, useSetRecoilState } from "recoil"; -import styled from "styled-components"; -import { IToDoProps, toDoState, USERTODOLIST_KEY } from "../atoms"; -import CreateDraggable from "./CreateDraggable"; - -const Wrapper = styled.div<{ isDragging: boolean }>` - padding: 10px 0px; - background-color: ${(props) => - props.isDragging ? "#FCD1D1" : props.theme.boardColor}; - width: 300px; - border-radius: 5px; - min-height: 300px; - display: flex; - flex-direction: column; - transform: none; - flex-grow: 1; -`; - -const Title = styled.div` - text-align: center; - font-weight: 600; - font-size: 18px; -`; - -interface IAreaProps { - isDraggingOver: boolean; - draggingFromThisWith: boolean; -} - -const Form = styled.form` - width: 100%; - input { - width: 100%; - } -`; - -const TitleDiv = styled.div` - display: flex; - justify-content: space-between; - align-items: center; - margin-bottom: 10px; - padding: 0px 10px; -`; - -const Button = styled.button` - border: none; - background-color: ${(props) => props.theme.boardColor}; - color: black; - padding: 0; - font-size: 28px; - position: relative; - i { - border: none; - background-color: none; - padding: none; - &:hover { - color: #f783ac; - } - } -`; - -const Area = styled.div` - background-color: ${(props) => - props.isDraggingOver - ? "#FCD1D1" - : props.draggingFromThisWith - ? "#D3E0DC" - : "#ECE2E1"}; - flex-grow: 1; - transition: background-color 0.3s ease-in-out; - padding: 20px; -`; - -const Input = styled.input` - text-align: center; -`; - -interface IBoardProps { - toDos: IToDoProps[]; - boardId: string; - boardIndex: number; -} - -interface IFormProps { - toDo: string; -} - -function Board({ toDos, boardId, boardIndex }: IBoardProps) { - const { register, setValue, handleSubmit } = useForm(); - const toDosa = useRecoilValue(toDoState); - const SetToDos = useSetRecoilState(toDoState); - const onClick = (event: React.FormEvent) => { - //console.log(event); - //console.log(toDos); - //console.log(boardId); - //console.log(toDosa); - SetToDos((allboards) => { - const newBoards = { ...allboards }; - delete newBoards[boardId]; - //console.log(boardId.toString()); - //console.log(newBoards); - return newBoards; - }); - }; - const onVaild = ({ toDo }: IFormProps) => { - //console.log(toDo); - const newToDos = { id: Date.now(), text: toDo }; - SetToDos((allBoards) => { - return { ...allBoards, [boardId]: [newToDos, ...allBoards[boardId]] }; - }); - setValue("toDo", ""); - }; - const onFocusClick = (event: React.FocusEvent) => { - setTimeout(() => { - event.target.blur(); - }, 3000); - }; - - localStorage.setItem(USERTODOLIST_KEY, JSON.stringify(toDosa)); - //console.log(JSON.parse("asdf")); - return ( - - {(magic, snapshot) => ( - - -
- {boardId} - -
-
- -
- - {(magic, snapshot) => ( - - {toDos.map((toDo, index) => ( - - ))} - {magic.placeholder} - - )} - -
- )} -
- ); -} - -export default React.memo(Board); diff --git a/src/Components/CreateDraggable.tsx b/src/Components/CreateDraggable.tsx deleted file mode 100644 index 45dc699..0000000 --- a/src/Components/CreateDraggable.tsx +++ /dev/null @@ -1,43 +0,0 @@ -import React from "react"; -import { Draggable } from "react-beautiful-dnd"; -import styled from "styled-components"; - -interface ISnapshot { - isDragging: boolean; -} - -const Card = styled.div` - padding: 10px; - border-radius: 5px; - margin-bottom: 5px; - background-color: ${(props) => - props.isDragging ? "#AEE1E1" : props.theme.cardColor}; - box-shadow: ${(props) => - props.isDragging ? "0px 2px 5px rgba(0,0,0,0.5)" : "none"}; -`; - -interface IDraggableProps { - toDoId: number; - toDoText: string; - index: number; -} - -function CreateDraggable({ toDoId, toDoText, index }: IDraggableProps) { - //console.log(toDo, "has been rendered"); - return ( - - {(magic, snapshot) => ( - - {toDoText} - - )} - - ); -} - -export default React.memo(CreateDraggable); diff --git a/src/atoms.ts b/src/atoms.ts index 51dd3ca..d732f83 100644 --- a/src/atoms.ts +++ b/src/atoms.ts @@ -1,19 +1 @@ import { atom } from "recoil"; - -export interface ItoDoStateProps { - [key: string]: IToDoProps[]; -} - -export interface IToDoProps { - id: number; - text: string; -} - -export const USERTODOLIST_KEY = "todos"; -const userGetTodo = localStorage.getItem(USERTODOLIST_KEY); -const parseTodo = JSON.parse(userGetTodo as string); - -export const toDoState = atom({ - key: "toDos", - default: userGetTodo !== null ? parseTodo : {}, -}); From 3bb050d7ee3401ae215e3aa56917a0a2d03cd437 Mon Sep 17 00:00:00 2001 From: GilPOP Date: Thu, 9 Dec 2021 05:41:09 +0900 Subject: [PATCH 07/24] slider practice --- craco.config.js | 15 +++++ package-lock.json | 159 ++++++++++++++++++++++++++++++++++++++++++++++ package.json | 8 ++- src/App.tsx | 91 +++++++++++++++++++++++++- 4 files changed, 269 insertions(+), 4 deletions(-) create mode 100644 craco.config.js diff --git a/craco.config.js b/craco.config.js new file mode 100644 index 0000000..aec2aab --- /dev/null +++ b/craco.config.js @@ -0,0 +1,15 @@ +module.exports = { + webpack: { + configure: { + module: { + rules: [ + { + type: "javascript/auto", + test: /\.mjs$/, + include: /node_modules/, + }, + ], + }, + }, + }, +}; diff --git a/package-lock.json b/package-lock.json index 56696db..858cd9f 100644 --- a/package-lock.json +++ b/package-lock.json @@ -8,6 +8,7 @@ "name": "react-masterclass", "version": "0.1.0", "dependencies": { + "@craco/craco": "^6.4.2", "@testing-library/jest-dom": "^5.15.1", "@testing-library/react": "^11.2.7", "@testing-library/user-event": "^12.8.3", @@ -16,6 +17,7 @@ "@types/react": "^17.0.36", "@types/react-dom": "^17.0.11", "apexcharts": "^3.31.0", + "framer-motion": "^5.4.1", "react": "^17.0.2", "react-apexcharts": "^1.3.9", "react-beautiful-dnd": "^13.1.0", @@ -1785,6 +1787,27 @@ "node": ">=0.1.95" } }, + "node_modules/@craco/craco": { + "version": "6.4.2", + "resolved": "https://registry.npmjs.org/@craco/craco/-/craco-6.4.2.tgz", + "integrity": "sha512-egIooyvuzKM5dsvWe/U5ISyFpZwLnG9uuTF1fU4s/6b/hE8MvoxyaxKymQKgbtpfOZeH0ebtEP4cbH7xZ4XRbw==", + "dependencies": { + "cosmiconfig": "^7.0.1", + "cross-spawn": "^7.0.0", + "lodash": "^4.17.15", + "semver": "^7.3.2", + "webpack-merge": "^4.2.2" + }, + "bin": { + "craco": "bin/craco.js" + }, + "engines": { + "node": ">=6" + }, + "peerDependencies": { + "react-scripts": "^4.0.0" + } + }, "node_modules/@csstools/convert-colors": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/@csstools/convert-colors/-/convert-colors-1.4.0.tgz", @@ -9606,6 +9629,43 @@ "node": ">=0.10.0" } }, + "node_modules/framer-motion": { + "version": "5.4.1", + "resolved": "https://registry.npmjs.org/framer-motion/-/framer-motion-5.4.1.tgz", + "integrity": "sha512-U4hiU3g5RCaZRTtTTOKStXH1jm+8uiIhM46wjIYQ7fWnc5c1cGlAzo238X7AM3WhU//jtqd1l5EHq32x96Eg4g==", + "dependencies": { + "framesync": "6.0.1", + "hey-listen": "^1.0.8", + "popmotion": "11.0.3", + "style-value-types": "5.0.0", + "tslib": "^2.1.0" + }, + "optionalDependencies": { + "@emotion/is-prop-valid": "^0.8.2" + }, + "peerDependencies": { + "@react-three/fiber": "^7.0.21", + "react": ">=16.8 || ^17.0.0", + "react-dom": ">=16.8 || ^17.0.0", + "three": "^0.135.0" + }, + "peerDependenciesMeta": { + "@react-three/fiber": { + "optional": true + }, + "three": { + "optional": true + } + } + }, + "node_modules/framesync": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/framesync/-/framesync-6.0.1.tgz", + "integrity": "sha512-fUY88kXvGiIItgNC7wcTOl0SNRCVXMKSWW2Yzfmn7EKNc+MpCzcz9DhdHcdjbrtN3c6R4H5dTY2jiCpPdysEjA==", + "dependencies": { + "tslib": "^2.1.0" + } + }, "node_modules/fresh": { "version": "0.5.2", "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", @@ -10073,6 +10133,11 @@ "resolved": "https://registry.npmjs.org/hex-color-regex/-/hex-color-regex-1.1.0.tgz", "integrity": "sha512-l9sfDFsuqtOqKDsQdqrMRk0U85RZc0RtOR9yPI7mRVOa4FsR/BVnZ0shmQRM96Ji99kYZP/7hn1cedc1+ApsTQ==" }, + "node_modules/hey-listen": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/hey-listen/-/hey-listen-1.0.8.tgz", + "integrity": "sha512-COpmrF2NOg4TBWUJ5UVyaCU2A88wEMkUPK4hNqyCkqHbxT92BbvfjoSozkAIIm6XhicGlJHhFdullInrdhwU8Q==" + }, "node_modules/history": { "version": "4.10.1", "resolved": "https://registry.npmjs.org/history/-/history-4.10.1.tgz", @@ -14884,6 +14949,17 @@ "node": ">=6" } }, + "node_modules/popmotion": { + "version": "11.0.3", + "resolved": "https://registry.npmjs.org/popmotion/-/popmotion-11.0.3.tgz", + "integrity": "sha512-Y55FLdj3UxkR7Vl3s7Qr4e9m0onSnP8W7d/xQLsoJM40vs6UKHFdygs6SWryasTZYqugMjm3BepCF4CWXDiHgA==", + "dependencies": { + "framesync": "6.0.1", + "hey-listen": "^1.0.8", + "style-value-types": "5.0.0", + "tslib": "^2.1.0" + } + }, "node_modules/portfinder": { "version": "1.0.28", "resolved": "https://registry.npmjs.org/portfinder/-/portfinder-1.0.28.tgz", @@ -19110,6 +19186,15 @@ "webpack": "^4.0.0 || ^5.0.0" } }, + "node_modules/style-value-types": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/style-value-types/-/style-value-types-5.0.0.tgz", + "integrity": "sha512-08yq36Ikn4kx4YU6RD7jWEv27v4V+PUsOGa4n/as8Et3CuODMJQ00ENeAVXAeydX4Z2j1XHZF1K2sX4mGl18fA==", + "dependencies": { + "hey-listen": "^1.0.8", + "tslib": "^2.1.0" + } + }, "node_modules/styled-components": { "version": "5.3.3", "resolved": "https://registry.npmjs.org/styled-components/-/styled-components-5.3.3.tgz", @@ -21497,6 +21582,14 @@ "node": ">= 4.0.0" } }, + "node_modules/webpack-merge": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/webpack-merge/-/webpack-merge-4.2.2.tgz", + "integrity": "sha512-TUE1UGoTX2Cd42j3krGYqObZbOD+xF7u28WB7tfUordytSjbWTIjK/8V0amkBfTYN4/pB/GIDlJZZ657BGG19g==", + "dependencies": { + "lodash": "^4.17.15" + } + }, "node_modules/webpack-sources": { "version": "1.4.3", "resolved": "https://registry.npmjs.org/webpack-sources/-/webpack-sources-1.4.3.tgz", @@ -23481,6 +23574,18 @@ "minimist": "^1.2.0" } }, + "@craco/craco": { + "version": "6.4.2", + "resolved": "https://registry.npmjs.org/@craco/craco/-/craco-6.4.2.tgz", + "integrity": "sha512-egIooyvuzKM5dsvWe/U5ISyFpZwLnG9uuTF1fU4s/6b/hE8MvoxyaxKymQKgbtpfOZeH0ebtEP4cbH7xZ4XRbw==", + "requires": { + "cosmiconfig": "^7.0.1", + "cross-spawn": "^7.0.0", + "lodash": "^4.17.15", + "semver": "^7.3.2", + "webpack-merge": "^4.2.2" + } + }, "@csstools/convert-colors": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/@csstools/convert-colors/-/convert-colors-1.4.0.tgz", @@ -29542,6 +29647,27 @@ "map-cache": "^0.2.2" } }, + "framer-motion": { + "version": "5.4.1", + "resolved": "https://registry.npmjs.org/framer-motion/-/framer-motion-5.4.1.tgz", + "integrity": "sha512-U4hiU3g5RCaZRTtTTOKStXH1jm+8uiIhM46wjIYQ7fWnc5c1cGlAzo238X7AM3WhU//jtqd1l5EHq32x96Eg4g==", + "requires": { + "@emotion/is-prop-valid": "^0.8.2", + "framesync": "6.0.1", + "hey-listen": "^1.0.8", + "popmotion": "11.0.3", + "style-value-types": "5.0.0", + "tslib": "^2.1.0" + } + }, + "framesync": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/framesync/-/framesync-6.0.1.tgz", + "integrity": "sha512-fUY88kXvGiIItgNC7wcTOl0SNRCVXMKSWW2Yzfmn7EKNc+MpCzcz9DhdHcdjbrtN3c6R4H5dTY2jiCpPdysEjA==", + "requires": { + "tslib": "^2.1.0" + } + }, "fresh": { "version": "0.5.2", "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", @@ -29885,6 +30011,11 @@ "resolved": "https://registry.npmjs.org/hex-color-regex/-/hex-color-regex-1.1.0.tgz", "integrity": "sha512-l9sfDFsuqtOqKDsQdqrMRk0U85RZc0RtOR9yPI7mRVOa4FsR/BVnZ0shmQRM96Ji99kYZP/7hn1cedc1+ApsTQ==" }, + "hey-listen": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/hey-listen/-/hey-listen-1.0.8.tgz", + "integrity": "sha512-COpmrF2NOg4TBWUJ5UVyaCU2A88wEMkUPK4hNqyCkqHbxT92BbvfjoSozkAIIm6XhicGlJHhFdullInrdhwU8Q==" + }, "history": { "version": "4.10.1", "resolved": "https://registry.npmjs.org/history/-/history-4.10.1.tgz", @@ -33520,6 +33651,17 @@ "ts-pnp": "^1.1.6" } }, + "popmotion": { + "version": "11.0.3", + "resolved": "https://registry.npmjs.org/popmotion/-/popmotion-11.0.3.tgz", + "integrity": "sha512-Y55FLdj3UxkR7Vl3s7Qr4e9m0onSnP8W7d/xQLsoJM40vs6UKHFdygs6SWryasTZYqugMjm3BepCF4CWXDiHgA==", + "requires": { + "framesync": "6.0.1", + "hey-listen": "^1.0.8", + "style-value-types": "5.0.0", + "tslib": "^2.1.0" + } + }, "portfinder": { "version": "1.0.28", "resolved": "https://registry.npmjs.org/portfinder/-/portfinder-1.0.28.tgz", @@ -36907,6 +37049,15 @@ "schema-utils": "^2.7.0" } }, + "style-value-types": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/style-value-types/-/style-value-types-5.0.0.tgz", + "integrity": "sha512-08yq36Ikn4kx4YU6RD7jWEv27v4V+PUsOGa4n/as8Et3CuODMJQ00ENeAVXAeydX4Z2j1XHZF1K2sX4mGl18fA==", + "requires": { + "hey-listen": "^1.0.8", + "tslib": "^2.1.0" + } + }, "styled-components": { "version": "5.3.3", "resolved": "https://registry.npmjs.org/styled-components/-/styled-components-5.3.3.tgz", @@ -39002,6 +39153,14 @@ } } }, + "webpack-merge": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/webpack-merge/-/webpack-merge-4.2.2.tgz", + "integrity": "sha512-TUE1UGoTX2Cd42j3krGYqObZbOD+xF7u28WB7tfUordytSjbWTIjK/8V0amkBfTYN4/pB/GIDlJZZ657BGG19g==", + "requires": { + "lodash": "^4.17.15" + } + }, "webpack-sources": { "version": "1.4.3", "resolved": "https://registry.npmjs.org/webpack-sources/-/webpack-sources-1.4.3.tgz", diff --git a/package.json b/package.json index b36c117..12d49dc 100644 --- a/package.json +++ b/package.json @@ -3,6 +3,7 @@ "version": "0.1.0", "private": true, "dependencies": { + "@craco/craco": "^6.4.2", "@testing-library/jest-dom": "^5.15.1", "@testing-library/react": "^11.2.7", "@testing-library/user-event": "^12.8.3", @@ -11,6 +12,7 @@ "@types/react": "^17.0.36", "@types/react-dom": "^17.0.11", "apexcharts": "^3.31.0", + "framer-motion": "^5.4.1", "react": "^17.0.2", "react-apexcharts": "^1.3.9", "react-beautiful-dnd": "^13.1.0", @@ -26,9 +28,9 @@ "web-vitals": "^1.1.2" }, "scripts": { - "start": "react-scripts start", - "build": "react-scripts build", - "test": "react-scripts test", + "start": "craco start", + "build": "craco build", + "test": "craco test", "eject": "react-scripts eject" }, "eslintConfig": { diff --git a/src/App.tsx b/src/App.tsx index 92d8d01..149b4d9 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -1,4 +1,93 @@ +import { + AnimatePresence, + motion, + motionValue, + useTransform, + Variants, +} from "framer-motion"; +import { useRef, useState } from "react"; +import styled from "styled-components"; + +const Wrapper = styled.div` + height: 100vh; + width: 100vw; + display: flex; + justify-content: center; + align-items: center; + flex-direction: column; +`; + +const BiggerBox = styled.div` + width: 600px; + height: 600px; + background-color: rgba(255, 255, 255, 0.4); + border-radius: 40px; + display: flex; + justify-content: center; + align-items: center; +`; + +const Box = styled(motion.div)` + display: flex; + top: 100px; + justify-content: center; + position: absolute; + align-items: center; + width: 400px; + height: 200px; + background-color: white; + border-radius: 40px; + box-shadow: 0 2px 3px rgba(0, 0, 0, 0.1), 0 10px 20px rgba(0, 0, 0, 0.06); +`; + +const Circle = styled(motion.div)` + place-self: center; + width: 70px; + height: 70px; + border-radius: 35px; + background-color: white; + box-shadow: 0 2px 3px rgba(0, 0, 0, 0.1), 0 10px 20px rgba(0, 0, 0, 0.06); +`; + +const BoxVariants: Variants = { + initial: (back: boolean) => ({ scale: 0, opacity: 0, x: back ? -500 : 500 }), + visiable: { scale: 1, opacity: 1, x: 0 }, + leaving: (back: boolean) => ({ scale: 0, opacity: 0, x: back ? 500 : -500 }), + hover: { rotateZ: 90 }, + click: { borderRadius: "100px" }, +}; + function App() { - return null; + const biggerBoxRef = useRef(null); + const x = motionValue(0); + const scale = useTransform(x, [-800, 0, 800], [2, 1, 0.1]); + const [value, setValue] = useState(1); + const [back, setBack] = useState(false); + const nextToggle = () => { + setBack(false); + setValue((current) => (current === 10 ? 10 : current + 1)); + }; + const prevToggle = () => { + setBack(true); + setValue((current) => (current === 1 ? 1 : current - 1)); + }; + return ( + + + + {value} + + + + + + ); } export default App; From aa6837c0efe49a8cb9fed919ac8da56e42ac4ce7 Mon Sep 17 00:00:00 2001 From: GilPOP Date: Thu, 9 Dec 2021 05:43:59 +0900 Subject: [PATCH 08/24] graduate challenge start --- src/App.tsx | 92 ++----------------------------------------------- src/index.tsx | 2 +- src/styled.d.ts | 13 +++++-- src/theme.ts | 13 +++++-- 4 files changed, 23 insertions(+), 97 deletions(-) diff --git a/src/App.tsx b/src/App.tsx index 149b4d9..b56b979 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -1,93 +1,5 @@ -import { - AnimatePresence, - motion, - motionValue, - useTransform, - Variants, -} from "framer-motion"; -import { useRef, useState } from "react"; -import styled from "styled-components"; - -const Wrapper = styled.div` - height: 100vh; - width: 100vw; - display: flex; - justify-content: center; - align-items: center; - flex-direction: column; -`; - -const BiggerBox = styled.div` - width: 600px; - height: 600px; - background-color: rgba(255, 255, 255, 0.4); - border-radius: 40px; - display: flex; - justify-content: center; - align-items: center; -`; - -const Box = styled(motion.div)` - display: flex; - top: 100px; - justify-content: center; - position: absolute; - align-items: center; - width: 400px; - height: 200px; - background-color: white; - border-radius: 40px; - box-shadow: 0 2px 3px rgba(0, 0, 0, 0.1), 0 10px 20px rgba(0, 0, 0, 0.06); -`; - -const Circle = styled(motion.div)` - place-self: center; - width: 70px; - height: 70px; - border-radius: 35px; - background-color: white; - box-shadow: 0 2px 3px rgba(0, 0, 0, 0.1), 0 10px 20px rgba(0, 0, 0, 0.06); -`; - -const BoxVariants: Variants = { - initial: (back: boolean) => ({ scale: 0, opacity: 0, x: back ? -500 : 500 }), - visiable: { scale: 1, opacity: 1, x: 0 }, - leaving: (back: boolean) => ({ scale: 0, opacity: 0, x: back ? 500 : -500 }), - hover: { rotateZ: 90 }, - click: { borderRadius: "100px" }, -}; - function App() { - const biggerBoxRef = useRef(null); - const x = motionValue(0); - const scale = useTransform(x, [-800, 0, 800], [2, 1, 0.1]); - const [value, setValue] = useState(1); - const [back, setBack] = useState(false); - const nextToggle = () => { - setBack(false); - setValue((current) => (current === 10 ? 10 : current + 1)); - }; - const prevToggle = () => { - setBack(true); - setValue((current) => (current === 1 ? 1 : current - 1)); - }; - return ( - - - - {value} - - - - - - ); + return null; } + export default App; diff --git a/src/index.tsx b/src/index.tsx index 44edaca..87c5944 100644 --- a/src/index.tsx +++ b/src/index.tsx @@ -63,7 +63,7 @@ a{ body{ font-family: 'Source Sans Pro', sans-serif; - background-color: ${(props) => props.theme.bgColor}; + background-color: ${(props) => props.theme.black}; color: black; } `; diff --git a/src/styled.d.ts b/src/styled.d.ts index 79f5e85..eb432dd 100644 --- a/src/styled.d.ts +++ b/src/styled.d.ts @@ -4,8 +4,15 @@ import "styled-components"; // and extend them! declare module "styled-components" { export interface DefaultTheme { - bgColor: string; - boardColor: string; - cardColor: string; + red: string; + black: { + veryDark: string; + darker: string; + lighter: string; + }; + white: { + darker: string; + lighter: string; + }; } } diff --git a/src/theme.ts b/src/theme.ts index 58cef16..edddbe0 100644 --- a/src/theme.ts +++ b/src/theme.ts @@ -1,7 +1,14 @@ import { DefaultTheme } from "styled-components"; export const theme: DefaultTheme = { - bgColor: "#3F8CF2", - boardColor: "#DADFE9", - cardColor: "white", + red: "#E51013", + black: { + veryDark: "#141414", + darker: "#181818", + lighter: "#2F2F2F", + }, + white: { + lighter: "#fff", + darker: "#e5e5e5", + }, }; From bf364a299292874dfcb07610959285f42288f2d3 Mon Sep 17 00:00:00 2001 From: GilPOP Date: Thu, 9 Dec 2021 22:46:47 +0900 Subject: [PATCH 09/24] header ,home slider finish --- src/App.tsx | 23 ++++- src/Components/Header.tsx | 174 ++++++++++++++++++++++++++++++++++ src/Routes/Home.tsx | 195 ++++++++++++++++++++++++++++++++++++++ src/Routes/Search.tsx | 5 + src/Routes/Tv.tsx | 5 + src/api.ts | 30 ++++++ src/index.tsx | 24 +++-- src/utils.ts | 3 + 8 files changed, 448 insertions(+), 11 deletions(-) create mode 100644 src/Components/Header.tsx create mode 100644 src/Routes/Home.tsx create mode 100644 src/Routes/Search.tsx create mode 100644 src/Routes/Tv.tsx create mode 100644 src/api.ts create mode 100644 src/utils.ts diff --git a/src/App.tsx b/src/App.tsx index b56b979..c37df7b 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -1,5 +1,26 @@ +import { BrowserRouter as Router, Route, Switch } from "react-router-dom"; +import Header from "./Components/Header"; +import Home from "./Routes/Home"; +import Search from "./Routes/Search"; +import Tv from "./Routes/Tv"; + function App() { - return null; + return ( + +
+ + + + + + + + + + + + + ); } export default App; diff --git a/src/Components/Header.tsx b/src/Components/Header.tsx new file mode 100644 index 0000000..3ad84c9 --- /dev/null +++ b/src/Components/Header.tsx @@ -0,0 +1,174 @@ +import { + motion, + useAnimation, + useViewportScroll, + Variants, +} from "framer-motion"; +import { useEffect, useState } from "react"; +import { useRouteMatch } from "react-router"; +import { Link } from "react-router-dom"; +import styled from "styled-components"; + +const Nav = styled(motion.div)` + display: flex; + justify-content: space-between; + align-items: center; + position: fixed; + width: 100%; + top: 0; + background-color: black; + font-size: 14px; + padding: 20px 60px; + color: white; +`; + +const Col = styled.div` + display: flex; + align-items: center; +`; + +const Logo = styled(motion.svg)` + margin-right: 50px; + width: 95px; + height: 25px; + fill: ${(props) => props.theme.red}; + + path { + stroke-width: 6px; + stroke: white; + } +`; + +const Circle = styled(motion.span)` + width: 5px; + height: 5px; + border-radius: 2.5px; + background-color: ${(props) => props.theme.red}; + position: absolute; + bottom: -10px; + left: 0; + right: 0; + margin: 0 auto; +`; + +const Items = styled.ul` + display: flex; + align-items: center; +`; + +const Item = styled.li` + margin-right: 20px; + color: ${(props) => props.theme.white.darker}; + transition: color 0.3s ease-in-out; + position: relative; + display: flex; + justify-content: center; + flex-direction: column; + &:hover { + color: ${(props) => props.theme.white.lighter}; + } +`; + +const Search = styled.span` + color: white; + position: relative; + display: flex; + align-items: center; + svg { + height: 25px; + } +`; + +const Input = styled(motion.input)` + transform-origin: right center; + position: absolute; + padding: 5px 10px; + right: 0; + padding-left: 40px; + z-index: -1; + color: white; + font-size: 16px; + background-color: transparent; + border: 1px solid ${(props) => props.theme.white.lighter}; +`; + +const LogoVariants: Variants = { + normal: { fillOpacity: 1 }, + active: { fillOpacity: [0, 1, 0], transition: { repeat: Infinity } }, +}; + +function Header() { + const [openSearch, setOpenSearch] = useState(false); + const homeMatch = useRouteMatch("/"); + const tvMatch = useRouteMatch("/tv"); + const navAnimation = useAnimation(); + // console.log(homeMatch, tvMatch); + const { scrollY } = useViewportScroll(); + const toggleSearch = () => { + setOpenSearch((current) => !current); + }; + useEffect(() => { + scrollY.onChange(() => { + if (scrollY.get() > 80) { + navAnimation.start({ backgroundColor: "rgba(0,0,0,1)" }); + } else { + navAnimation.start({ backgroundColor: "rgba(0,0,0,0)" }); + } + }); + }, [scrollY, navAnimation]); + return ( + + ); +} + +export default Header; diff --git a/src/Routes/Home.tsx b/src/Routes/Home.tsx new file mode 100644 index 0000000..777c757 --- /dev/null +++ b/src/Routes/Home.tsx @@ -0,0 +1,195 @@ +import { + AnimatePresence, + motion, + useViewportScroll, + Variants, +} from "framer-motion"; +import { useState } from "react"; +import { useQuery } from "react-query"; +import { useHistory, useRouteMatch } from "react-router"; +import styled from "styled-components"; +import { getMovies, IGetMoviesProps } from "../api"; +import { makeImageHelper } from "../utils"; + +const Wrapper = styled.div` + background-color: black; +`; + +const Banner = styled.div<{ isBack: string }>` + background-image: linear-gradient(rgba(0, 0, 0, 0), rgba(0, 0, 0, 1)), + url(${(props) => props.isBack}); + background-size: cover; + display: flex; + flex-direction: column; + justify-content: center; + height: 100vh; + padding: 60px; +`; + +const Loader = styled.div``; + +const Title = styled.h2` + font-size: 64px; + margin-bottom: 20px; +`; + +const Overview = styled.p` + font-size: 34px; + width: 50%; +`; + +const Slider = styled.div` + position: relative; + top: -100px; +`; + +const Row = styled(motion.div)` + display: grid; + grid-template-columns: repeat(6, 1fr); + gap: 5px; + width: 100%; + position: absolute; +`; + +const Box = styled(motion.div)<{ bgPhoto: string }>` + background-color: white; + height: 200px; + font-size: 66px; + background-image: url(${(props) => props.bgPhoto}); + background-position: center center; + background-size: cover; + cursor: pointer; + &:first-child { + transform-origin: center left; + } + &:last-child { + transform-origin: center right; + } +`; + +const Overlay = styled(motion.div)` + position: fixed; + top: 0; + width: 100%; + height: 100%; + background-color: rgba(0, 0, 0, 0.5); + opacity: 0; +`; + +const BigMovie = styled(motion.div)` + position: absolute; + width: 40vw; + height: 80vh; + left: 0; + right: 0; + margin: 0 auto; +`; + +const rowVariants: Variants = { + hidden: { x: window.outerWidth + 5 }, + visible: { x: 0 }, + exit: { x: -window.outerWidth - 5 }, +}; + +const boxVariants: Variants = { + normal: { scale: 1 }, + hover: { scale: 1.3, y: -50, transition: { delay: 0.5, type: "tween" } }, +}; + +const offset = 6; + +function Home() { + const history = useHistory(); + const bigMovieMatch = useRouteMatch<{ movieId: string }>("/movies/:movieId"); + const { scrollY } = useViewportScroll(); + const { data, isLoading } = useQuery( + ["movies", "nowPlaying"], + getMovies + ); + const [index, setIndex] = useState(0); + const increaseIndex = () => { + if (data) { + if (leaving) return; + + toggleLeaving(); + const totalMovies = data?.results.length - 1; + const maxIndex = Math.floor(totalMovies / offset) - 1; + setIndex((prev) => (index === maxIndex ? 0 : prev + 1)); + } + }; + const onBoxClicked = (movieId: number) => { + history.push(`/movies/${movieId}`); + }; + const [leaving, setLeaving] = useState(false); + const toggleLeaving = () => { + setLeaving((prev) => !prev); + }; + const onOverlayClicked = () => { + history.push("/"); + }; + //console.log(data, isLoading); + return ( + + {isLoading ? ( + Loading... + ) : ( + <> + + {data?.results[0].title} + {data?.results[0].overview} + + + + + {data?.results + .slice(1) + .slice(offset * index, offset * index + offset) + .map((item) => ( + onBoxClicked(item.id)} + variants={boxVariants} + initial="normal" + whileHover="hover" + transition={{ type: "tween" }} + bgPhoto={makeImageHelper(item.backdrop_path, "w500")} + key={item.id} + > + ))} + + + + + {bigMovieMatch ? ( + <> + + + hello + + + ) : null} + + + )} + + ); +} + +export default Home; diff --git a/src/Routes/Search.tsx b/src/Routes/Search.tsx new file mode 100644 index 0000000..f0e33ca --- /dev/null +++ b/src/Routes/Search.tsx @@ -0,0 +1,5 @@ +function Search() { + return null; +} + +export default Search; diff --git a/src/Routes/Tv.tsx b/src/Routes/Tv.tsx new file mode 100644 index 0000000..9bdb2fc --- /dev/null +++ b/src/Routes/Tv.tsx @@ -0,0 +1,5 @@ +function Tv() { + return null; +} + +export default Tv; diff --git a/src/api.ts b/src/api.ts new file mode 100644 index 0000000..5b77d23 --- /dev/null +++ b/src/api.ts @@ -0,0 +1,30 @@ +const API_KEY = "8ada0ba81365b222c17dc83dc8b3e61d"; +const BASE_URL = "https://api.themoviedb.org/3"; + +export interface IMovie { + backdrop_path: string; + id: number; + original_title: string; + overview: string; + poster_path: string; + title: string; + release_date: string; + vote_average: number; +} + +export interface IGetMoviesProps { + dates: { + maximum: string; + minimum: string; + }; + page: number; + results: IMovie[]; + total_pages: number; + total_results: number; +} + +export function getMovies() { + return fetch(`${BASE_URL}/movie/now_playing?api_key=${API_KEY}`).then( + (response) => response.json() + ); +} diff --git a/src/index.tsx b/src/index.tsx index 87c5944..803fec7 100644 --- a/src/index.tsx +++ b/src/index.tsx @@ -1,6 +1,7 @@ import React from "react"; import ReactDOM from "react-dom"; import { QueryClient, QueryClientProvider } from "react-query"; +import { BrowserRouter } from "react-router-dom"; import { RecoilRoot } from "recoil"; import { createGlobalStyle, ThemeProvider } from "styled-components"; import App from "./App"; @@ -63,21 +64,24 @@ a{ body{ font-family: 'Source Sans Pro', sans-serif; - background-color: ${(props) => props.theme.black}; - color: black; + background-color: ${(props) => props.theme.black.veryDark}; + color: ${(props) => props.theme.white.darker}; + height: 200vh; } `; ReactDOM.render( - - - - - - - - + + + + + + + + + + , document.getElementById("root") ); diff --git a/src/utils.ts b/src/utils.ts new file mode 100644 index 0000000..fcbd388 --- /dev/null +++ b/src/utils.ts @@ -0,0 +1,3 @@ +export function makeImageHelper(id: string, format?: string) { + return `https://image.tmdb.org/t/p/${format ? format : "original"}/${id}`; +} From 7eef43a10ea20155e8505d2899b4a564bcb696f1 Mon Sep 17 00:00:00 2001 From: GilPOP Date: Fri, 10 Dec 2021 05:29:50 +0900 Subject: [PATCH 10/24] simple finish --- src/Components/Header.tsx | 18 +++++++-- src/Routes/Home.tsx | 77 +++++++++++++++++++++++++++++++++++++-- src/Routes/Search.tsx | 6 +++ 3 files changed, 95 insertions(+), 6 deletions(-) diff --git a/src/Components/Header.tsx b/src/Components/Header.tsx index 3ad84c9..4f17647 100644 --- a/src/Components/Header.tsx +++ b/src/Components/Header.tsx @@ -5,7 +5,8 @@ import { Variants, } from "framer-motion"; import { useEffect, useState } from "react"; -import { useRouteMatch } from "react-router"; +import { useForm } from "react-hook-form"; +import { useHistory, useRouteMatch } from "react-router"; import { Link } from "react-router-dom"; import styled from "styled-components"; @@ -69,7 +70,7 @@ const Item = styled.li` } `; -const Search = styled.span` +const Search = styled.form` color: white; position: relative; display: flex; @@ -97,7 +98,13 @@ const LogoVariants: Variants = { active: { fillOpacity: [0, 1, 0], transition: { repeat: Infinity } }, }; +interface IForm { + keyword: string; +} + function Header() { + const { register, handleSubmit } = useForm(); + const history = useHistory(); const [openSearch, setOpenSearch] = useState(false); const homeMatch = useRouteMatch("/"); const tvMatch = useRouteMatch("/tv"); @@ -116,6 +123,10 @@ function Header() { } }); }, [scrollY, navAnimation]); + const onValid = (data: IForm) => { + console.log(data); + history.push(`/search?keyword=${data.keyword}`); + }; return (