diff --git a/components/footer.tsx b/components/footer.tsx index 4b8550f..c5824b5 100644 --- a/components/footer.tsx +++ b/components/footer.tsx @@ -1,4 +1,4 @@ -import { Footer } from "../components/styles/generic" +import { Footer, MobileFooter } from "../components/styles/generic" const PageFooter = () => { return ( @@ -7,5 +7,13 @@ const PageFooter = () => { ); } + +export const NavMenuFooter = () => { + return ( + + Built using Next.js + + ); +} export default PageFooter; \ No newline at end of file diff --git a/components/layout.tsx b/components/layout.tsx index 8482b63..6a2f966 100644 --- a/components/layout.tsx +++ b/components/layout.tsx @@ -2,40 +2,80 @@ import PageFooter from './footer'; import PageNavbar from './navbar'; import Script from 'next/script'; import { Page, Main } from './styles/generic'; +import useWindowSize from './windowsize'; const Layout = ({ children }: { children: React.ReactNode }) => { - return ( - - - + const isMobile = useWindowSize(); - -
- {children} -
- -
- ); + let ret: JSX.Element; + if(isMobile) { + ret = ( + + + + + +
+ {children} +
+
+ ); + } + else { + ret = ( + + + + + +
+ {children} +
+ +
+ ); + } + return ret; } export default Layout; \ No newline at end of file diff --git a/components/navbar.tsx b/components/navbar.tsx index 6994205..83a84c5 100644 --- a/components/navbar.tsx +++ b/components/navbar.tsx @@ -1,27 +1,74 @@ import { usePathname } from 'next/navigation' -import { NavBar, NavLink, NavWrap } from './styles/navbar'; +import { NavBarMobile, NavIndicator, NavIndicators , NavSideMenu, NavSideMenuButton, NavSideMenuPanel, NavLinkMobile, NavWrapMobile, NavWrapMobileGhost, NavSideMenuGhost } from './styles/navbar/mobile'; +import { NavBar, NavLink, NavWrap } from './styles/navbar/desktop'; import { StyleSelector, StyleSelectorPlaceholder } from './themeselector'; import Links from '../public/data/navbar.json'; +import { useState } from 'react'; +import { NavMenuFooter } from './footer'; -const PageNavbar = () => { +const PageNavbar = ({ mobile }: { mobile: number }) => { const path = usePathname(); + const [sideBarActive, setSideBarActive] = useState(true); //DEBUG: set to false - return ( - - - - {Links.links.map((item) => ( - - {item.name} + function handleSidebar(event: any) { + if (!event.currentTarget.contains(event.relatedTarget)) { + setSideBarActive(false); + } + } + + let navbar: JSX.Element; + if (mobile) { + navbar = ( + <> + handleSidebar(event)} active={+sideBarActive}> + setSideBarActive(sideBarActive => !sideBarActive)} active={+sideBarActive}>Menu + + + {Links.links.map((item) => ( + + {item.name} + + ))} + + Mastodon + + + + + + + + + + + {Links.links.map((item) => ( + + ))} + + + + + ); + } + else { + navbar = ( + + + + {Links.links.map((item) => ( + + {item.name} + + ))} + + Mastodon - ))} - - Mastodon - - - - - ); + + + + ); + } + return navbar; } export default PageNavbar; \ No newline at end of file diff --git a/components/styles/content.tsx b/components/styles/content.tsx index 31c35d8..376cc94 100644 --- a/components/styles/content.tsx +++ b/components/styles/content.tsx @@ -24,7 +24,7 @@ export const PageTitle = styled.h1` export const PageDescription = styled.p` background-color: ${({ theme }) => theme.colors.background ? theme.colors.background : ""}; padding: 0.25rem 0.5rem; - margin: 4rem 0; + margin: 4rem 2rem; line-height: 1.5; font-size: 1.5rem; text-align: center; diff --git a/components/styles/generic.tsx b/components/styles/generic.tsx index b74877c..e8354de 100644 --- a/components/styles/generic.tsx +++ b/components/styles/generic.tsx @@ -7,13 +7,18 @@ export const StyledBody = styled.body` Ubuntu, Cantarell, Fira Sans, Droid Sans, Helvetica Neue, sans-serif; ` -export const Page = styled.div` +interface MobilePropType { + mobile?: number; +} + +export const Page = styled.div` width: 100%; background-color: ${({ theme }) => theme.colors.background}; background-image: ${({ theme }) => theme.backgroundImage ? "url(" + theme.backgroundImage + ")" : ""}; background-repeat: no-repeat; background-attachment: fixed; background-size: cover; + background-position: ${ props => props.mobile ? ({ theme }) => theme.backgroundOffset ? theme.backgroundOffset : "60%" : ""}; ` export const Main = styled.main` @@ -43,3 +48,9 @@ export const Footer = styled.footer` flex-grow: 1; } ` + +export const MobileFooter = styled(Footer)` + white-space: nowrap; + flex: 0.5; + width: 100%; +` diff --git a/components/styles/navbar.tsx b/components/styles/navbar/desktop.tsx similarity index 96% rename from components/styles/navbar.tsx rename to components/styles/navbar/desktop.tsx index 4b9f0d3..b77b140 100644 --- a/components/styles/navbar.tsx +++ b/components/styles/navbar/desktop.tsx @@ -40,8 +40,6 @@ export const NavLink = styled(Link) ` margin: 0.2rem; border-radius: 5px; display: flex; - justify-content: center; - align-items: center; transition: all 0.1s ease; &:hover { diff --git a/components/styles/navbar/mobile.tsx b/components/styles/navbar/mobile.tsx new file mode 100644 index 0000000..9c903e5 --- /dev/null +++ b/components/styles/navbar/mobile.tsx @@ -0,0 +1,249 @@ +import styled from 'styled-components' +import Link from 'next/link'; +import { NavBar, NavLink, NavWrap } from './desktop'; + +interface ActivePropType { + active?: number; +} + +interface MultipliesPropType { + num?: number; +} + +export const NavWrapMobile = styled(NavWrap)` + width: 100%; + flex-direction: row; + position: fixed; + z-index: 50; +` + +export const NavWrapMobileGhost = styled.div` + position: relative; + width: 100%; + height: 50px; +` + +export const NavSideMenu = styled.div ` + position: fixed; + top: 0%; left: 0%; right: 0%; bottom: 0%; + max-width: ${props => props.active ? "240px" : "0px"}; + max-height: ${props => props.active ? "100%" : "50px"}; + display: flex; + justify-content: space-between; + flex-direction: column; + height: 100%; + z-index: 100; + border-right: ${ props => ({ theme }) => { + let ret: string; + if(props.active) { + ret = "1px solid " + theme.colors.primary; + } + else { + ret = "0px solid"; + } + return ret; + }}; + + background-color: ${ props => ({ theme }) => { + let ret: string; + if (props.active) { + ret = theme.colors.background; + } + else { + ret = ""; + } + return ret; + }}; + backdrop-filter: ${props => props.active ? "blur(5px)" : ""}; + + overflow-x: hidden; + transition-property: max-width, max-height, border-right, background-color, backdrop-filter; + transition-timing-function: ease-in-out; + transition-duration: 0.15s, 0s; + transition-delay: ${props => props.active ? "0s" : "0s, 0.15s"}; +` + +export const NavSideMenuPanel = styled.div ` + height: 100%; + width: 240px; + overflow: hidden; + display: flex; + flex-direction: column; + align-items: left; +` + +export const NavSideMenuButton = styled.button ` + position: ${ props => props.active ? "absolute" : "fixed"}; + z-index: 200; + left: ${ props => props.active ? "165px" : "0px"}; + + transition-property: left, color, background-color, border-color; + transition-timing-function: ease-in-out; + transition-duration: 0.135s, 0.15s; + transition-delay: ${props => props.active ? "0.03s, 0s" : "0s, 0s"}; + + align-self: flex-end; + cursor: pointer; + margin: 12px; + color: ${props => ({ theme }) => { + let ret: string; + if (props.active) { + if (theme.invertButtons) { + ret = theme.colors.text ? theme.colors.text : theme.colors.secondary; + } + else { + ret = theme.colors.secondary; + } + } + else { + ret = theme.colors.primary; + } + return ret; + }}; + background-color: ${props => ({ theme }) => { + let ret: string; + if (props.active) { + if (theme.invertButtons) { + ret = theme.colors.secondary; + } + else { + ret = theme.colors.background; + } + } + else { + ret = theme.colors.background; + } + return ret; + }}; + + border: 2px solid; + border-radius: 5px; + border-color: ${props => ({ theme }) => { + let ret: string; + if (props.active) { + if (theme.invertButtons) { + ret = theme.colors.text ? theme.colors.text : theme.colors.secondary; + } + else { + ret = theme.colors.secondary; + } + } + else { + ret = theme.colors.primary; + } + return ret; + }}; + + &:hover { + color: ${({ theme }) => { + let ret: string; + if (theme.invertButtons) { + ret = theme.colors.text ? theme.colors.text : theme.colors.primary; + } + else { + ret = theme.colors.secondary; + } + return ret; + }}; + + background-color: ${({ theme }) => { + let ret: string; + if (theme.invertButtons) { + ret = theme.colors.secondary; + } + else { + ret = theme.colors.background; + } + return ret; + }}; + + border-color: ${({ theme }) => { + let ret: string; + if (theme.invertButtons) { + ret = theme.colors.text ? theme.colors.text : theme.colors.secondary; + } + else { + ret = theme.colors.secondary; + } + return ret; + }}; + } +` + +export const NavSideMenuGhost = styled.div ` + flex: ${ props => props.num ? props.num * 2 : 2 }; +` + +export const NavBarMobile = styled(NavBar)` + margin-top: 56px; + flex-direction: column; + align-items: flex-start; +` + +export const NavLinkMobile = styled(NavLink)` + display: block; + margin-left: 1rem; + margin-top: 1rem; + padding: 0.5rem; + border: none; + border-top: 2px solid; + width: 80%; + text-align: center; + + color: ${props => ({ theme }) => props.active ? theme.colors.secondary : theme.colors.primary}; + background-color: ${({ theme }) => theme.colors.backgroundAlt ? theme.colors.backgroundAlt : theme.colors.background}; + + &:hover { + color: ${({ theme }) => theme.colors.secondary }; + background-color: ${({ theme }) => theme.colors.backgroundAlt ? theme.colors.backgroundAlt : theme.colors.background}; + } +` + +export const NavIndicators = styled.nav` + background-color: ${({ theme }) => theme.colors.background}; + background-image: ${({ theme }) => theme.backgroundImage ? "url(" + theme.backgroundImage + ")" : ""}; + background-repeat: no-repeat; + background-attachment: fixed; + background-size: cover; + background-position: ${({ theme }) => theme.backgroundOffset ? theme.backgroundOffset : "60%"}; + width: 100%; + display: flex; + flex: 1; + padding: 1rem 0; + flex-wrap: nowrap; + justify-content: center; + align-items: center; +` + +export const NavIndicator = styled(Link) ` + margin: 0.2rem; + border-radius: 50%; + aspect-ratio: 1; + width: 10px; + border: 1px solid; + border-color: ${ props => ({ theme }) => props.active ? + theme.invertButtons ? theme.colors.secondary : theme.colors.primary : + theme.colors.primary + }; + + background-color: ${props => ({ theme }) => props.active ? + theme.invertButtons ? theme.colors.secondary : theme.colors.primary : + theme.colors.background + }; + + &:hover { + border-color: ${ props => ({ theme }) => + theme.invertButtons ? theme.colors.secondary : theme.colors.primary + }; + + color: ${({ theme }) => theme.invertButtons ? + theme.invertButtons ? theme.colors.secondary : theme.colors.primary : + theme.colors.primary + }; + + background-color: ${({ theme }) => theme.invertButtons ? + theme.colors.secondary : + theme.colors.primary + }; + } +` \ No newline at end of file diff --git a/components/styles/themedropdown.tsx b/components/styles/themedropdown/desktop.tsx similarity index 100% rename from components/styles/themedropdown.tsx rename to components/styles/themedropdown/desktop.tsx diff --git a/components/styles/themedropdown/mobile.tsx b/components/styles/themedropdown/mobile.tsx new file mode 100644 index 0000000..36a5eb3 --- /dev/null +++ b/components/styles/themedropdown/mobile.tsx @@ -0,0 +1,49 @@ +import styled from 'styled-components'; +import { ThemeDropDown, ThemeDropDownButton, ThemeDropDownOption, ThemeDropDownOptions } from './desktop'; + +export const ThemeDropDownMobile = styled(ThemeDropDown)` + width: 80%; + margin-left: 1rem; +`; + +export const ThemeDropDownButtonMobile = styled(ThemeDropDownButton)` + width: 100%; + padding: 0.5rem; + border: none; + border-top: 2px solid; + border-left: ${ props => props.show ? "2px solid" : "0px solid"}; + border-right: ${ props => props.show ? "2px solid" : "0px solid"}; + + color: ${props => ({ theme }) => props.focus ? theme.colors.secondary : theme.colors.primary}; + background-color: ${({ theme }) => theme.colors.backgroundAlt ? theme.colors.backgroundAlt : theme.colors.background}; + + &:focus,:hover { + color: ${({ theme }) => theme.colors.secondary}; + + background-color: ${({ theme }) => theme.colors.backgroundAlt ? theme.colors.backgroundAlt : theme.colors.background}; + } + + transition-property: color, border-bottom-left-radius, border-bottom-right-radius, background-color, border-left, border-right; + transition-timing-function: ease; + transition-duration: 0.15s; + transition-delay: 0s, ${ props => props.show ? "0s" : "0.6s, 0.6s, 0s, 0.6s, 0.6s" }; +`; + +export const ThemeDropDownOptionsMobile = styled(ThemeDropDownOptions)` + background-image: unset; + background-color: transparent; + border: 2px solid ${props => ({ theme }) => props.focus ? theme.colors.secondary : theme.colors.primary}; + border-top: 0; + max-height: ${ props => props.show ? "100%" : "0%"}; + position: relative; + width: 100%; +`; + +export const ThemeDropDownOptionMobile = styled(ThemeDropDownOption)` + text-align: left; + margin: 0.5rem; + padding: 0rem 0.5rem; + width: 80%; + border-left: 2px solid; + border-radius: 5px; +`; \ No newline at end of file diff --git a/components/themeselector.tsx b/components/themeselector.tsx index eb4fee9..55be165 100644 --- a/components/themeselector.tsx +++ b/components/themeselector.tsx @@ -2,10 +2,11 @@ import { useUpdateTheme } from "../pages/_app"; import { useContext, useState } from 'react'; import { ThemeContext, DefaultTheme } from "styled-components"; import { darkTheme, lightTheme } from './themes'; -import { ThemeDropDown, ThemeDropDownButton, ThemeDropDownOption, ThemeDropDownOptions } from "./styles/themedropdown"; +import { ThemeDropDown, ThemeDropDownButton, ThemeDropDownOption, ThemeDropDownOptions } from "./styles/themedropdown/desktop"; +import { ThemeDropDownMobile, ThemeDropDownButtonMobile, ThemeDropDownOptionMobile, ThemeDropDownOptionsMobile } from "./styles/themedropdown/mobile"; import Themes from '../public/data/themes.json'; -export const StyleSelector = () => { +export const StyleSelector = ({ mobile }: { mobile: number }) => { const themes: DefaultTheme[] = Themes.themes; const updateTheme = useUpdateTheme(); const currentTheme = useContext(ThemeContext); @@ -34,7 +35,7 @@ export const StyleSelector = () => { } } - const [visible, setVisible] = useState(false); + const [visible, setVisible] = useState(true); //DEBUG set to false const [buttonFocus, setButtonFocus] = useState(visible); function handleBlur(event:any) { @@ -44,19 +45,39 @@ export const StyleSelector = () => { } } - return ( - handleBlur(event)}> - setButtonFocus(true)} onClick={() => setVisible(visible => !visible)}>{selectedTheme.themeName} - - - {themes.map((theme) => ( - updateThemeWithStorage(theme)}> - {theme.themeName} - - ))} - - - ); + let themeselector: JSX.Element; + if(mobile) { + themeselector = ( + handleBlur(event)}> + setButtonFocus(true)} onClick={() => setVisible(visible => !visible)}> + {selectedTheme.themeName} + + + {themes.map((theme) => ( + updateThemeWithStorage(theme)}> + {theme.themeName} + + ))} + + + ); + } + else { + themeselector = ( + handleBlur(event)}> + setButtonFocus(true)} onClick={() => setVisible(visible => !visible)}>{selectedTheme.themeName} + + + {themes.map((theme) => ( + updateThemeWithStorage(theme)}> + {theme.themeName} + + ))} + + + ); + } + return themeselector; } export const StyleSelectorPlaceholder = () => { diff --git a/components/windowsize.tsx b/components/windowsize.tsx new file mode 100644 index 0000000..61f6ca7 --- /dev/null +++ b/components/windowsize.tsx @@ -0,0 +1,34 @@ +import { useEffect, useState } from "react"; + +interface ScreenSize { + width: number | undefined; + height: number | undefined; +} + +export default function useWindowSize(): number { + const [windowSize, setWindowSize] = useState({ + width: undefined, + height: undefined, + }); + + useEffect(() => { + function handleResize() { + setWindowSize({ + width: window.innerWidth, + height: window.innerHeight, + }); + } + + window.addEventListener("resize", handleResize); + + handleResize(); + + return () => window.removeEventListener("resize", handleResize); + }, []); + if(typeof(windowSize.width) === "number") { + return windowSize.width <= 1080 ? 1 : 0; + } + else { + return 0; + } +} \ No newline at end of file diff --git a/public/data/themes.json b/public/data/themes.json index 18b4933..eeba534 100644 --- a/public/data/themes.json +++ b/public/data/themes.json @@ -52,6 +52,7 @@ "themeName": "Nordlys", "themeId": 4, "backgroundImage": "https://images4.alphacoders.com/112/1123390.jpg", + "backgroundOffset": "60%", "invertButtons": true, "colors": { "background": "#0008", diff --git a/styled.d.ts b/styled.d.ts index 1dc2225..d774093 100644 --- a/styled.d.ts +++ b/styled.d.ts @@ -5,6 +5,7 @@ declare module 'styled-components' { themeName: string, themeId: number, backgroundImage?: string, + backgroundOffset?: string, invertButtons?: boolean, colors: { background: string,