major refactor in navbar and searchbar wip

This commit is contained in:
amirhouieh 2023-05-16 18:19:18 +02:00
parent e704b36038
commit 4d27bc6a96
11 changed files with 264 additions and 64 deletions

View File

@ -1,46 +1,57 @@
import { Typography } from '@acid-info/lsd-react'
import styled from '@emotion/styled'
import { uiConfigs } from '@/configs/ui.configs'
import { NavbarFiller } from '@/components/Navbar/NavbarFiller'
import { Searchbar } from '@/components/Searchbar'
export default function Hero() {
return (
<Container>
<Title genericFontFamily="serif" component="h1" variant="h1">
<span>LOGOS</span>
<span> </span>
<span>PRESS ENGINE</span>
</Title>
<Description component="div" variant="body1">
Blog with media written by Logos members
</Description>
<HeroText>
<Title genericFontFamily="serif" component="h1" variant="h1">
<span>LOGOS</span>
<span> </span>
<span>PRESS ENGINE</span>
</Title>
<Description component="div" variant="body1">
Blog with media written by Logos members
</Description>
</HeroText>
<NavbarFiller />
<Searchbar className={'desktop'} />
</Container>
)
}
const Container = styled.div`
border-bottom: 1px solid rgb(var(--lsd-theme-primary));
@media (max-width: 768px) {
.desktop {
display: none;
}
}
`
const HeroText = styled.div`
display: flex;
flex-direction: column;
align-items: center;
padding: 16px 16px 8px 16px;
@media (max-width: 768px) {
align-items: flex-start;
text-align: left;
gap: 6px;
}
`
const Title = styled(Typography)`
@media (min-width: 1440px) {
font-size: 90px;
line-height: 98px;
font-size: var(--lsd-display1-fontSize);
line-height: var(--lsd-display1-lineHeight);
}
@media (max-width: 768px) {
font-size: 36px;
line-height: 44px;
> span:last-of-type {
display: block;
}
font-size: var(--lsd-h4-fontSize);
line-height: var(--lsd-h4-lineHeight);
}
`

View File

