diff --git a/package-lock.json b/package-lock.json index a1b99d2..74842b9 100644 --- a/package-lock.json +++ b/package-lock.json @@ -16,6 +16,7 @@ "@tailwindcss/typography": "^0.5.8", "@types/mdx": "^2.0.3", "@types/node": "^18.11.18", + "@types/nprogress": "^0.2.0", "@types/react": "18.0.26", "@types/react-dom": "18.0.10", "autoprefixer": "^10.4.13", @@ -28,6 +29,7 @@ "motion": "^10.15.5", "next": "^13.3.0", "node-fetch": "^3.3.0", + "nprogress": "^0.2.0", "postcss": "^8.4.21", "postcss-focus-visible": "^7.1.0", "react": "^18.2.0", @@ -808,6 +810,11 @@ "resolved": "https://registry.npmjs.org/@types/node/-/node-18.11.18.tgz", "integrity": "sha512-DHQpWGjyQKSHj3ebjFI/wRKcqQcdR+MoFBygntYOZytCqNfkd2ZC4ARDJ2DQqhjH5p85Nnd3jhUJIXrszFX/JA==" }, + "node_modules/@types/nprogress": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/@types/nprogress/-/nprogress-0.2.0.tgz", + "integrity": "sha512-1cYJrqq9GezNFPsWTZpFut/d4CjpZqA0vhqDUPFWYKF1oIyBz5qnoYMzR+0C/T96t3ebLAC1SSnwrVOm5/j74A==" + }, "node_modules/@types/phoenix": { "version": "1.5.5", "resolved": "https://registry.npmjs.org/@types/phoenix/-/phoenix-1.5.5.tgz", @@ -5509,6 +5516,11 @@ "node": "^14.17.0 || ^16.13.0 || >=18.0.0" } }, + "node_modules/nprogress": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/nprogress/-/nprogress-0.2.0.tgz", + "integrity": "sha512-I19aIingLgR1fmhftnbWWO3dXc0hSxqHQHQb3H8m+K3TnEn/iSeTZZOyvKXWqQESMwuUVnatlCnZdLBZZt2VSA==" + }, "node_modules/object-assign": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", @@ -8519,6 +8531,11 @@ "resolved": "https://registry.npmjs.org/@types/node/-/node-18.11.18.tgz", "integrity": "sha512-DHQpWGjyQKSHj3ebjFI/wRKcqQcdR+MoFBygntYOZytCqNfkd2ZC4ARDJ2DQqhjH5p85Nnd3jhUJIXrszFX/JA==" }, + "@types/nprogress": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/@types/nprogress/-/nprogress-0.2.0.tgz", + "integrity": "sha512-1cYJrqq9GezNFPsWTZpFut/d4CjpZqA0vhqDUPFWYKF1oIyBz5qnoYMzR+0C/T96t3ebLAC1SSnwrVOm5/j74A==" + }, "@types/phoenix": { "version": "1.5.5", "resolved": "https://registry.npmjs.org/@types/phoenix/-/phoenix-1.5.5.tgz", @@ -11807,6 +11824,11 @@ "integrity": "sha512-g+DPQSkusnk7HYXr75NtzkIP4+N81i3RPsGFidF3DzHd9MT9wWngmqoeg/fnHFz5MNdtG4w03s+QnhewSLTT2Q==", "dev": true }, + "nprogress": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/nprogress/-/nprogress-0.2.0.tgz", + "integrity": "sha512-I19aIingLgR1fmhftnbWWO3dXc0hSxqHQHQb3H8m+K3TnEn/iSeTZZOyvKXWqQESMwuUVnatlCnZdLBZZt2VSA==" + }, "object-assign": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", diff --git a/package.json b/package.json index 0fccc29..5fd289a 100644 --- a/package.json +++ b/package.json @@ -18,6 +18,7 @@ "@tailwindcss/typography": "^0.5.8", "@types/mdx": "^2.0.3", "@types/node": "^18.11.18", + "@types/nprogress": "^0.2.0", "@types/react": "18.0.26", "@types/react-dom": "18.0.10", "autoprefixer": "^10.4.13", @@ -30,6 +31,7 @@ "motion": "^10.15.5", "next": "^13.3.0", "node-fetch": "^3.3.0", + "nprogress": "^0.2.0", "postcss": "^8.4.21", "postcss-focus-visible": "^7.1.0", "react": "^18.2.0", diff --git a/pages/_app.tsx b/pages/_app.tsx index b135084..9d0e7e1 100644 --- a/pages/_app.tsx +++ b/pages/_app.tsx @@ -1,15 +1,35 @@ import type {AppProps} from 'next/app' +import {useRouter} from 'next/router' +import NProgress from 'nprogress' import {createBrowserSupabaseClient} from '@supabase/auth-helpers-nextjs' import {SessionContextProvider, Session} from '@supabase/auth-helpers-react' -import {useState} from 'react' +import {useEffect, useState} from 'react' import {Header} from '@/components/Header' import {Footer} from '@/components/Footer' +import '../styles/nprogress.css' import '../styles/tailwind.css' import 'focus-visible' export default function App({Component, pageProps}: AppProps<{ initialSession: Session }>) { const [supabaseClient] = useState(() => createBrowserSupabaseClient()) + const router = useRouter() + NProgress.configure({showSpinner: false}) + + useEffect(() => { + const handleRouteStart = () => NProgress.start() + const handleRouteDone = () => NProgress.done() + + router.events.on("routeChangeStart", handleRouteStart) + router.events.on("routeChangeComplete", handleRouteDone) + router.events.on("routeChangeError", handleRouteDone) + + return () => { + router.events.off("routeChangeStart", handleRouteStart) + router.events.off("routeChangeComplete", handleRouteDone) + router.events.off("routeChangeError", handleRouteDone) + } + }) return ( diff --git a/styles/nprogress.css b/styles/nprogress.css new file mode 100644 index 0000000..7626525 --- /dev/null +++ b/styles/nprogress.css @@ -0,0 +1,83 @@ +/* Make clicks pass-through */ +#nprogress { + pointer-events: none; +} + +#nprogress .bar { + background: #4338ca; + + position: fixed; + z-index: 1031; + top: 0; + left: 0; + + width: 100%; + height: 2px; +} + +/* Fancy blur effect */ +#nprogress .peg { + display: block; + position: absolute; + right: 0px; + width: 100px; + height: 100%; + box-shadow: 0 0 10px #4338ca, 0 0 5px #4338ca; + opacity: 1.0; + + -webkit-transform: rotate(3deg) translate(0px, -4px); + -ms-transform: rotate(3deg) translate(0px, -4px); + transform: rotate(3deg) translate(0px, -4px); +} + +/* Remove these to get rid of the spinner */ +#nprogress .spinner { + display: block; + position: fixed; + z-index: 1031; + top: 15px; + right: 15px; +} + +#nprogress .spinner-icon { + width: 18px; + height: 18px; + box-sizing: border-box; + + border: solid 2px transparent; + border-top-color: #4338ca; + border-left-color: #4338ca; + border-radius: 50%; + + -webkit-animation: nprogress-spinner 400ms linear infinite; + animation: nprogress-spinner 400ms linear infinite; +} + +.nprogress-custom-parent { + overflow: hidden; + position: relative; +} + +.nprogress-custom-parent #nprogress .spinner, +.nprogress-custom-parent #nprogress .bar { + position: absolute; +} + +@-webkit-keyframes nprogress-spinner { + 0% { + -webkit-transform: rotate(0deg); + } + 100% { + -webkit-transform: rotate(360deg); + } +} + +@keyframes nprogress-spinner { + 0% { + transform: rotate(0deg); + } + 100% { + transform: rotate(360deg); + } +} +