init
This commit is contained in:
13
.eslintrc.json
Normal file
13
.eslintrc.json
Normal file
@@ -0,0 +1,13 @@
|
||||
{
|
||||
"extends": [
|
||||
"next",
|
||||
"next/core-web-vitals",
|
||||
"plugin:jsx-a11y/strict",
|
||||
"prettier",
|
||||
"eslint:recommended",
|
||||
"plugin:@typescript-eslint/recommended",
|
||||
"plugin:react/recommended",
|
||||
"plugin:react-hooks/recommended"
|
||||
],
|
||||
"plugins": ["@typescript-eslint", "react-hooks", "jsx-a11y"]
|
||||
}
|
||||
34
.gitignore
vendored
Normal file
34
.gitignore
vendored
Normal file
@@ -0,0 +1,34 @@
|
||||
# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
|
||||
|
||||
# dependencies
|
||||
/node_modules
|
||||
/.pnp
|
||||
.pnp.js
|
||||
|
||||
# testing
|
||||
/coverage
|
||||
|
||||
# next.js
|
||||
/.next/
|
||||
/out/
|
||||
|
||||
# production
|
||||
/build
|
||||
|
||||
# misc
|
||||
.DS_Store
|
||||
*.pem
|
||||
|
||||
# debug
|
||||
npm-debug.log*
|
||||
yarn-debug.log*
|
||||
yarn-error.log*
|
||||
|
||||
# local env files
|
||||
.env*
|
||||
|
||||
# yarn
|
||||
.yarn/*
|
||||
!.yarn/releases
|
||||
!.yarn/plugins
|
||||
.pnp.*
|
||||
17
.prettierignore
Normal file
17
.prettierignore
Normal file
@@ -0,0 +1,17 @@
|
||||
# Ignore artifacts:
|
||||
build
|
||||
coverage
|
||||
.next
|
||||
out
|
||||
.yarn
|
||||
.github
|
||||
.env*
|
||||
.eslintrc.json
|
||||
.gitignore
|
||||
.yarnrc.yml
|
||||
next-env.d.ts
|
||||
next-env.d
|
||||
package*
|
||||
tsconfig.json
|
||||
yarn.lock
|
||||
next.config.js
|
||||
363
.yarn/plugins/@yarnpkg/plugin-interactive-tools.cjs
vendored
Normal file
363
.yarn/plugins/@yarnpkg/plugin-interactive-tools.cjs
vendored
Normal file
File diff suppressed because one or more lines are too long
768
.yarn/releases/yarn-3.1.0.cjs
vendored
Executable file
768
.yarn/releases/yarn-3.1.0.cjs
vendored
Executable file
File diff suppressed because one or more lines are too long
7
.yarnrc.yml
Normal file
7
.yarnrc.yml
Normal file
@@ -0,0 +1,7 @@
|
||||
nodeLinker: node-modules
|
||||
|
||||
plugins:
|
||||
- path: .yarn/plugins/@yarnpkg/plugin-interactive-tools.cjs
|
||||
spec: "@yarnpkg/plugin-interactive-tools"
|
||||
|
||||
yarnPath: .yarn/releases/yarn-3.1.0.cjs
|
||||
165
README.md
Normal file
165
README.md
Normal file
@@ -0,0 +1,165 @@
|
||||
<p align="center">
|
||||
</p>
|
||||
|
||||
# [LCM Potty Chart](https://lucidcreations.media/)
|
||||
|
||||
## About
|
||||
|
||||
This app is meant to be a progress tracker for littles and bigs to track progress and behaviors.
|
||||
|
||||
The types of progress can be anything from:
|
||||
|
||||
- Potty Training
|
||||
- Diaper Training
|
||||
- Behavior Tracking
|
||||
- Eating Habits
|
||||
- Other Habit Tracking
|
||||
- Learning New Skills
|
||||
- Dedicating certain amount of hours a day to a new skill or talent
|
||||
- Whatever else a little would want to track
|
||||
|
||||
Positive days will be given "stars" and happy designs where negative days will be given rain clouds or sad designs.
|
||||
|
||||
A big will be able to track as many littles as they desire and can create multiple charts for each little they have. How you use the charts and trackers is up to you.
|
||||
|
||||
Give descriptions to each sticker you use and make a list of rules for each chart and how to earn the stars and what would result in a rain cloud.
|
||||
|
||||
# Technologies
|
||||
|
||||
## TypeScript
|
||||
|
||||
[<img src="https://upload.wikimedia.org/wikipedia/commons/4/4c/Typescript_logo_2020.svg" height="100px">](https://www.typescriptlang.org/)
|
||||
|
||||
> [TypeScript](https://www.typescriptlang.org/) is a strongly typed programming language which builds on JavaScript giving you better tooling at any scale.
|
||||
|
||||
## Next.js
|
||||
|
||||
[](https://nextjs.org/)
|
||||
|
||||
> The React Framework for Production
|
||||
|
||||
[Next.js](https://nextjs.org/) is a serverless, zero config React framework.
|
||||
|
||||
## Chakra UI
|
||||
|
||||
[<img src="https://gist.githubusercontent.com/navin-moorthy/d4c5fe7f384a106ba8171eee77b45623/raw/3e4d37340270a38367bfe94dd2f7daea2a0537a2/chakra-ui-logo.svg" height="75px" alt="Chakra UI" >](https://chakra-ui.com/)
|
||||
|
||||
> [Chakra UI](https://chakra-ui.com/) is a simple, modular and accessible component library that gives you the building blocks you need to build your React applications.
|
||||
|
||||
# Getting Started
|
||||
|
||||
## Requirements
|
||||
|
||||
The app was built on and is tested for:
|
||||
|
||||
- Ubuntu 20.04
|
||||
- Node.js 14.x
|
||||
- Node.js 16.x
|
||||
|
||||
I cannot guarantee functionality or stability if used on other OSs Ubuntu versions or Node.js versions.
|
||||
|
||||
## Installation
|
||||
|
||||
After cloning the app you will need to install the dependencies and packages. The app uses Yarn v2. Run this command to install using Yarn v2:
|
||||
|
||||
```
|
||||
Yarn install
|
||||
```
|
||||
|
||||
### Upgrading Packages
|
||||
|
||||
The `upgrade-interactive` plugin has been included in this app. To ungrade packages and dependencies run the following command:
|
||||
|
||||
```
|
||||
yarn upgrade-interactive
|
||||
```
|
||||
|
||||
The plugin `upgrade-interactive` is a combination of the `yarn outdated` and `yarn upgrade [package...]` commands. Where `yarn outdated` displays the list of outdated packages and `yarn upgrade [package...]` can then be used to upgrade desired packages, `yarn upgrade-interactive` displays the same outdated package list and lets you immediately chose which to upgrade.
|
||||
|
||||
To learn more about the `upgrade-interactive` plugin please read the [official docs](https://classic.yarnpkg.com/lang/en/docs/cli/upgrade-interactive/).
|
||||
|
||||
## Environment Variables
|
||||
|
||||
_[Learn more about environment variables in Next.js](https://nextjs.org/docs/basic-features/environment-variables)_
|
||||
|
||||
All environment variables are in files named `example.env.*`.
|
||||
|
||||
Copy the files and remove the `example.` prefix to use them.
|
||||
|
||||
- .env
|
||||
|
||||
```
|
||||
|
||||
```
|
||||
|
||||
- .env.development
|
||||
|
||||
```
|
||||
|
||||
```
|
||||
|
||||
- .env.production
|
||||
|
||||
```
|
||||
|
||||
```
|
||||
|
||||
Development and production variables overwrite the main env variables. Development keys can be kept in the main env file to be overridden when `yarn start` is used.
|
||||
|
||||
## Development Server
|
||||
|
||||
To start the development server run the command
|
||||
|
||||
```
|
||||
yarn dev
|
||||
```
|
||||
|
||||
## Deployment Server
|
||||
|
||||
To deploy the app it must first be built.
|
||||
|
||||
_The build script will automatically check for linting and typescript type errors._
|
||||
|
||||
If any errors are present the build is aborted.
|
||||
|
||||
To run the build command use:
|
||||
|
||||
```
|
||||
yarn build
|
||||
```
|
||||
|
||||
If the build is successful then the deployed server needs to be started.
|
||||
|
||||
_This will not work if the build did not complete or a build was never done._
|
||||
|
||||
To start the app run:
|
||||
|
||||
```
|
||||
yarn run
|
||||
```
|
||||
|
||||
##### It is recommended that the app is deployed using the [Vercel Platform](https://vercel.com/new). Vercel is optimized to dynamically serve static, dynamic, and hybrid pages based on the needs of each individual page that is built. It deploys in less than a minute and can be linked to a Github repo to keep the production server up do date with the most recent pushes to your main or production branch. It automatically provides SSL and CDN to each app and scales automatically. Vercel also monitors all branched and deploys preview builds for those branches to test fixes, refactors, and new content live.
|
||||
|
||||
# Development Features
|
||||
|
||||
## Prettier
|
||||
|
||||
This app has the prettier code formatter built in. [More about Prettier](https://prettier.io/)
|
||||
|
||||
To have Prettier update the structure of the codebase run the following command:
|
||||
|
||||
```
|
||||
yarn pretty
|
||||
```
|
||||
|
||||
## ESLint
|
||||
|
||||
This app has ESLIne built in to check for errors within the code.
|
||||
|
||||
**The A11y plugin in installed to help check for and meet accessibility standards.**
|
||||
|
||||
To lint the codebase run the following command:
|
||||
|
||||
```
|
||||
yarn lint
|
||||
```
|
||||
6
next-env.d.ts
vendored
Normal file
6
next-env.d.ts
vendored
Normal file
@@ -0,0 +1,6 @@
|
||||
/// <reference types="next" />
|
||||
/// <reference types="next/types/global" />
|
||||
/// <reference types="next/image-types/global" />
|
||||
|
||||
// NOTE: This file should not be edited
|
||||
// see https://nextjs.org/docs/basic-features/typescript for more information.
|
||||
42
package.json
Normal file
42
package.json
Normal file
@@ -0,0 +1,42 @@
|
||||
{
|
||||
"private": true,
|
||||
"name": "lucid-creations-media-potty-chart",
|
||||
"homepage": "https://github.com/LucidCreationsMedia/",
|
||||
"version": "0.1.0",
|
||||
"author": {
|
||||
"name": "Lucid Creations Media",
|
||||
"url": "https://lucidcreations.media",
|
||||
"email": "social@lucidcreations.media"
|
||||
},
|
||||
"scripts": {
|
||||
"dev": "next dev",
|
||||
"build": "next build",
|
||||
"start": "next start",
|
||||
"lint": "next lint",
|
||||
"pretty": "prettier --write ."
|
||||
},
|
||||
"dependencies": {
|
||||
"@chakra-ui/react": "^1.7.1",
|
||||
"@emotion/react": "^11.5.0",
|
||||
"@emotion/styled": "^11.3.0",
|
||||
"@iconify/react": "^3.1.0",
|
||||
"@types/react": "^17.0.34",
|
||||
"framer-motion": "^5.3.0",
|
||||
"next": "12.0.3",
|
||||
"react": "17.0.2",
|
||||
"react-dom": "17.0.2",
|
||||
"sharp": "^0.29.2"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@typescript-eslint/eslint-plugin": "^5.3.1",
|
||||
"eslint": "<8.0.0",
|
||||
"eslint-config-next": "12.0.3",
|
||||
"eslint-config-prettier": "^8.3.0",
|
||||
"eslint-plugin-jsx-a11y": "^6.4.1",
|
||||
"eslint-plugin-react": "^7.26.1",
|
||||
"eslint-plugin-react-hooks": "^4.2.0",
|
||||
"prettier": "^2.4.1",
|
||||
"typescript": "4.4.4"
|
||||
},
|
||||
"packageManager": "yarn@3.1.0"
|
||||
}
|
||||
28
pages/_app.tsx
Normal file
28
pages/_app.tsx
Normal file
@@ -0,0 +1,28 @@
|
||||
import type { AppProps } from "next/app";
|
||||
import React from "react";
|
||||
import { ChakraProvider } from "@chakra-ui/react";
|
||||
import AppTheme from "../theme/AppTheme";
|
||||
import Layout from "../theme/layout/Layout";
|
||||
import Head from "next/head";
|
||||
|
||||
function LCMPottyChart({
|
||||
Component,
|
||||
pageProps,
|
||||
}: AppProps): JSX.Element {
|
||||
return (
|
||||
<ChakraProvider theme={AppTheme}>
|
||||
<Layout {...pageProps}>
|
||||
<Head>
|
||||
<title>{"LCM Potty Chart"}</title>
|
||||
<meta
|
||||
name="viewport"
|
||||
content="width=device-width, user-scalable=yes, initial-scale=1.0"
|
||||
/>
|
||||
</Head>
|
||||
<Component {...pageProps} />
|
||||
</Layout>
|
||||
</ChakraProvider>
|
||||
);
|
||||
}
|
||||
|
||||
export default LCMPottyChart;
|
||||
57
pages/_document.tsx
Normal file
57
pages/_document.tsx
Normal file
@@ -0,0 +1,57 @@
|
||||
import React from "react";
|
||||
import NextDocument, { Html, Head, Main, NextScript } from "next/document";
|
||||
import { ColorModeScript } from "@chakra-ui/react";
|
||||
import AppTheme from "../theme/AppTheme";
|
||||
|
||||
const description =
|
||||
"Behavior and progress tracker for ABDLs and babyfurs alike. Track multiple littles and create any trackers you would like.";
|
||||
|
||||
class Document extends NextDocument {
|
||||
render(): JSX.Element {
|
||||
return (
|
||||
<Html>
|
||||
<Head>
|
||||
<meta name="theme-color" content="#3138dc" />
|
||||
<link rel="icon" href="/images/favicon.svg" sizes="32x32 192x192" />
|
||||
<link rel="apple-touch-icon" href="/images/favicon.svg" />
|
||||
<meta property="og:title" content="LCM Potty Chart" />
|
||||
<meta name="og:description" content={description} />
|
||||
<meta property="og:type" content="Progress Tracking" />
|
||||
<meta property="og:image" content="/images/logo.jpg" />
|
||||
<meta property="og:image:type" content="image/jpeg" />
|
||||
<meta property="og:image:alt" content="LCM Potty Chart Logo" />
|
||||
<meta property="og:url" content="https://lucidcreations.media" />
|
||||
<meta name="twitter:card" content="summary_large_image" />
|
||||
<meta property="title" content="LCM Potty Chart" />
|
||||
<meta name="description" content={description} />
|
||||
<meta property="type" content="Progress Tracking" />
|
||||
<meta property="image" content="/images/logojpg" />
|
||||
<meta property="image:type" content="image/jpeg" />
|
||||
<meta property="image:alt" content="LCM Potty Chart Logo" />
|
||||
<meta property="url" content="https://https://lucidcreations.media" />
|
||||
<meta httpEquiv="content-language" content="en_US" />
|
||||
<meta charSet="UTF-8" />
|
||||
<meta
|
||||
name="keywords"
|
||||
content="ABDL Adult Baby Diaper Lover Furry Babyfur ab/dl AB/DL potty chart training progress behavior tracker habbit"
|
||||
/>
|
||||
<meta name="copyright" content="Lucid Creations Media" />
|
||||
<meta name="page-topic" content="Progress Tracking" />
|
||||
<meta name="page-type" content="Calender" />
|
||||
<meta name="audience" content="18+" />
|
||||
<meta name="robots" content="index, follow" />
|
||||
<html lang="en" />
|
||||
</Head>
|
||||
<body>
|
||||
<ColorModeScript
|
||||
initialColorMode={AppTheme.config.initialColorMode}
|
||||
/>
|
||||
<Main />
|
||||
<NextScript />
|
||||
</body>
|
||||
</Html>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default Document;
|
||||
14
pages/index.tsx
Normal file
14
pages/index.tsx
Normal file
@@ -0,0 +1,14 @@
|
||||
import React from "react";
|
||||
import { Box, Heading } from "@chakra-ui/react";
|
||||
|
||||
const IndexPage = (): JSX.Element => {
|
||||
return (
|
||||
<Box textAlign="center" w="100%" h="auto" py="10vh">
|
||||
<Heading as="h1" size="2xl">
|
||||
Hello World
|
||||
</Heading>
|
||||
</Box>
|
||||
);
|
||||
};
|
||||
|
||||
export default IndexPage;
|
||||
47
theme/AppTheme.ts
Normal file
47
theme/AppTheme.ts
Normal file
@@ -0,0 +1,47 @@
|
||||
import { extendTheme, ThemeConfig } from "@chakra-ui/react";
|
||||
import { createBreakpoints } from "@chakra-ui/theme-tools";
|
||||
import buttons from "./components/buttonStyles";
|
||||
|
||||
const config: ThemeConfig = {
|
||||
initialColorMode: "dark",
|
||||
useSystemColorMode: false,
|
||||
};
|
||||
|
||||
// const breakpoints = createBreakpoints({
|
||||
// sm: "30em",
|
||||
// md: "48em",
|
||||
// lg: "75em",
|
||||
// xl: "85em",
|
||||
// "2xl": "100em",
|
||||
// });
|
||||
|
||||
const AppTheme = extendTheme({
|
||||
config,
|
||||
colors: {
|
||||
brand: {
|
||||
main: "#3138dc",
|
||||
primary: "#0068ff",
|
||||
secondary: "#0086ff",
|
||||
hover: "#00aec1",
|
||||
warning: "#ffbd48",
|
||||
danger: "#FC8181",
|
||||
valid: "#00c17c",
|
||||
footer: "#0097a7",
|
||||
footerText: "black",
|
||||
content: "#2d3748",
|
||||
},
|
||||
},
|
||||
styles: {
|
||||
global: {
|
||||
body: {
|
||||
bg: "gray.900",
|
||||
},
|
||||
},
|
||||
},
|
||||
components: {
|
||||
Button: buttons,
|
||||
},
|
||||
// breakpoints,
|
||||
});
|
||||
|
||||
export default AppTheme;
|
||||
165
theme/components/buttonStyles.ts
Normal file
165
theme/components/buttonStyles.ts
Normal file
@@ -0,0 +1,165 @@
|
||||
/* eslint-disable @typescript-eslint/explicit-module-boundary-types */
|
||||
import {
|
||||
darken,
|
||||
mode,
|
||||
StyleFunctionProps,
|
||||
whiten,
|
||||
} from "@chakra-ui/theme-tools";
|
||||
import { Dict } from "@chakra-ui/utils";
|
||||
|
||||
const buttonStyles = {
|
||||
// style object for base or default style
|
||||
baseStyle: {},
|
||||
// styles for different sizes ("sm", "md", "lg")
|
||||
sizes: {},
|
||||
// styles for different visual variants ("outline", "solid")
|
||||
variants: {
|
||||
contactPrimary: (props: Dict<never> | StyleFunctionProps) => ({
|
||||
bg: "rgba(255, 255, 255, .15)",
|
||||
fontSize: "xl",
|
||||
p: "2",
|
||||
_hover: {
|
||||
bg: mode(
|
||||
whiten("brand.primary", 20),
|
||||
darken("brand.primary", 20)
|
||||
)(props),
|
||||
},
|
||||
}),
|
||||
contactSecondary: (props: Dict<never> | StyleFunctionProps) => ({
|
||||
bg: "brand.primary",
|
||||
fontSize: "xl",
|
||||
p: "2",
|
||||
_hover: {
|
||||
bg: mode(
|
||||
whiten("brand.primary", 20),
|
||||
darken("brand.primary", 20)
|
||||
)(props),
|
||||
},
|
||||
}),
|
||||
project: (props: Dict<never> | StyleFunctionProps) => ({
|
||||
bg: "transparent",
|
||||
fontSize: "md",
|
||||
py: 2,
|
||||
px: 4,
|
||||
boxShadow:
|
||||
"rgba(0, 134, 255, 0.2) 0px 0px 15px, rgba(0, 134, 255, 0.15) 0px 0px 3px 1px",
|
||||
border: "1px solid rgba(0, 134, 255, 0.4)",
|
||||
_hover: {
|
||||
bg: mode(
|
||||
whiten("brand.secondary", 20),
|
||||
darken("brand.secondary", 20)
|
||||
)(props),
|
||||
boxShadow:
|
||||
"rgba(0, 104, 255, 0.5) 0px 0px 15px, rgba(0, 104, 255, 0.3) 0px 0px 3px 1px",
|
||||
},
|
||||
}),
|
||||
nav: (props: Dict<never> | StyleFunctionProps) => ({
|
||||
bg: "transparent",
|
||||
fontSize: "md",
|
||||
px: "2",
|
||||
_hover: {
|
||||
bg: mode(
|
||||
whiten("brand.secondary", 20),
|
||||
darken("brand.secondary", 20)
|
||||
)(props),
|
||||
},
|
||||
}),
|
||||
stickyNav: (/* props: Dict<never> | StyleFunctionProps */) => ({
|
||||
bg: "transparent",
|
||||
fontSize: "md",
|
||||
px: "2",
|
||||
_hover: {
|
||||
textDecoration: "underline",
|
||||
},
|
||||
}),
|
||||
credits: (props: Dict<never> | StyleFunctionProps) => ({
|
||||
bg: "brand.main",
|
||||
fontSize: "lg",
|
||||
p: 3,
|
||||
color: "whiteAlpha",
|
||||
_hover: {
|
||||
bg: mode(whiten("brand.main", 20), darken("brand.main", 20))(props),
|
||||
},
|
||||
}),
|
||||
backToTop: (props: Dict<never> | StyleFunctionProps) => ({
|
||||
bg: "rgba(23, 25, 35, 0.5)",
|
||||
fontSize: "lg",
|
||||
py: 2,
|
||||
px: 4,
|
||||
color: "rgba(0, 134, 255, 0.6)",
|
||||
boxShadow:
|
||||
"rgba(0, 134, 255, 0.05) 0px 0px 15px, rgba(0, 134, 255, 0.1) 0px 0px 3px 1px",
|
||||
border: "1px solid rgba(0, 134, 255, 0.15)",
|
||||
_hover: {
|
||||
bg: mode(
|
||||
whiten("brand.secondary", 20),
|
||||
darken("brand.secondary", 20)
|
||||
)(props),
|
||||
boxShadow:
|
||||
"rgba(0, 104, 255, 0.5) 0px 0px 15px, rgba(0, 104, 255, 0.3) 0px 0px 3px 1px",
|
||||
color: "whiteAlpha.900",
|
||||
border: "1px solid rgba(0, 134, 255, 1)",
|
||||
},
|
||||
}),
|
||||
collapse: (props: Dict<never> | StyleFunctionProps) => ({
|
||||
bg: "transparent",
|
||||
fontSize: "md",
|
||||
p: 2,
|
||||
h: 8,
|
||||
color: "brand.hover",
|
||||
textDecoration: "underline",
|
||||
_hover: {
|
||||
bg: mode(
|
||||
whiten("brand.secondary", 20),
|
||||
darken("brand.secondary", 20)
|
||||
)(props),
|
||||
color: "whiteAlpha.900",
|
||||
textDecoration: "none",
|
||||
},
|
||||
}),
|
||||
submit: (props: Dict<never> | StyleFunctionProps) => ({
|
||||
fontSize: "lg",
|
||||
py: 2,
|
||||
px: 4,
|
||||
type: "submit",
|
||||
_hover: {
|
||||
color: "whiteAlpha.900",
|
||||
bg: mode(whiten("brand.valid", 20), darken("brand.valid", 20))(props),
|
||||
_disabled: {
|
||||
color: mode(
|
||||
whiten("brand.danger", 20),
|
||||
darken("brand.danger", 20)
|
||||
)(props),
|
||||
boxShadow:
|
||||
"rgba(252, 129, 129, .95) 0px 0px 15px, rgba(252, 129, 129, 0.75) 0px 0px 3px 1px",
|
||||
border: "1px solid #FC8181",
|
||||
},
|
||||
},
|
||||
}),
|
||||
mobileNav: (props: Dict<never> | StyleFunctionProps) => ({
|
||||
// bg: "transparent",
|
||||
fontSize: "md",
|
||||
px: "2",
|
||||
boxShadow:
|
||||
"rgba(0, 134, 255, 0.30) 0px 0px 15px, rgba(0, 134, 255, 0.15) 0px 0px 3px 1px",
|
||||
_hover: {
|
||||
bg: mode(
|
||||
whiten("brand.secondary", 20),
|
||||
darken("brand.secondary", 20)
|
||||
)(props),
|
||||
boxShadow:
|
||||
"rgba(0, 134, 255, 0.5) 0px 0px 15px, rgba(0, 134, 255, 0.3) 0px 0px 3px 1px",
|
||||
},
|
||||
_expanded: {
|
||||
bg: "brand.primary",
|
||||
boxShadow:
|
||||
"rgba(0, 134, 255, 0.5) 0px 0px 15px, rgba(0, 134, 255, 0.3) 0px 0px 3px 1px",
|
||||
border: "1px solid #0068ff",
|
||||
},
|
||||
}),
|
||||
},
|
||||
// default values for `size` and `variant`
|
||||
defaultProps: {},
|
||||
};
|
||||
|
||||
export default buttonStyles;
|
||||
28
theme/layout/BackToTopButton.tsx
Normal file
28
theme/layout/BackToTopButton.tsx
Normal file
@@ -0,0 +1,28 @@
|
||||
import React, { FC } from "react";
|
||||
import { Button, Flex, Link } from "@chakra-ui/react";
|
||||
import { Icon } from "@iconify/react";
|
||||
|
||||
interface BackToTopButtonProps {
|
||||
show: boolean;
|
||||
}
|
||||
|
||||
const BackToTopButton: FC<BackToTopButtonProps> = ({
|
||||
show,
|
||||
}: BackToTopButtonProps) => {
|
||||
return (
|
||||
<Flex
|
||||
d={show ? "flex" : "none"}
|
||||
pos="fixed"
|
||||
top="85vh"
|
||||
right={{ base: "1.25rem", sm: "2rem", md: "3rem" }}
|
||||
>
|
||||
<Link href="/#top">
|
||||
<Button variant="backToTop">
|
||||
<Icon icon="akar-icons:chevron-up" />
|
||||
</Button>
|
||||
</Link>
|
||||
</Flex>
|
||||
);
|
||||
};
|
||||
|
||||
export default BackToTopButton;
|
||||
33
theme/layout/DesktopNav.tsx
Normal file
33
theme/layout/DesktopNav.tsx
Normal file
@@ -0,0 +1,33 @@
|
||||
import React, { FC } from "react";
|
||||
import { Button, HStack, Link } from "@chakra-ui/react";
|
||||
import navItems, { NavItem } from "./navItems";
|
||||
|
||||
interface DesktopNavProps {
|
||||
sticky?: boolean;
|
||||
}
|
||||
|
||||
const DesktopNav: FC<DesktopNavProps> = ({ sticky }: DesktopNavProps) => {
|
||||
return (
|
||||
<HStack
|
||||
as="nav"
|
||||
d={{ base: "none", lg: "flex" }}
|
||||
h="auto"
|
||||
w="auto"
|
||||
spacing={4}
|
||||
// m="auto"
|
||||
justifyContent="center"
|
||||
alignContent="center"
|
||||
alignItems="center"
|
||||
>
|
||||
{navItems.map((navItem: NavItem) => {
|
||||
return (
|
||||
<Link id={"dekstop-" + navItem[0]} key={navItem[0]} href={navItem[1]}>
|
||||
<Button variant={sticky ? "stickyNav" : "nav"}>{navItem[0]}</Button>
|
||||
</Link>
|
||||
);
|
||||
})}
|
||||
</HStack>
|
||||
);
|
||||
};
|
||||
|
||||
export default DesktopNav;
|
||||
98
theme/layout/Footer.tsx
Normal file
98
theme/layout/Footer.tsx
Normal file
@@ -0,0 +1,98 @@
|
||||
import React, { useEffect, useRef, useState } from "react";
|
||||
import {
|
||||
Box,
|
||||
// HStack,
|
||||
Text,
|
||||
VStack,
|
||||
Link,
|
||||
// Icon,
|
||||
// Image,
|
||||
// Button,
|
||||
// BoxProps,
|
||||
} from "@chakra-ui/react";
|
||||
// import BackToTopButton from "./BackToTopButton";
|
||||
// import { motion } from "framer-motion";
|
||||
|
||||
// export const MotionBox = motion<BoxProps>(Box);
|
||||
|
||||
const Footer = (): JSX.Element => {
|
||||
// const [showBackToTop, setShowBackToTop] = useState<boolean>(false);
|
||||
// const lastScroll = useRef<number>(0);
|
||||
|
||||
// const handleScroll = (): void => {
|
||||
// if (window.scrollY >= 500) {
|
||||
// setShowBackToTop(true);
|
||||
// } else {
|
||||
// setShowBackToTop(false);
|
||||
// }
|
||||
|
||||
// const currentScroll =
|
||||
// window.pageYOffset || document.documentElement.scrollTop;
|
||||
|
||||
// lastScroll.current = currentScroll <= 0 ? 0 : currentScroll;
|
||||
// };
|
||||
|
||||
// useEffect(() => {
|
||||
// if (!window) {
|
||||
// console.log("waiting for mount");
|
||||
// } else if (window) {
|
||||
// window.addEventListener("scroll", handleScroll);
|
||||
// }
|
||||
|
||||
// return () => window.removeEventListener("scroll", handleScroll);
|
||||
// }, []);
|
||||
return (
|
||||
<Box bg="brand.footer" as="footer" w="100%" h="auto">
|
||||
{/* <BackToTopButton show={showBackToTop} /> */}
|
||||
<VStack
|
||||
h="auto"
|
||||
w="auto"
|
||||
py={12}
|
||||
spacing={5}
|
||||
justifyItems="center"
|
||||
justifyContent="center"
|
||||
>
|
||||
{/* <HStack color="brand.footerText" spacing={2}>
|
||||
<Text fontSize="xl">Deployed by</Text>
|
||||
<Link
|
||||
aria-label="Vercel"
|
||||
href="https://vercel.com/"
|
||||
rel="noopener"
|
||||
target="_blank"
|
||||
>
|
||||
<Icon fontSize="1.75rem" icon="logos:vercel" />
|
||||
</Link>
|
||||
</HStack> */}
|
||||
<VStack spacing={2}>
|
||||
{/* <MotionBox whileHover={{ scale: 1.1 }} whileTap={{ scale: 0.9 }}>
|
||||
<Link
|
||||
href="https://github.com/LucidCreationsMedia"
|
||||
target="_blank"
|
||||
rel="noopener"
|
||||
>
|
||||
<Button
|
||||
color="whiteAlpha"
|
||||
variant="credits"
|
||||
leftIcon={<Icon icon="akar-icons:github-fill" />}
|
||||
>
|
||||
View Codebase
|
||||
</Button>
|
||||
</Link>
|
||||
</MotionBox> */}
|
||||
<Text color="brand.footerText" fontSize="xs">
|
||||
© 2021 - {new Date().getFullYear()}
|
||||
<Link
|
||||
href="https://lucidcreations.media"
|
||||
rel="noopener"
|
||||
target="_blank"
|
||||
>
|
||||
Lucid Creations Media
|
||||
</Link>
|
||||
</Text>
|
||||
</VStack>
|
||||
</VStack>
|
||||
</Box>
|
||||
);
|
||||
};
|
||||
|
||||
export default Footer;
|
||||
170
theme/layout/Header.tsx
Normal file
170
theme/layout/Header.tsx
Normal file
@@ -0,0 +1,170 @@
|
||||
import React, { useEffect, useRef, useState } from "react";
|
||||
import {
|
||||
Heading,
|
||||
HStack,
|
||||
Box,
|
||||
IconButton,
|
||||
Flex,
|
||||
Menu,
|
||||
MenuButton,
|
||||
} from "@chakra-ui/react";
|
||||
import { Icon } from "@iconify/react";
|
||||
import DesktopNav from "./DesktopNav";
|
||||
import MobileNav from "./MobileNav";
|
||||
|
||||
const Header = (): JSX.Element => {
|
||||
// Sticky Navbar, Scroll Direction, and Back to Top Button Visibility
|
||||
const [stickyNavbar, setStickyNavbar] = useState<boolean>(false);
|
||||
const lastScroll = useRef<number>(0);
|
||||
const [scrollDirection, setScrollDirection] = useState<"up" | "down" | "top">(
|
||||
"top"
|
||||
);
|
||||
// const [scroll, setScroll] = useState<number>(0);
|
||||
|
||||
const handleScroll = (): void => {
|
||||
// Sticky Nav
|
||||
if (window.scrollY >= 20) {
|
||||
setStickyNavbar(true);
|
||||
} else {
|
||||
setStickyNavbar(false);
|
||||
}
|
||||
|
||||
// Scroll Direction
|
||||
const currentScroll =
|
||||
window.scrollY || window.pageYOffset || document.body.scrollTop;
|
||||
|
||||
if (currentScroll > lastScroll.current) {
|
||||
setScrollDirection("down");
|
||||
} else if (currentScroll <= 20) {
|
||||
setScrollDirection("top");
|
||||
} else {
|
||||
setScrollDirection("up");
|
||||
}
|
||||
|
||||
lastScroll.current = currentScroll <= 0 ? 0 : currentScroll;
|
||||
// setScroll(lastScroll.current = currentScroll <= 0 ? 0 : currentScroll)
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
if (!window) {
|
||||
console.log("waiting for mount");
|
||||
} else if (window) {
|
||||
window.addEventListener("scroll", handleScroll);
|
||||
}
|
||||
|
||||
return () => window.removeEventListener("scroll", handleScroll);
|
||||
}, []);
|
||||
|
||||
// Mobile Menu Icon && Open/Close
|
||||
const [open, setOpen] = useState<boolean>(false);
|
||||
const [hover, setHover] = useState<boolean>(false);
|
||||
|
||||
const menuIcon = (): JSX.Element => {
|
||||
const iconType = {
|
||||
default: <Icon icon="bx:bx-menu-alt-right" />,
|
||||
hover: <Icon icon="bx:bx-menu" />,
|
||||
open: <Icon icon="bx:bx-x" />,
|
||||
};
|
||||
|
||||
if (open) {
|
||||
return iconType.open;
|
||||
} else if (hover) {
|
||||
return iconType.hover;
|
||||
} else {
|
||||
return iconType.default;
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<Box
|
||||
zIndex={1000000}
|
||||
w="100%"
|
||||
pos="fixed"
|
||||
top="0"
|
||||
alignItems={"center"}
|
||||
boxShadow={
|
||||
open
|
||||
? "none"
|
||||
: stickyNavbar
|
||||
? "rgba(0, 134, 255, 0.75) 0px 0px 15px, rgba(0, 134, 255, 0.5) 0px 0px 3px 1px"
|
||||
: "none"
|
||||
}
|
||||
bg={
|
||||
open
|
||||
? "brand.main"
|
||||
: stickyNavbar
|
||||
? "rgba(49, 56, 220, 0.9)"
|
||||
: "transparent"
|
||||
}
|
||||
d={
|
||||
scrollDirection === "up" || scrollDirection === "top" ? "block" : "none"
|
||||
}
|
||||
transition=".5s ease"
|
||||
borderRadius="0px 0px 10px 10px"
|
||||
_hover={{
|
||||
bg: open ? "brand.main" : stickyNavbar ? "brand.main" : "transparent",
|
||||
boxShadow: open
|
||||
? "none"
|
||||
: stickyNavbar
|
||||
? "rgba(0, 134, 255, 0.9) 0px 0px 15px, rgba(0, 134, 255, 0.7) 0px 0px 3px 1px"
|
||||
: "none",
|
||||
}}
|
||||
h={open ? "125px" : "auto"}
|
||||
>
|
||||
{/* Logo | Site Name */}
|
||||
<Flex
|
||||
width="100%"
|
||||
justifyContent={{ base: "flex-start", sm: "center" }}
|
||||
alignItems="center"
|
||||
height={12}
|
||||
top={0}
|
||||
position="absolute"
|
||||
ml={4}
|
||||
d={{ base: "flex", lg: "none" }}
|
||||
>
|
||||
<Heading as="h1" fontSize="lg">
|
||||
LCM Potty Chart
|
||||
</Heading>
|
||||
</Flex>
|
||||
|
||||
{/* Desktop Nav Items and Mobile Menu Button */}
|
||||
<Box h="auto" w="100%" px={4}>
|
||||
<Flex h={12} alignItems={"center"} justifyContent={"space-between"}>
|
||||
<HStack
|
||||
w="100%"
|
||||
h="auto"
|
||||
alignItems="center"
|
||||
justifyContent="space-between"
|
||||
>
|
||||
<Box w="auto" d={{ base: "flex", lg: "none " }}></Box>
|
||||
<Box w="100%" d={{ base: "none", lg: "flex" }} m="auto">
|
||||
<Heading as="h1" size="md">
|
||||
LCM Potty Chart
|
||||
</Heading>
|
||||
</Box>
|
||||
<DesktopNav sticky={stickyNavbar} />
|
||||
</HStack>
|
||||
<Menu isLazy lazyBehavior="unmount" isOpen={open}>
|
||||
<MenuButton
|
||||
as={IconButton}
|
||||
aria-label="Mobile Menu"
|
||||
icon={menuIcon()}
|
||||
onClick={() => setOpen(!open)}
|
||||
onMouseEnter={() => setHover(true)}
|
||||
onMouseLeave={() => setHover(false)}
|
||||
d={{ base: "inline-flex", lg: "none" }}
|
||||
variant="mobileNav"
|
||||
bg={stickyNavbar ? "transparent" : "rgba(255, 255, 255, .15)"}
|
||||
type="button"
|
||||
border={stickyNavbar ? "1px solid #0068ff" : "none"}
|
||||
id="mobile-menu-button"
|
||||
/>
|
||||
<MobileNav updateOpen={setOpen} />
|
||||
</Menu>
|
||||
</Flex>
|
||||
</Box>
|
||||
</Box>
|
||||
);
|
||||
};
|
||||
|
||||
export default Header;
|
||||
25
theme/layout/Layout.tsx
Normal file
25
theme/layout/Layout.tsx
Normal file
@@ -0,0 +1,25 @@
|
||||
import React, { FC, ReactNode } from "react";
|
||||
|
||||
import type { AppProps } from "next/app";
|
||||
import Header from "../layout/Header";
|
||||
import { Box } from "@chakra-ui/layout";
|
||||
import Footer from "./Footer";
|
||||
|
||||
interface LayoutProps {
|
||||
children: ReactNode;
|
||||
elementType?: string;
|
||||
}
|
||||
|
||||
const Layout: FC<LayoutProps> = (
|
||||
{ children }: LayoutProps,
|
||||
{ pageProps }: AppProps
|
||||
) => {
|
||||
return (
|
||||
<Box w="100%">
|
||||
<Header {...pageProps} />
|
||||
<main>{children}</main>
|
||||
<Footer />
|
||||
</Box>
|
||||
);
|
||||
};
|
||||
export default Layout;
|
||||
53
theme/layout/MobileNav.tsx
Normal file
53
theme/layout/MobileNav.tsx
Normal file
@@ -0,0 +1,53 @@
|
||||
import React, { FC, Fragment } from "react";
|
||||
import {
|
||||
Button,
|
||||
Link,
|
||||
MenuDivider,
|
||||
MenuItem,
|
||||
MenuList,
|
||||
} from "@chakra-ui/react";
|
||||
import navItems, { NavItem } from "./navItems";
|
||||
|
||||
interface MobileNavProps {
|
||||
updateOpen: React.Dispatch<React.SetStateAction<boolean>>;
|
||||
}
|
||||
|
||||
const MobileNav: FC<MobileNavProps> = ({ updateOpen }: MobileNavProps) => {
|
||||
return (
|
||||
<MenuList
|
||||
as="nav"
|
||||
d={{ base: "block", lg: "none" }}
|
||||
bg="brand.main"
|
||||
h="auto"
|
||||
w="100%"
|
||||
p={0}
|
||||
border="none"
|
||||
boxShadow="none"
|
||||
>
|
||||
{navItems.map((navItem: NavItem, index: number) => {
|
||||
return (
|
||||
<MenuItem
|
||||
id={"mobile-" + navItem[0]}
|
||||
key={navItem[0]}
|
||||
w="auto"
|
||||
h="auto"
|
||||
p={0}
|
||||
_hover={{
|
||||
backgroundColor: "none",
|
||||
}}
|
||||
>
|
||||
<Link onClick={() => updateOpen(false)} href={navItem[1]}>
|
||||
{index === 0 ? <MenuDivider /> : <Fragment></Fragment>}
|
||||
<Button w="100vw" variant={"nav"} p={0} m="auto">
|
||||
{navItem[0]}
|
||||
</Button>
|
||||
<MenuDivider />
|
||||
</Link>
|
||||
</MenuItem>
|
||||
);
|
||||
})}
|
||||
</MenuList>
|
||||
);
|
||||
};
|
||||
|
||||
export default MobileNav;
|
||||
6
theme/layout/navItems.ts
Normal file
6
theme/layout/navItems.ts
Normal file
@@ -0,0 +1,6 @@
|
||||
export type NavItem = [string, string];
|
||||
export type NavItems = NavItem[];
|
||||
|
||||
const navItems: NavItems = [["Home", "#top"]];
|
||||
|
||||
export default navItems;
|
||||
30
tsconfig.json
Normal file
30
tsconfig.json
Normal file
@@ -0,0 +1,30 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"target": "es5",
|
||||
"lib": [
|
||||
"dom",
|
||||
"dom.iterable",
|
||||
"esnext"
|
||||
],
|
||||
"allowJs": true,
|
||||
"skipLibCheck": true,
|
||||
"strict": false,
|
||||
"forceConsistentCasingInFileNames": true,
|
||||
"noEmit": true,
|
||||
"esModuleInterop": true,
|
||||
"module": "esnext",
|
||||
"moduleResolution": "node",
|
||||
"resolveJsonModule": true,
|
||||
"isolatedModules": true,
|
||||
"jsx": "preserve",
|
||||
"incremental": true
|
||||
},
|
||||
"include": [
|
||||
"next-env.d.ts",
|
||||
"**/*.ts",
|
||||
"**/*.tsx"
|
||||
],
|
||||
"exclude": [
|
||||
"node_modules"
|
||||
]
|
||||
}
|
||||
Reference in New Issue
Block a user