@ -1,38 +1,149 @@
import styled from '@emotion/styled'
import { IconButton } from '@acid-info/lsd-react'
import { CloseIcon, IconButton, SearchIcon } from '@acid-info/lsd-react'
import { LogosIcon } from '../Icons/LogosIcon'
import { SunIcon } from '../Icons/SunIcon'
import { MoonIcon } from '../Icons/MoonIcon'
import { uiConfigs } from '@/configs/ui.configs'
import Link from 'next/link'
import styles from '@/components/Searchbar/Search.module.css'
import React, { useEffect, useState } from 'react'
import { Searchbar } from '@/components/Searchbar'
import { useScrollDirection } from '@/utils/ui.utils'
import { useRouter } from 'next/router'
import { useSearchBarContext } from '@/context/searchbar.context'
interface NavbarProps {
isDark: boolean
toggle: () => void
onSearch?: (query: string, tags: string[]) => void
onReset?: () => void
}
export default function Navbar({ isDark, toggle }: NavbarProps) {
export default function Navbar({
isDark,
toggle,
onReset,
onSearch,
}: NavbarProps) {
const { resultsNumber } = useSearchBarContext()
const { pathname } = useRouter()
const isSearchPage = pathname === '/search'
const [hideSearch, setHideSearch] = useState(
resultsNumber === null && !isSearchPage,
)
const [hide, setHide] = useState(false)
const scrollDirection = useScrollDirection()
const onSearchIconClick = () => {
setHideSearch(!hideSearch)
}
useEffect(() => {
if (scrollDirection) {
setHide(scrollDirection === 'down')
if (!hideSearch && resultsNumber === null) {
setHideSearch(scrollDirection === 'down')
}
}
setHideSearch(resultsNumber === null)
}, [scrollDirection, resultsNumber])
const className = pathname.split('/')[1] + '_page'
return (
<Container>
<LogosIconContainer href={'/'}>
<LogosIcon color="primary" />
</LogosIconContainer>
<Icons>
<IconButton size="small" onClick={() => toggle()}>
{isDark ? <SunIcon color="primary" /> : <MoonIcon color="primary" />}
</IconButton>
</Icons>
<Container className={`${hide ? 'hide' : ''} ${className}`}>
<AppBar>
<LogosIconContainer href={'/'}>
<LogosIcon color="primary" />
</LogosIconContainer>
<Icons>
<IconButton size="small" onClick={() => toggle()}>
{isDark ? (
<SunIcon color="primary" />
) : (
<MoonIcon color="primary" />
)}
</IconButton>
<IconButton
className={'searchIcon searchIconHome'}
size="small"
onClick={() => onSearchIconClick()}
>
<SearchIcon />
</IconButton>
</Icons>
</AppBar>
<MobileSearchContainer
className={`searchBar ${hideSearch ? 'hide' : ''}`}
>
<Searchbar
className={'mobile'}
onSearch={onSearch}
onReset={onReset}
withFilterTags={isSearchPage}
/>
</MobileSearchContainer>
</Container>
)
}
const Container = styled.nav`
const Container = styled.div`
width: 100%;
transition: top 0.2s;
position: fixed;
top: 0;
left: 0;
z-index: 101;
&._page {
@media (min-width: 768px) {
.searchBar {
display: none;
}
}
.searchIconHome {
display: none;
}
}
&.article_page,
&.search_page {
}
@media (max-width: 768px) {
&.hide {
top: -44px;
}
}
> * {
position: absolute;
top: 0;
left: 0;
}
`
const MobileSearchContainer = styled.div`
width: 100%;
transition: transform 0.3s ease-in-out;
z-index: 98;
transform: translateY(44px);
@media (max-width: 768px) {
display: block;
&.hide {
transform: translateY(0);
}
}
`
const AppBar = styled.nav`
display: flex;
padding: 8px 0;
align-items: center;
justify-content: center;
border-bottom: 1px solid rgb(var(--lsd-theme-primary));
position: fixed;
//position: fixed;
top: 0;
width: 100%;
height: 44px;
@ -78,6 +189,20 @@ const Icons = styled.div`
display: flex;
align-items: center;
margin-left: auto;
> *:last-of-type {
margin-left: -1px;
}
.searchIcon {
display: none;
}
@media (max-width: 768px) {
.searchIcon {
display: block;
}
}
`
const Selector = styled(IconButton)`

View File

@ -2,6 +2,19 @@
width: 100% !important;
}
.searchInput{
font-size: 14px;
transition: font-size 150ms ease-in-out;
}
.searchInput.active{
font-size: 28px;
}
@media (max-width: 768px) {
.searchInput.active{
font-size: 18px;
}
}
.searchButton{
border: none !important;
}

View File

@ -29,10 +29,18 @@ export type SearchbarProps = {
className?: string
onSearch?: (query: string, filterTags: string[]) => void
onReset?: () => void
withFilterTags?: boolean
beSticky?: boolean
}
export default function Searchbar(props: SearchbarProps) {
const { onSearch, onReset } = props
const {
onSearch,
beSticky,
onReset,
className,
withFilterTags = true,
} = props
const { resultsNumber, resultsHelperText, tags } = useSearchBarContext()
const [placeholder, setPlaceholder] = useState<string>()
@ -135,12 +143,12 @@ export default function Searchbar(props: SearchbarProps) {
}, [active, searchScope, query.length])
const showResultsNumber = resultsNumber !== null && active
const renderTagFilters = tags.length > 0 && !isArticlePage
const showTagFilters = renderTagFilters && active
const showTagFilters = withFilterTags && active
let height = 45
if (active) {
height += 10
height +=
typeof window !== 'undefined' ? (window.innerWidth > 768 ? 10 : 0) : 0
}
if (showResultsNumber) {
height += isArticlePage ? 22 : 26
@ -151,6 +159,7 @@ export default function Searchbar(props: SearchbarProps) {
return (
<SearchbarContainer
className={className}
onUnfocus={() => {
setActive(false)
if (router.query.query && router.query.query.length > 0) {
@ -161,6 +170,7 @@ export default function Searchbar(props: SearchbarProps) {
transition: 'height 150ms ease-in-out',
height: active ? `${height}px` : 'auto',
}}
beSticky={beSticky}
>
<SearchBox>
<TextField
@ -177,10 +187,7 @@ export default function Searchbar(props: SearchbarProps) {
// height: active ? '56px' : 'auto',
}}
inputProps={{
style: {
fontSize: active ? '28px' : '14px',
transition: 'font-size 150ms ease-in-out',
},
className: `${styles.searchInput} ${active ? styles.active : ''}`,
}}
/>
{searchScope === ESearchScope.ARTICLE && (
@ -204,7 +211,7 @@ export default function Searchbar(props: SearchbarProps) {
</IconButton>
</div>
</SearchBox>
{renderTagFilters && (
{withFilterTags && (
<TagsWrapper className={showTagFilters ? 'active' : ''}>
<FilterTags
tags={tags}
@ -223,9 +230,10 @@ export default function Searchbar(props: SearchbarProps) {
onClick={() => setActive(true)}
variant={'subtitle2'}
dangerouslySetInnerHTML={{
__html: [`${resultsNumber} matches`, resultsHelperText].join(
'<span class="dot">.</span>',
),
__html: [
`${resultsNumber} matches`,
`<span class="helper">${resultsHelperText}<span>`,
].join('<span class="dot">.</span>'),
}}
/>
</SearchbarContainer>
@ -241,6 +249,11 @@ const TagsWrapper = styled.div`
margin-top: 19px;
height: 24px;
}
@media (max-width: 768px) {
&.active {
margin-top: 10px;
}
}
`
const ResultsStatus = styled.div`
@ -298,6 +311,21 @@ const Collapsed = styled(Typography)`
b {
transform: translateY(-2px);
}
.helper,
.tags {
display: flex;
gap: 4px;
}
@media (max-width: 768px) {
.helper {
text-overflow: ellipsis;
overflow: hidden;
width: 200px;
white-space: nowrap;
}
}
`
const SearchBox = styled.div`
display: flex;

View File

@ -9,12 +9,16 @@ import { useRouter } from 'next/router'
type Props = PropsWithChildren<{
onUnfocus?: () => void
style?: any
className?: string
beSticky?: boolean
}>
export function SearchbarContainer({
children,
onUnfocus = nope,
style = {},
className,
beSticky = false,
}: Props) {
const { pathname } = useRouter()
const isSearchPage = pathname === '/search'
@ -40,7 +44,7 @@ export function SearchbarContainer({
<SearchBarWrapper
style={style}
ref={stickyRef}
className={sticky || isSearchPage || isArticlePage ? 'sticky' : ''}
className={`${className} ${beSticky && sticky ? 'sticky' : ''}`}
>
{children}
</SearchBarWrapper>
@ -59,10 +63,7 @@ const SearchBarWrapper = styled.div<Props>`
width: 100%;
background: rgb(var(--lsd-surface-primary));
//height: 44px;
border-bottom: 1px solid rgb(var(--lsd-border-primary));
//border-top: 1px solid rgb(var(--lsd-border-primary));
transition: top 0.2s ease-in-out;
box-sizing: border-box;

