Skip to content

Commit

Permalink
feat: init navbar
Browse files Browse the repository at this point in the history
  • Loading branch information
vien.nguyen2-tiki committed Sep 21, 2022
1 parent 6e7f1c2 commit 94ea627
Show file tree
Hide file tree
Showing 15 changed files with 2,915 additions and 744 deletions.
6 changes: 6 additions & 0 deletions components/Container/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
type ContainerProps = {
children: React.ReactNode;
};
export default function Container({ children }: ContainerProps) {
return <div className="container">{children}</div>;
}
44 changes: 5 additions & 39 deletions components/Layout.tsx
Original file line number Diff line number Diff line change
@@ -1,49 +1,15 @@
import React, { ReactNode } from "react";
import Header from "./Header";
import Navbar from "./Navbar";

type Props = {
children: ReactNode;
};

const Layout: React.FC<Props> = (props) => (
<div>
<Header />
<div className="layout">{props.children}</div>
<style jsx global>{`
html {
box-sizing: border-box;
}
*,
*:before,
*:after {
box-sizing: inherit;
}
body {
margin: 0;
padding: 0;
font-size: 16px;
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto,
Helvetica, Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji",
"Segoe UI Symbol";
background: rgba(0, 0, 0, 0.05);
}
input,
textarea {
font-size: 16px;
}
button {
cursor: pointer;
}
`}</style>
<style jsx>{`
.layout {
padding: 0 2rem;
}
`}</style>
<div className="dark--mode">
<Navbar />
<div className="layout" style={{height: "200vh"}}>{props.children}</div>

</div>
);

Expand Down
14 changes: 14 additions & 0 deletions components/Logo/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import Image from 'next/image'

type LogoProps = {
type?: 'transparent' | 'white'
}

export default function Logo({ type = 'white' }: LogoProps) {
return (
<div className='block-center bold contrast-color-100'>
<Image src={`/images/logo/${type}_logo.svg`} width={48} height={48} />
Astra Protocol
</div>
)
}
116 changes: 116 additions & 0 deletions components/Navbar/Navigation.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,116 @@
import clsx from 'clsx'
import Link from 'next/link'
import { useRef, useState } from 'react'
import cloneDeep from 'lodash/cloneDeep'

import styles from './style.module.scss'
import useOutsideAlerter from './useOutsideAlerter'

type SubMenuItem = {
id: string
label: string
prefix?: JSX.Element
suffix?: JSX.Element
link?: string
show?: boolean
submenus?: { id: string; label: string; link: string; show?: boolean }[]
}
export type MenuItem = {
id: string
label: string
link?: string
show?: boolean
submenus?: SubMenuItem[]
}

type NavigationProps = {
items: MenuItem[]
}

export default function Navigation({ items }: NavigationProps) {
const [_menuItems, setMenuItems] = useState(items)
const wrapperRef = useRef(null)
const hideMenu = () => setMenuItems(items)
useOutsideAlerter(wrapperRef, hideMenu)

const _renderLink = (link, text) => {
return link ? (
<Link href={link}>
<span className="text-bold contrast-color-70">{text}</span>
</Link>
) : (
<span className="text-bold contrast-color-70">{text}</span>
)
}
/**
*
* @param id string[]: [url level 1, url level 2 ]
*/
const _showSubMenu = (event: React.MouseEvent<HTMLElement>, ids: string[]) => {
event.stopPropagation()
let newItems = cloneDeep(items)
let currentSubItems = newItems
for (let id of ids) {
if (!id) {
break
}
const selectItem = currentSubItems.find(item => item.id === id)
selectItem.show = true
if (selectItem.submenus) {
currentSubItems = selectItem.submenus
} else {
break
}
}
if (ids.length > 0) {
setMenuItems(newItems)
}
}

return (
<ul className={styles.navigation} ref={wrapperRef}>
{_menuItems.map(({ link, label, show, id, submenus: sub1 }) => (
<li
key={`${link} + ${label}`}
className={clsx(styles.item, 'margin-left-lg', 'padding-left-md', 'padding-sm', 'padding-right-md')}
onClick={event => _showSubMenu(event, [id])}
>
{_renderLink(link, label)}
{sub1?.length > 0 && (
<ul
className={clsx(styles.submenu, 'same-bg-color-100', 'radius-sm', "border-solid border-sm", {
[styles.show]: show
})}
>
{sub1.map(menu => (
<li
className={clsx('padding-left-md padding-sm')}
key={`${menu.link} + ${menu.label}`}
onClick={event => _showSubMenu(event, [id, menu.id])}
>
{_renderLink(menu.link, menu.label)}
{menu.submenus && (
<ul
className={clsx(styles.submenu2, 'contrast-bg-color-100', 'radius-xs', {
[styles.show]: menu.show
})}
>
{menu.submenus.map(sub2 => (
<li
className={clsx('padding-left-md padding-sm')}
key={`${sub2.link} + ${sub2.label}`}
>
{_renderLink(sub2.link, sub2.label)}
</li>
))}
</ul>
)}
</li>
))}
</ul>
)}
</li>
))}
</ul>
)
}
72 changes: 72 additions & 0 deletions components/Navbar/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
import styles from './style.module.scss'
import Logo from '../Logo'
import Navigation, { MenuItem } from './Navigation'
import clsx from 'clsx'
import { useEffect, useState } from 'react'

