2022-03-13 14:40:38 +08:00
|
|
|
import SvgIcon from '@/components/SvgIcon'
|
|
|
|
import useScroll from '@/hooks/useScroll'
|
|
|
|
import useUser from '@/hooks/useUser'
|
|
|
|
import { resizeImage } from '@/utils/common'
|
|
|
|
|
|
|
|
const NavigationButtons = () => {
|
|
|
|
const navigate = useNavigate()
|
|
|
|
enum ACTION {
|
|
|
|
BACK = 'back',
|
|
|
|
FORWARD = 'forward',
|
|
|
|
}
|
|
|
|
const handleNavigate = (action: ACTION) => {
|
|
|
|
if (action === ACTION.BACK) navigate(-1)
|
|
|
|
if (action === ACTION.FORWARD) navigate(1)
|
|
|
|
}
|
|
|
|
return (
|
2022-03-17 19:30:43 +08:00
|
|
|
<div className='flex gap-1'>
|
2022-03-13 14:40:38 +08:00
|
|
|
{[ACTION.BACK, ACTION.FORWARD].map(action => (
|
|
|
|
<div
|
|
|
|
onClick={() => handleNavigate(action)}
|
|
|
|
key={action}
|
2022-03-21 02:03:25 +08:00
|
|
|
className='app-region-no-drag btn-hover-animation rounded-lg p-2 text-gray-500 transition duration-300 after:rounded-full after:bg-black/[.06] hover:text-gray-900 dark:text-gray-300 dark:after:bg-white/10 dark:hover:text-gray-200'
|
2022-03-13 14:40:38 +08:00
|
|
|
>
|
2022-03-21 02:03:25 +08:00
|
|
|
<SvgIcon className='h-5 w-5' name={action} />
|
2022-03-13 14:40:38 +08:00
|
|
|
</div>
|
|
|
|
))}
|
|
|
|
</div>
|
|
|
|
)
|
|
|
|
}
|
|
|
|
|
|
|
|
const SearchBox = () => {
|
2022-03-29 00:11:05 +08:00
|
|
|
const { type } = useParams()
|
|
|
|
const [keywords, setKeywords] = useState('')
|
2022-03-27 15:21:48 +08:00
|
|
|
const navigate = useNavigate()
|
|
|
|
const toSearch = (e: React.KeyboardEvent) => {
|
2022-03-29 00:11:05 +08:00
|
|
|
if (!keywords) return
|
2022-03-27 15:21:48 +08:00
|
|
|
if (e.key === 'Enter') {
|
2022-03-29 00:11:05 +08:00
|
|
|
navigate(`/search/${keywords}${type ? `/${type}` : ''}`)
|
2022-03-27 15:21:48 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-03-13 14:40:38 +08:00
|
|
|
return (
|
2022-03-27 15:21:48 +08:00
|
|
|
<div className='app-region-no-drag group flex w-[16rem] cursor-text items-center rounded-full bg-gray-500 bg-opacity-5 pl-2.5 pr-2 transition duration-300 hover:bg-opacity-10 dark:bg-gray-300 dark:bg-opacity-5'>
|
2022-03-13 14:40:38 +08:00
|
|
|
<SvgIcon
|
2022-03-27 15:21:48 +08:00
|
|
|
className='mr-2 h-4 w-4 text-gray-500 transition duration-300 group-hover:text-gray-600 dark:text-gray-400 dark:group-hover:text-gray-200'
|
2022-03-17 19:30:43 +08:00
|
|
|
name='search'
|
2022-03-13 14:40:38 +08:00
|
|
|
/>
|
|
|
|
<input
|
2022-03-29 00:11:05 +08:00
|
|
|
value={keywords}
|
|
|
|
onChange={e => setKeywords(e.target.value)}
|
2022-03-27 15:21:48 +08:00
|
|
|
onKeyDown={toSearch}
|
2022-03-17 19:30:43 +08:00
|
|
|
type='text'
|
2022-03-27 15:21:48 +08:00
|
|
|
className='flex-grow bg-transparent placeholder:text-gray-500 dark:text-white dark:placeholder:text-gray-400'
|
2022-03-21 02:03:25 +08:00
|
|
|
placeholder='搜索'
|
2022-03-13 14:40:38 +08:00
|
|
|
/>
|
2022-03-27 15:21:48 +08:00
|
|
|
<div
|
2022-03-29 00:11:05 +08:00
|
|
|
onClick={() => setKeywords('')}
|
2022-03-27 15:21:48 +08:00
|
|
|
className={classNames(
|
2022-03-30 16:54:11 +08:00
|
|
|
'cursor-default rounded-full p-1 text-gray-600 transition hover:bg-gray-400/20 dark:text-white/50 dark:hover:bg-white/20',
|
2022-03-29 00:11:05 +08:00
|
|
|
!keywords && 'hidden'
|
2022-03-27 15:21:48 +08:00
|
|
|
)}
|
|
|
|
>
|
|
|
|
<SvgIcon className='h-4 w-4' name='x' />
|
|
|
|
</div>
|
2022-03-13 14:40:38 +08:00
|
|
|
</div>
|
|
|
|
)
|
|
|
|
}
|
|
|
|
|
|
|
|
const Settings = () => {
|
|
|
|
return (
|
2022-03-17 19:30:43 +08:00
|
|
|
<div className='app-region-no-drag btn-hover-animation rounded-lg p-2.5 text-gray-500 transition duration-300 after:rounded-full after:bg-black/[.06] hover:text-gray-900 dark:text-gray-300 dark:after:bg-white/10 dark:hover:text-gray-200'>
|
2022-03-21 02:03:25 +08:00
|
|
|
<SvgIcon className='h-[1.125rem] w-[1.125rem]' name='settings' />
|
2022-03-13 14:40:38 +08:00
|
|
|
</div>
|
|
|
|
)
|
|
|
|
}
|
|
|
|
|
|
|
|
const Avatar = () => {
|
|
|
|
const navigate = useNavigate()
|
|
|
|
const { data: user } = useUser()
|
2022-03-27 15:21:48 +08:00
|
|
|
|
|
|
|
const avatarUrl = resizeImage(user?.profile?.avatarUrl ?? '', 'sm')
|
|
|
|
|
2022-03-13 14:40:38 +08:00
|
|
|
return (
|
|
|
|
<img
|
2022-03-27 15:21:48 +08:00
|
|
|
src={avatarUrl}
|
2022-03-13 14:40:38 +08:00
|
|
|
onClick={() => navigate('/login')}
|
2022-03-17 19:30:43 +08:00
|
|
|
className='app-region-no-drag h-9 w-9 rounded-full bg-gray-100 dark:bg-gray-700'
|
2022-03-13 14:40:38 +08:00
|
|
|
/>
|
2022-03-29 00:11:05 +08:00
|
|
|
// <div onClick={() => navigate('/login')}>
|
|
|
|
// <SvgIcon
|
|
|
|
// name='user'
|
|
|
|
// className='h-9 w-9 rounded-full bg-gray-400/10 p-1 text-gray-400'
|
|
|
|
// />
|
|
|
|
// </div>
|
2022-03-13 14:40:38 +08:00
|
|
|
)
|
|
|
|
}
|
|
|
|
|
|
|
|
const Topbar = () => {
|
|
|
|
/**
|
|
|
|
* Show topbar background when scroll down
|
|
|
|
*/
|
|
|
|
const [mainContainer, setMainContainer] = useState<HTMLElement | null>(null)
|
|
|
|
const scroll = useScroll(mainContainer, { throttle: 100 })
|
|
|
|
|
|
|
|
useEffect(() => {
|
|
|
|
setMainContainer(document.getElementById('mainContainer'))
|
|
|
|
}, [setMainContainer])
|
|
|
|
|
|
|
|
return (
|
|
|
|
<div
|
|
|
|
className={classNames(
|
|
|
|
'app-region-drag sticky top-0 z-30 flex h-16 min-h-[4rem] w-full cursor-default items-center justify-between px-8 transition duration-300',
|
|
|
|
!scroll.arrivedState.top &&
|
2022-03-17 14:45:04 +08:00
|
|
|
'bg-white bg-opacity-[.86] backdrop-blur-xl backdrop-saturate-[1.8] dark:bg-[#222] dark:bg-opacity-[.86]'
|
2022-03-13 14:40:38 +08:00
|
|
|
)}
|
|
|
|
>
|
2022-03-17 19:30:43 +08:00
|
|
|
<div className='flex gap-2'>
|
2022-03-13 14:40:38 +08:00
|
|
|
<NavigationButtons />
|
|
|
|
<SearchBox />
|
|
|
|
</div>
|
|
|
|
|
2022-03-17 19:30:43 +08:00
|
|
|
<div className='flex items-center gap-3'>
|
2022-03-13 14:40:38 +08:00
|
|
|
<Settings />
|
|
|
|
<Avatar />
|
|
|
|
</div>
|
|
|
|
</div>
|
|
|
|
)
|
|
|
|
}
|
|
|
|
|
|
|
|
export default Topbar
|