├── .github └── workflows │ └── build-deploy.yml ├── .gitignore ├── .prettierrc ├── CNAME ├── README.md ├── package-lock.json ├── package.json ├── public ├── favicon.ico ├── index.html ├── logo192.png ├── logo512.png ├── manifest.json └── robots.txt ├── src ├── App.tsx ├── assets │ └── images │ │ └── react-stripe-dropdown.png ├── components │ ├── DropDown │ │ ├── CompanyComponent.tsx │ │ ├── DevelopersComponent.tsx │ │ ├── DropdownContent.tsx │ │ └── ProductsComponent.tsx │ └── Navbar │ │ ├── Container.style.tsx │ │ ├── NavItems.tsx │ │ └── Navbar.tsx ├── index.tsx ├── react-app-env.d.ts ├── serviceWorker.js └── styles │ └── GlobalStyles.ts ├── tsconfig.json └── yarn.lock /.github/workflows/build-deploy.yml: -------------------------------------------------------------------------------- 1 | name: Build and Deploy 2 | on: [push] 3 | 4 | jobs: 5 | build-and-deploy: 6 | runs-on: ubuntu-latest 7 | steps: 8 | - name: Checkout 9 | uses: actions/checkout@master 10 | 11 | - name: Build and Deploy 12 | uses: JamesIves/github-pages-deploy-action@releases/v2 13 | env: 14 | ACCESS_TOKEN: ${{ secrets.ACCESS_TOKEN }} 15 | BASE_BRANCH: master # The branch the action should deploy from. 16 | BRANCH: gh-pages # The branch the action should deploy to. 17 | FOLDER: build # The folder the action should deploy. 18 | BUILD_SCRIPT: npm install && npm run-script build # The build script the action should run prior to deploying. 19 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files. 2 | 3 | # dependencies 4 | /node_modules 5 | /.pnp 6 | .pnp.js 7 | 8 | # testing 9 | /coverage 10 | 11 | # production 12 | /build 13 | 14 | # misc 15 | .DS_Store 16 | .env.local 17 | .env.development.local 18 | .env.test.local 19 | .env.production.local 20 | 21 | npm-debug.log* 22 | yarn-debug.log* 23 | yarn-error.log* 24 | -------------------------------------------------------------------------------- /.prettierrc: -------------------------------------------------------------------------------- 1 | {} -------------------------------------------------------------------------------- /CNAME: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/anuraghazra/react-stripe-dropdown/6e07c3594f5ba676bd3598ba7c7c95cbe01b47c3/CNAME -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## React Stripe Inspired Dropdown 2 | 3 | (Warning: This Dropdown is hugely inaccessible, do not reenginner and use it in your project) 4 | 5 | Stripe Inspired Dropdown made with React and FramerMotion. 6 | 7 | **demo:** https://anuraghazra.github.io/react-stripe-dropdown 8 | 9 | ![React Stripe Inspired Dropdown](./src/assets/images/react-stripe-dropdown.png) 10 | 11 | ### :wrench: Tools Used 12 | - [React](http://reactjs.org/) 13 | - [Framer Motion](https://www.framer.com/motion/) 14 | - [Styled Components](https://www.styled-components.com/) 15 | - [normalize.css](https://www.npmjs.com/package/normalize.css) 16 | 17 | 18 | ## :v: Contributing 19 | Great!, 20 | after cloning & setting up the local project you can push the changes to your github fork and make a pull request. 21 | 22 | ----- 23 | 24 | **local development** 25 | 26 | ### Step 1: Clone The Repo 27 | 28 | Fork the repository. then clone the repo locally by doing - 29 | 30 | ```bash 31 | git clone https://github.com/anuraghazra/react-stripe-dropdown.git 32 | ``` 33 | 34 | ### Step 2: Install Dependencies 35 | 36 | cd into the directory 37 | 38 | ```bash 39 | cd react-stripe-dropdown 40 | ``` 41 | 42 | install all the dependencies 43 | ```bash 44 | npm install 45 | ``` 46 | 47 | ### Step 3: Start Development Server 48 | 49 | Then start the development Server 50 | ``` 51 | npm start 52 | ``` 53 | After running the development server the site should be running on https://localhost:3000 54 | 55 | 56 | Give the project a :star: if you liked it. 57 | Made with :heart: and React. 58 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "stripe-dropdown-menu", 3 | "version": "0.1.0", 4 | "private": true, 5 | "homepage": "https://anuraghazra.github.io/react-stripe-dropdown", 6 | "dependencies": { 7 | "@types/jest": "^24.0.22", 8 | "@types/node": "^12.12.7", 9 | "@types/react": "^16.9.11", 10 | "@types/react-dom": "^16.9.4", 11 | "@types/styled-components": "^4.4.0", 12 | "framer-motion": "^1.6.15", 13 | "normalize.css": "^8.0.1", 14 | "react": "^16.11.0", 15 | "react-dom": "^16.11.0", 16 | "react-scripts": "3.2.0", 17 | "styled-components": "^4.4.1", 18 | "typescript": "^3.7.2" 19 | }, 20 | "scripts": { 21 | "predeploy": "npm run build", 22 | "deploy": "gh-pages -d build", 23 | "start": "react-scripts start", 24 | "build": "react-scripts build", 25 | "test": "react-scripts test", 26 | "eject": "react-scripts eject" 27 | }, 28 | "eslintConfig": { 29 | "extends": "react-app" 30 | }, 31 | "browserslist": { 32 | "production": [ 33 | ">0.2%", 34 | "not dead", 35 | "not op_mini all" 36 | ], 37 | "development": [ 38 | "last 1 chrome version", 39 | "last 1 firefox version", 40 | "last 1 safari version" 41 | ] 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/anuraghazra/react-stripe-dropdown/6e07c3594f5ba676bd3598ba7c7c95cbe01b47c3/public/favicon.ico -------------------------------------------------------------------------------- /public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 12 | 13 | 17 | 18 | 27 | React App 28 | 29 | 30 | 31 |
32 | 42 | 43 | 44 | -------------------------------------------------------------------------------- /public/logo192.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/anuraghazra/react-stripe-dropdown/6e07c3594f5ba676bd3598ba7c7c95cbe01b47c3/public/logo192.png -------------------------------------------------------------------------------- /public/logo512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/anuraghazra/react-stripe-dropdown/6e07c3594f5ba676bd3598ba7c7c95cbe01b47c3/public/logo512.png -------------------------------------------------------------------------------- /public/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "short_name": "React App", 3 | "name": "Create React App Sample", 4 | "icons": [ 5 | { 6 | "src": "favicon.ico", 7 | "sizes": "64x64 32x32 24x24 16x16", 8 | "type": "image/x-icon" 9 | }, 10 | { 11 | "src": "logo192.png", 12 | "type": "image/png", 13 | "sizes": "192x192" 14 | }, 15 | { 16 | "src": "logo512.png", 17 | "type": "image/png", 18 | "sizes": "512x512" 19 | } 20 | ], 21 | "start_url": ".", 22 | "display": "standalone", 23 | "theme_color": "#000000", 24 | "background_color": "#ffffff" 25 | } 26 | -------------------------------------------------------------------------------- /public/robots.txt: -------------------------------------------------------------------------------- 1 | # https://www.robotstxt.org/robotstxt.html 2 | User-agent: * 3 | -------------------------------------------------------------------------------- /src/App.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import Navbar from './components/Navbar/Navbar'; 3 | import GlobalStyles from './styles/GlobalStyles' 4 | 5 | import { ThemeProvider, DefaultTheme } from 'styled-components'; 6 | 7 | // and extend them! 8 | declare module 'styled-components' { 9 | export interface DefaultTheme { 10 | primaryBlack: string 11 | lightGray: string 12 | gray: string 13 | gradient: string 14 | } 15 | } 16 | 17 | const theme: DefaultTheme = { 18 | primaryBlack: '#424770', 19 | lightGray: '#8898aa', 20 | gray: '#696e98', 21 | gradient: 'linear-gradient(45deg, #0ec3ff, #5533ff)' 22 | } 23 | 24 | function App() { 25 | return ( 26 |
27 | 28 | 29 | 30 | 31 | 32 | react-stripe-dropdown | Made With React + FramerMotion; 33 | 34 | 35 |
36 | ); 37 | } 38 | 39 | export default App; 40 | -------------------------------------------------------------------------------- /src/assets/images/react-stripe-dropdown.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/anuraghazra/react-stripe-dropdown/6e07c3594f5ba676bd3598ba7c7c95cbe01b47c3/src/assets/images/react-stripe-dropdown.png -------------------------------------------------------------------------------- /src/components/DropDown/CompanyComponent.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import DropdownContent from './DropdownContent'; 3 | import { IconLink } from './DropdownContent'; 4 | import { motion } from "framer-motion"; 5 | 6 | const container = { 7 | hidden: { opacity: 1, scale: 0 }, 8 | visible: { 9 | opacity: 1, 10 | scale: 1, 11 | transition: { 12 | when: "beforeChildren", 13 | staggerChildren: 0.04 14 | } 15 | } 16 | }; 17 | 18 | const item = { 19 | hidden: { y: 5, opacity: 0 }, 20 | visible: { 21 | y: 0, 22 | opacity: 1 23 | } 24 | }; 25 | 26 | function CompanyComponent() { 27 | return ( 28 | 29 | 35 | { 36 | [ 37 | 'About', 38 | 'Customers', 39 | 'Enterprise', 40 | 'Partners', 41 | 'Partners Program', 42 | 'Job', 43 | 'Enviournment', 44 | 'Newsroom' 45 | ].map((link, index) => { 46 | return ( 47 | 48 | {link} 49 | 50 | ) 51 | }) 52 | } 53 | 54 | 55 |
56 |

FROM THE BLOG

57 |
58 | Making a cool payment-system 59 | CLI tools to the rescue 60 | Developers are humans too 61 |
62 |
63 | ); 64 | } 65 | export default CompanyComponent; -------------------------------------------------------------------------------- /src/components/DropDown/DevelopersComponent.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import styled from 'styled-components'; 3 | import DropdownContent from './DropdownContent'; 4 | 5 | import { IconLink } from './DropdownContent'; 6 | 7 | const DevelopersDDContent = styled(DropdownContent)` 8 | .developers__title { 9 | font-weight: bold; 10 | margin-top: 20px; 11 | margin-bottom: 5px; 12 | text-transform: uppercase; 13 | font-size: 16px; 14 | } 15 | 16 | .grid { 17 | display: grid; 18 | grid-template-columns: 1fr 1fr; 19 | grid-template-rows: 1fr; 20 | margin-left: 30px; 21 | h4 { 22 | color: ${p => p.theme.lightGray}; 23 | } 24 | } 25 | 26 | a { 27 | margin: 15px auto; 28 | font-size: 14px; 29 | text-transform: capitalize; 30 | } 31 | ` 32 | 33 | function DevelopersComponent() { 34 | return ( 35 | 36 |
37 | Documentation 38 | Start integrating the APIs right away 39 |
40 | 41 |
42 |
43 |
44 |
45 |

Get Started

46 | Pre-built checkouts 47 | Libraries and SDK's 48 | Plugins 49 | Code samples 50 |
51 |
52 |

Guides

53 | Accept online payments 54 | Manage subscriptions 55 | Send payouts 56 | Send payments 57 |
58 |
59 | 60 |
61 | Full API Reference 62 | API Status 63 | API Changelog 64 |
65 |
66 | ); 67 | } 68 | export default DevelopersComponent; -------------------------------------------------------------------------------- /src/components/DropDown/DropdownContent.tsx: -------------------------------------------------------------------------------- 1 | import styled from 'styled-components'; 2 | 3 | const DropdownContent = styled.div` 4 | .company__blog--links { 5 | a { 6 | text-overflow: ellipsis; 7 | font-size: 14px; 8 | margin-left: 30px; 9 | 10 | &:after { 11 | content: '>'; 12 | margin-left: 10px; 13 | font-weight: 900; 14 | } 15 | } 16 | } 17 | ` 18 | 19 | 20 | export const IconLink = styled.a` 21 | font-weight: bold; 22 | letter-spacing: 1px; 23 | 24 | &:before { 25 | content: ''; 26 | position: relative; 27 | top: 2px; 28 | margin-right: 15px; 29 | border-radius: 50%; 30 | display: inline-block; 31 | width: 15px; 32 | height: 15px; 33 | background-color: ${p => p.theme.primaryBlack}; 34 | } 35 | ` 36 | 37 | export default DropdownContent; -------------------------------------------------------------------------------- /src/components/DropDown/ProductsComponent.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import styled, { css } from 'styled-components'; 3 | import DropdownContent from './DropdownContent'; 4 | 5 | 6 | const DescriptionLinkWrapper = styled.div<{ oneliner?: boolean }>` 7 | margin: 20px 0; 8 | font-size: 14px; 9 | 10 | .flex { 11 | display: flex; 12 | justify-content: flex-start; 13 | align-items: center; 14 | } 15 | 16 | .link__icon { 17 | ${p => p.oneliner ? css` 18 | width: 30px; 19 | height: 30px; 20 | `: css` 21 | width: 50px; 22 | height: 50px; 23 | `} 24 | border-radius: 50%; 25 | margin-right: 15px; 26 | } 27 | 28 | .text-body { 29 | align-items: center; 30 | display: flex; 31 | 32 | h3 { 33 | font-size: 16px; 34 | margin-right: 10px 35 | } 36 | } 37 | 38 | .title { 39 | text-transform: uppercase; 40 | color: ${p => p.theme.primaryBlack}; 41 | } 42 | ` 43 | 44 | 45 | interface DescriptionLinkProps { 46 | title: string; 47 | children: React.ReactNode; 48 | iconColor: string; 49 | oneliner?: boolean; 50 | } 51 | const DescriptionLink: React.FC = ({ title, children, iconColor, oneliner }) => { 52 | return ( 53 | 54 |
55 |
56 | 57 |
58 |

{title}

59 | {children} 60 |
61 |
62 | 63 | ) 64 | } 65 | 66 | function ProductsComponent() { 67 | return ( 68 | 69 | 70 | Full platform for online payments 71 | 72 | 73 | Smart invoice & subscriptions 74 | 75 | 76 | Multy-party payments for platforms 77 | 78 | 79 |
80 | {/* oneliners */} 81 | 82 | Advanced bussiness analytics 83 | 84 | 85 | Advanced bussiness analytics 86 | 87 | 88 | Advanced bussiness analytics 89 | 90 | 91 | Advanced bussiness analytics 92 | 93 |
94 | ); 95 | } 96 | export default ProductsComponent; -------------------------------------------------------------------------------- /src/components/Navbar/Container.style.tsx: -------------------------------------------------------------------------------- 1 | import styled from 'styled-components'; 2 | 3 | const Container = styled.section` 4 | color: ${p => p.theme.gray}; 5 | /* color: #6d7580; */ 6 | 7 | font-size: 14px; 8 | transform: translate(-50%,0); 9 | 10 | position: absolute; 11 | left: 50%; 12 | margin-top: 15px; 13 | 14 | background-color: rgba(255,255,255); 15 | border-radius: 5px; 16 | min-width: 400px; 17 | min-height: 300px; 18 | box-shadow: 0 5px 20px 5px rgba(0,0,0,0.1); 19 | 20 | /* arrow */ 21 | &:after { 22 | position: absolute; 23 | content: ''; 24 | top: -8px; 25 | left: 50%; 26 | transform: translate(-50%, 0); 27 | width: 0; 28 | height: 0; 29 | border-left: 8px solid transparent; 30 | border-right: 8px solid transparent; 31 | 32 | border-bottom: 8px solid rgba(255,255,255); 33 | } 34 | 35 | .header__title { 36 | &:before { 37 | content: ''; 38 | position: relative; 39 | top: 2px; 40 | margin-right: 15px; 41 | border-radius: 50%; 42 | display: inline-block; 43 | width: 15px; 44 | height: 15px; 45 | background-color: ${p => p.theme.primaryBlack}; 46 | } 47 | } 48 | 49 | a { 50 | text-decoration: none; 51 | display: block; 52 | color: ${p => p.theme.primaryBlack}; 53 | text-transform: uppercase; 54 | 55 | &:hover { 56 | color: ${p => p.theme.gray}; 57 | } 58 | &:hover:before { 59 | background-color: ${p => p.theme.gray}; 60 | } 61 | margin: 20px 0; 62 | } 63 | 64 | h1, h2, h3, h4, h5 { 65 | color: ${p => p.theme.primaryBlack}; 66 | text-transform: uppercase; 67 | } 68 | `; 69 | 70 | export default Container; 71 | -------------------------------------------------------------------------------- /src/components/Navbar/NavItems.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import styled from 'styled-components'; 3 | 4 | import Container from './Container.style'; 5 | import { motion, AnimatePresence } from 'framer-motion'; 6 | 7 | export const Menu = styled.ul` 8 | display: flex; 9 | width: 400px; 10 | list-style: none; 11 | align-items: center; 12 | justify-content: space-between; 13 | ` 14 | 15 | export const MenuItemWrapper = styled.li` 16 | padding: 20px 0; 17 | position: relative; 18 | ` 19 | 20 | const ItemTitle = styled.span` 21 | cursor: pointer; 22 | ` 23 | 24 | interface ItemProps { 25 | title: string; 26 | children: any; 27 | handleMouseEnter: () => void; 28 | handleIndexLeave: () => void; 29 | direction: number; 30 | } 31 | 32 | export const Item: React.FC = ({ 33 | title, 34 | children, 35 | handleMouseEnter, 36 | handleIndexLeave, 37 | direction 38 | }) => { 39 | let animationDirection = direction * 50; 40 | 41 | return ( 42 | 43 | 47 | {title} 48 | 49 | 50 | {/* AnimatePresence needed for exit */} 51 | 52 | {children && ( 53 | 61 | 62 |
63 | 68 | {children} 69 | 70 |
71 |
72 |
73 | )} 74 |
75 |
76 | ) 77 | } 78 | 79 | export default Menu; -------------------------------------------------------------------------------- /src/components/Navbar/Navbar.tsx: -------------------------------------------------------------------------------- 1 | import React, { useState } from 'react'; 2 | import styled from 'styled-components'; 3 | 4 | import Menu, { Item } from './NavItems' 5 | import ProductsComponent from '../DropDown/ProductsComponent'; 6 | import DevelopersComponent from '../DropDown/DevelopersComponent'; 7 | import CompanyComponent from '../DropDown/CompanyComponent'; 8 | 9 | 10 | 11 | const navConfig: { title: string, component: React.ReactNode }[] = [ 12 | { title: 'Products', component: }, 13 | { title: 'Developers', component: }, 14 | { title: 'Company', component: }, 15 | ] 16 | 17 | const NavWrapper = styled.nav` 18 | display: flex; 19 | align-items: center; 20 | justify-content: center; 21 | /* background: linear-gradient(45deg, #5533ff, #0ec3ff); */ 22 | width: 100%; 23 | position: absolute; 24 | left: 0; 25 | top: 0; 26 | color: white; 27 | height: 65px; 28 | padding: 0 250px; 29 | 30 | .logo { 31 | background-color: white; 32 | border-radius: 50%; 33 | width: 40px; 34 | height: 40px; 35 | text-align: center; 36 | line-height: 40px; 37 | margin-right: auto; 38 | } 39 | 40 | @media all and (max-width: 1200px) { 41 | padding: 0 100px; 42 | } 43 | ` 44 | 45 | 46 | const Navbar = () => { 47 | const [activeIndex, setActiveIndex] = useState(null); 48 | const [previousIndex, setPreviousIndex] = useState(null); 49 | 50 | function handleActiveIndex(index: number) { 51 | setActiveIndex(index); 52 | } 53 | 54 | function handleMouseLeave() { 55 | setActiveIndex(null) 56 | } 57 | 58 | function handleIndexLeave() { 59 | setPreviousIndex(activeIndex); 60 | } 61 | 62 | return ( 63 | 64 |

AH

65 | 66 | 67 | {navConfig.map((item, index) => { 68 | return ( 69 | handleActiveIndex(index)} 72 | direction={previousIndex - (activeIndex || 0)} 73 | key={index} 74 | title={item.title} 75 | > 76 | {(index === activeIndex) && item.component} 77 | 78 | ) 79 | })} 80 | 81 |
82 | ); 83 | } 84 | export default Navbar; -------------------------------------------------------------------------------- /src/index.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import ReactDOM from 'react-dom'; 3 | 4 | import App from './App'; 5 | import * as serviceWorker from './serviceWorker'; 6 | 7 | import 'normalize.css'; 8 | 9 | ReactDOM.render(, document.getElementById('root')); 10 | 11 | // If you want your app to work offline and load faster, you can change 12 | // unregister() to register() below. Note this comes with some pitfalls. 13 | // Learn more about service workers: https://bit.ly/CRA-PWA 14 | serviceWorker.unregister(); 15 | -------------------------------------------------------------------------------- /src/react-app-env.d.ts: -------------------------------------------------------------------------------- 1 | /// 2 | -------------------------------------------------------------------------------- /src/serviceWorker.js: -------------------------------------------------------------------------------- 1 | // This optional code is used to register a service worker. 2 | // register() is not called by default. 3 | 4 | // This lets the app load faster on subsequent visits in production, and gives 5 | // it offline capabilities. However, it also means that developers (and users) 6 | // will only see deployed updates on subsequent visits to a page, after all the 7 | // existing tabs open on the page have been closed, since previously cached 8 | // resources are updated in the background. 9 | 10 | // To learn more about the benefits of this model and instructions on how to 11 | // opt-in, read https://bit.ly/CRA-PWA 12 | 13 | const isLocalhost = Boolean( 14 | window.location.hostname === 'localhost' || 15 | // [::1] is the IPv6 localhost address. 16 | window.location.hostname === '[::1]' || 17 | // 127.0.0.1/8 is considered localhost for IPv4. 18 | window.location.hostname.match( 19 | /^127(?:\.(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)){3}$/ 20 | ) 21 | ); 22 | 23 | export function register(config) { 24 | if (process.env.NODE_ENV === 'production' && 'serviceWorker' in navigator) { 25 | // The URL constructor is available in all browsers that support SW. 26 | const publicUrl = new URL(process.env.PUBLIC_URL, window.location.href); 27 | if (publicUrl.origin !== window.location.origin) { 28 | // Our service worker won't work if PUBLIC_URL is on a different origin 29 | // from what our page is served on. This might happen if a CDN is used to 30 | // serve assets; see https://github.com/facebook/create-react-app/issues/2374 31 | return; 32 | } 33 | 34 | window.addEventListener('load', () => { 35 | const swUrl = `${process.env.PUBLIC_URL}/service-worker.js`; 36 | 37 | if (isLocalhost) { 38 | // This is running on localhost. Let's check if a service worker still exists or not. 39 | checkValidServiceWorker(swUrl, config); 40 | 41 | // Add some additional logging to localhost, pointing developers to the 42 | // service worker/PWA documentation. 43 | navigator.serviceWorker.ready.then(() => { 44 | console.log( 45 | 'This web app is being served cache-first by a service ' + 46 | 'worker. To learn more, visit https://bit.ly/CRA-PWA' 47 | ); 48 | }); 49 | } else { 50 | // Is not localhost. Just register service worker 51 | registerValidSW(swUrl, config); 52 | } 53 | }); 54 | } 55 | } 56 | 57 | function registerValidSW(swUrl, config) { 58 | navigator.serviceWorker 59 | .register(swUrl) 60 | .then(registration => { 61 | registration.onupdatefound = () => { 62 | const installingWorker = registration.installing; 63 | if (installingWorker == null) { 64 | return; 65 | } 66 | installingWorker.onstatechange = () => { 67 | if (installingWorker.state === 'installed') { 68 | if (navigator.serviceWorker.controller) { 69 | // At this point, the updated precached content has been fetched, 70 | // but the previous service worker will still serve the older 71 | // content until all client tabs are closed. 72 | console.log( 73 | 'New content is available and will be used when all ' + 74 | 'tabs for this page are closed. See https://bit.ly/CRA-PWA.' 75 | ); 76 | 77 | // Execute callback 78 | if (config && config.onUpdate) { 79 | config.onUpdate(registration); 80 | } 81 | } else { 82 | // At this point, everything has been precached. 83 | // It's the perfect time to display a 84 | // "Content is cached for offline use." message. 85 | console.log('Content is cached for offline use.'); 86 | 87 | // Execute callback 88 | if (config && config.onSuccess) { 89 | config.onSuccess(registration); 90 | } 91 | } 92 | } 93 | }; 94 | }; 95 | }) 96 | .catch(error => { 97 | console.error('Error during service worker registration:', error); 98 | }); 99 | } 100 | 101 | function checkValidServiceWorker(swUrl, config) { 102 | // Check if the service worker can be found. If it can't reload the page. 103 | fetch(swUrl) 104 | .then(response => { 105 | // Ensure service worker exists, and that we really are getting a JS file. 106 | const contentType = response.headers.get('content-type'); 107 | if ( 108 | response.status === 404 || 109 | (contentType != null && contentType.indexOf('javascript') === -1) 110 | ) { 111 | // No service worker found. Probably a different app. Reload the page. 112 | navigator.serviceWorker.ready.then(registration => { 113 | registration.unregister().then(() => { 114 | window.location.reload(); 115 | }); 116 | }); 117 | } else { 118 | // Service worker found. Proceed as normal. 119 | registerValidSW(swUrl, config); 120 | } 121 | }) 122 | .catch(() => { 123 | console.log( 124 | 'No internet connection found. App is running in offline mode.' 125 | ); 126 | }); 127 | } 128 | 129 | export function unregister() { 130 | if ('serviceWorker' in navigator) { 131 | navigator.serviceWorker.ready.then(registration => { 132 | registration.unregister(); 133 | }); 134 | } 135 | } 136 | -------------------------------------------------------------------------------- /src/styles/GlobalStyles.ts: -------------------------------------------------------------------------------- 1 | import { createGlobalStyle } from 'styled-components'; 2 | 3 | export default createGlobalStyle` 4 | @import url('https://fonts.googleapis.com/css?family=Karla:400,700'); 5 | 6 | * { 7 | box-sizing: border-box; 8 | } 9 | 10 | body { 11 | font-family: karla, -apple-system, system-ui, "Segoe UI", "Roboto", "Helvetica Neue", Arial, sans-serif; 12 | -webkit-font-smoothing: antialiased; 13 | color: whtie; 14 | 15 | height: 100vh; 16 | background: ${p => p.theme.gradient}; 17 | } 18 | 19 | h1,h2,h3,h4,h5,h6 { 20 | margin: 0; 21 | line-height: 150%; 22 | } 23 | 24 | a { 25 | text-decoration: none; 26 | color: #0ec3ff; 27 | 28 | &:hover { 29 | color: #3670ff; 30 | } 31 | } 32 | 33 | .credits { 34 | position: absolute; 35 | bottom: 20px; 36 | color: #c5f0ff; 37 | left: 50%; 38 | transform: translateX(-50%); 39 | 40 | 41 | a { 42 | color: #06070f; 43 | 44 | &:hover { 45 | color: #c5f0ff; 46 | } 47 | } 48 | } 49 | 50 | .overflow__content { 51 | padding: 10px 30px; 52 | overflow: hidden !important; 53 | } 54 | .text-14 { 55 | font-size: 14px; 56 | } 57 | ` -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "module": "esnext", 4 | "target": "es5", 5 | "incremental": true, 6 | "allowJs": true, 7 | "checkJs": false, 8 | "jsx": "react", 9 | "removeComments": true, 10 | "noEmit": true, 11 | "pretty": true, 12 | "skipLibCheck": true, 13 | "strict": false, 14 | "moduleResolution": "node", 15 | "esModuleInterop": true, 16 | "lib": [ 17 | "dom", 18 | "dom.iterable", 19 | "esnext" 20 | ], 21 | "allowSyntheticDefaultImports": true, 22 | "forceConsistentCasingInFileNames": true, 23 | "resolveJsonModule": true, 24 | "isolatedModules": true, 25 | }, 26 | "exclude": [ 27 | "node_modules" 28 | ], 29 | "include": [ 30 | "src" 31 | ] 32 | } 33 | --------------------------------------------------------------------------------