View File

@ -1,7 +1,7 @@
export const uiConfigs = {
navbarRenderedHeight: 45,
postSectionMargin: 108,
postSectionMobileMargin: 78,
postSectionMobileMargin: 48,
articleSectionMargin: 40,
maxContainerWidth: 1440,
articleRenderedMT: 45 * 2,

View File

@ -37,7 +37,6 @@ export const SearchBarProvider = ({ children }: any) => {
}, [router])
const resetResults = () => {
console.log('resetting results')
setResultsNumber(null)
setResultsHelperText(null)
}

View File

@ -19,9 +19,9 @@ export default function ArticleLayout({ children }: Props) {
return (
<>
<header className={styles.header}>
<Navbar isDark={isDarkState.get()} toggle={isDarkState.toggle} />
<Searchbar
searchScope={ESearchScope.ARTICLE}
<Navbar
isDark={isDarkState.get()}
toggle={isDarkState.toggle}
onSearch={onSearch}
onReset={onReset}
/>

View File

@ -22,19 +22,18 @@ export default function DefaultLayout(props: PropsWithChildren<any>) {
}}
>
<div
style={{
borderBottom: `1px solid rgb(${
isDarkState.get()
? defaultThemes.dark.palette.border.primary
: defaultThemes.light.palette.border.primary
})`,
}}
// style={{
// borderBottom: `1px solid rgb(${
// isDarkState.get()
// ? defaultThemes.dark.palette.border.primary
// : defaultThemes.light.palette.border.primary
// })`,
// }}
>
<Navbar isDark={isDarkState.get()} toggle={isDarkState.toggle} />
<Hero />
<NavbarFiller />
</div>
<Searchbar />
<Searchbar withFilterTags={false} />
</header>
<Main>{props.children}</Main>
<Footer />

View File

@ -15,9 +15,6 @@ export default function SearchLayout(props: PropsWithChildren<any>) {
<>
<header className={styles.header}>
<Navbar isDark={isDarkState.get()} toggle={isDarkState.toggle} />
{/*<NavbarFiller />*/}
<div style={{ height: `${uiConfigs.navbarRenderedHeight - 2}px` }} />
<Searchbar searchScope={ESearchScope.ARTICLE} />
</header>
<Main>{props.children}</Main>
<Footer />

View File

@ -1,4 +1,10 @@
import { MutableRefObject, RefObject, useEffect, useRef, useState } from 'react'
import React, {
MutableRefObject,
RefObject,
useEffect,
useRef,
useState,
} from 'react'
export const useSticky = <T extends HTMLElement>(dy: number = 0) => {
const stickyRef = useRef<T>(null)
@ -124,4 +130,25 @@ export function useWindowSize() {
}
}
export const useScrollDirection = () => {
const [scrollDirection, setScrollDirection] = useState<string | null>(null)
const [lastScrollTop, setLastScrollTop] = useState(0)
const handleScroll = () => {
const scrollTop = window.pageYOffset || document.documentElement.scrollTop
const scrollDirection = scrollTop > lastScrollTop ? 'down' : 'up'
setLastScrollTop(scrollTop)
setScrollDirection(scrollDirection)
}
React.useEffect(() => {
window.addEventListener('scroll', handleScroll, { passive: true })
return () => window.removeEventListener('scroll', handleScroll)
}, [lastScrollTop])
return scrollDirection
}
export default useWindowSize