const items: MenuItem[] = [
{
id: '1',
label: 'Text 1',
submenus: [
{
id: '1.1',
label: 'Text1.1111 1111111',
submenus: [
{ id: '1.1.1', label: 'AA', link: 'aaa' },
{ id: '1.1.2', label: 'BB', link: 'aa' }
]
}
]
},
{
id: '2',
label: 'Text 2',
submenus: [
{
id: '2.1',
label: 'Text2.1',
link: '#'
},
{
id: '2.2',
label: 'Text2.2',
submenus: [
{ id: '2.2.1', label: 'AAa', link: 'aaa' },
{ id: '2.2.2', label: 'Bb', link: 'aa' }
]
}
]
}
]

export default function Navbar() {
const [shadow, setShadow] = useState(false)
useEffect(() => {
function scroll() {}
window.addEventListener('scroll', _ => {
const pos = document.body.scrollTop || document.documentElement.scrollTop
if (pos > 0) {
setShadow(true)
} else {
setShadow(false)
}
})
return () => {
window.removeEventListener('scroll', scroll)
}
}, [shadow])

return (
<nav className={clsx(styles.navbar, { 'shadow-xs': shadow })}>
<div className={clsx(styles.container, 'margin-auto')}>
<div className={styles.left}>
<Logo />
</div>
<div className={styles.right}>
<Navigation items={items} />
</div>
</div>
</nav>
)
}
59 changes: 59 additions & 0 deletions components/Navbar/style.module.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
.navbar {
width: 100%;
position: sticky;
background-color: red;
top: 0;
left: 0;
width: 100%;
z-index: 99;
.container {
max-width: 1400px;
display: flex;
justify-content: space-between;
align-items: center;
}
}

.navigation {
display: flex;
.item {
position: relative;
// &:hover > .submenu {
// visibility: visible;
// opacity: 1;
// }
.show {
visibility: visible !important;
opacity: 1 !important;
}
.submenu {
position: absolute;
top: 100%;
left: 0;
visibility: hidden;
opacity: 0;
transition: opacity 0.5s;
a {
text-decoration: none;
font-weight: bold;
&:hover {
opacity: 0.8;
}
}
> li {
position: relative;
}
// > li:hover > .submenu2 {
// visibility: visible;
// opacity: 1;
// }
.submenu2 {
position: absolute;
right: -60%;
top: 0;
visibility: hidden;
opacity: 0;
}
}
}
}
24 changes: 24 additions & 0 deletions components/Navbar/useOutsideAlerter.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import { useEffect } from 'react'

/**
* Hook that alerts clicks outside of the passed ref
*/
export default function useOutsideAlerter(ref, hideMenu: Function) {
useEffect(() => {
/**
* Alert if clicked on outside of element
*/
function handleClickOutside(event) {
if (ref.current && !ref.current.contains(event.target)) {
// alert('You clicked outside of me!')O
hideMenu();
}
}
// Bind the event listener
document.addEventListener('mousedown', handleClickOutside)
return () => {
// Unbind the event listener on clean up
document.removeEventListener('mousedown', handleClickOutside)
}
}, [ref])
}
13 changes: 13 additions & 0 deletions next.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,19 @@ module.exports = {
issuer: { and: [/\.(js|ts)x?$/] },
use: ['@svgr/webpack']
})
// config.module.rules.push({
// test: /\.s?css$/,
// use: ['style-loader', 'css-loader', 'sass-loader'],
// })
// config.module.rules.push({
// test: /\.s[ac]ss$/i,
// use: {
// loader: 'sass-loader',
// // options: {
// // additionalData: '@import "src/styles/variables.scss";'
// // }
// }
// })

return config
}
Expand Down
2 changes: 2 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@
"author": "",
"license": "MIT",
"dependencies": {
"@types/lodash": "^4.14.185",
"astra-ui": "file:/Users/lap02631/astra/astra-ui",
"axios": "^0.27.2",
"clsx": "^1.2.1",
"lodash": "^4.17.21",
Expand Down
2 changes: 2 additions & 0 deletions pages/_app.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
import { NextIntlProvider } from 'next-intl'
import { AppProps } from 'next/app'

import "astra-ui/lib/shared/style.css";

const App = ({ Component, pageProps }: AppProps) => {
return (
<NextIntlProvider messages={pageProps.messages}>
Expand Down
Loading

0 comments on commit 94ea627

Please sign in to comment.