index.jsx 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346
  1. import styles from './styles.module.scss';
  2. // components
  3. import SidebarTrigger from '@ui/SidebarTrigger';
  4. import Headroom from 'react-headroom';
  5. import Logo from '@components/Logo';
  6. import AppBar from "@mui/material/AppBar";
  7. import {useSidebar} from "@contexts/sidebarContext";
  8. import {useThemeProvider} from "@contexts/themeContext";
  9. import {useEffect, useRef, useState} from 'react';
  10. import styled from 'styled-components/macro';
  11. import {Tab, Tabs} from "@mui/material";
  12. // import LINKS from "@constants/links";
  13. import Button from "@mui/material/Button";
  14. import {NavLink, useLocation, useNavigate} from "react-router-dom";
  15. import ToggleButtonGroup from "@mui/material/ToggleButtonGroup";
  16. import ToggleButton from "@mui/material/ToggleButton";
  17. import symbol from "@assets/symbol-defs.svg";
  18. import symbol_m from "@assets/symbol-mobile.svg";
  19. import {string} from "prop-types";
  20. import IconButton from "@mui/material/IconButton";
  21. import {animated,easings} from "@react-spring/web";
  22. import {useSpring} from "@react-spring/core";
  23. import {useWindowSize} from "react-use";
  24. import ResizeObserver from "resize-observer-polyfill";
  25. import {useSelector} from "react-redux";
  26. import {allTopNavs, getRoute, getRouteChain, isLogin, isLogined} from "@features/mainSlice";
  27. const i18n=global.i18n;
  28. const StyledToggleGroup= styled(ToggleButtonGroup)`
  29. border-radius: 0px !important;
  30. gap: 12px;
  31. `;
  32. const StyledToggle=styled(ToggleButton)`
  33. gap: 12px;
  34. text-transform:none !important;
  35. border: none !important;
  36. border-radius: var(--border-radius) !important;
  37. padding: 0px !important;
  38. &:hover, &:focus ,&.Mui-selected, &.active {
  39. background-color: var(--nav-item-hover) !important;
  40. a {
  41. color: var(--header);
  42. font-weight: 700;
  43. .icon {
  44. fill:${props => props.fill};
  45. }
  46. }
  47. }
  48. a {
  49. display: flex;
  50. align-items: center;
  51. color: var(--text);
  52. padding: 12px !important;
  53. transition: all var(--transition);
  54. font-family: var(--heading-font);
  55. gap: 8px;
  56. .icon {
  57. display: flex;
  58. align-items: center;
  59. justify-content: center;
  60. width: 18px;
  61. height: 18px;
  62. fill: var(--text);
  63. }
  64. }
  65. `;
  66. const DesktopNavToggle=()=>{
  67. const LINKS=useSelector(allTopNavs);
  68. const links=LINKS.slice(0,4);
  69. const {pathname} = useLocation();
  70. const [alignment, setAlignment] = useState(pathname);
  71. const currRoute=useSelector(getRoute(pathname));
  72. const getRootRoute=(pathname)=>{
  73. let route="";
  74. if (currRoute){
  75. let clv=currRoute.lv;
  76. if (clv==null)return route;
  77. for (let i = 0; i < LINKS.length; i++) {
  78. let link=LINKS[i];
  79. let lv=link.lv;
  80. if (clv.startsWith(lv)){
  81. route=link.path;
  82. break;
  83. }
  84. }
  85. }
  86. return route;
  87. }
  88. useEffect(() => {
  89. setAlignment(getRootRoute(pathname));
  90. }, [pathname]);
  91. const handleChange = (event, newAlignment) => {
  92. // setAlignment(newAlignment);
  93. };
  94. return (
  95. <StyledToggleGroup
  96. value={alignment}
  97. exclusive
  98. onChange={handleChange}
  99. >
  100. {
  101. links.map((link, index) => (
  102. <StyledToggle value={link.path} fill={link.fill.split(";").pop()} key={link.title}>
  103. <NavLink className="h4" to={link.path}>
  104. <svg className={"icon"}>
  105. <use xlinkHref={`${symbol}#${link.icon}`} />
  106. </svg>
  107. {i18n.t(link.title)}
  108. </NavLink>
  109. </StyledToggle>
  110. ))
  111. }
  112. </StyledToggleGroup>
  113. )
  114. }
  115. const DesktopSignInUp=()=>{
  116. const navigate=useNavigate();
  117. const isLogin=useSelector(isLogined);
  118. return (
  119. <div className="d-flex align-items-center justify-content-end">
  120. <IconButton aria-label="search">
  121. <svg className={"ls-icon"} style={{width:'20px',height:'20px'}}>
  122. <use xlinkHref={`${symbol}#icon_Search`} />
  123. </svg>
  124. </IconButton>
  125. {
  126. !isLogin?(
  127. <>
  128. <Button className={`m-button ${styles.btn_sign_in}`} onClick={()=>{
  129. navigate("#/signin");
  130. }}>{i18n.t('sign_in')}</Button>
  131. <Button className={`${styles.btn_sign_up}`} onClick={()=>{
  132. navigate("#/signup");
  133. }}>{i18n.t('sign_up')}</Button>
  134. </>
  135. ):(<></>)
  136. }
  137. <Button className="m-button">
  138. <svg className={"ls-icon"} style={{width:'20px',height:'20px'}}>
  139. <use xlinkHref={`${symbol}#icon_Chat`} />
  140. </svg>
  141. <div className="chat-green-dont"></div>
  142. </Button>
  143. <Button className="m-button" style={{marginLeft: '0.5rem'}}>
  144. <svg className={"ls-icon"} style={{width:'20px',height:'20px'}}>
  145. <use xlinkHref={`${symbol}#icon_Language`} />
  146. </svg>
  147. </Button>
  148. </div>
  149. )
  150. }
  151. const DesktopNav = () => {
  152. const {open} = useSidebar();
  153. const navstyle=open?styles.nav_open:"";
  154. return (
  155. <div
  156. className={`d-flex align-items-center justify-content-between ${styles.header} ${navstyle}`}>
  157. <DesktopNavToggle />
  158. <DesktopSignInUp/>
  159. </div>
  160. );
  161. }
  162. const MobileSignInUp=()=>{
  163. const navigate=useNavigate();
  164. const isLogin=useSelector(isLogined);
  165. return (
  166. <div className="d-flex align-items-center justify-content-end">
  167. {
  168. !isLogin?(
  169. <>
  170. <Button className={`m-button ${styles.btn_sign_in} ${styles.btn_sign_in_mobile}`} onClick={()=>{
  171. navigate("#/signin");
  172. }}>{i18n.t('sign_in')}</Button>
  173. <Button className={`${styles.btn_sign_up} ${styles.btn_sign_up_mobile}`} onClick={()=>{
  174. navigate("#/signup");
  175. }}>{i18n.t('sign_up')}</Button>
  176. </>
  177. ):(
  178. <></>
  179. )
  180. }
  181. {/*<Button className="m-button">*/}
  182. {/* <svg className={"ls-icon"} style={{width:'1.4rem',height:'1.4rem'}}>*/}
  183. {/* <use xlinkHref={`${symbol}#icon_Language`} />*/}
  184. {/* </svg>*/}
  185. {/*</Button>*/}
  186. </div>
  187. );
  188. }
  189. const MobileNav=()=>{
  190. return (
  191. <div
  192. className={`d-flex align-items-center justify-content-between ${styles.header} ${styles.header_mobile}`}>
  193. <div className={`d-flex flex-row flex-1`} style={{maxWidth:"55%"}}>
  194. <SidebarTrigger/>
  195. <Logo size="sm"/>
  196. </div>
  197. <MobileSignInUp/>
  198. </div>
  199. )
  200. }
  201. const PageHeader = () => {
  202. const LINKS=useSelector(allTopNavs);
  203. const {initHeaderHeight} = useThemeProvider();
  204. const {pathname} = useLocation();
  205. const navigate=useNavigate();
  206. const barRef = useRef(null);
  207. const currRoute=useSelector(getRoute(pathname));
  208. const chain=useSelector(getRouteChain(pathname));
  209. const {isMobile,showHeader,setShowHeader} = useThemeProvider();
  210. const [showMHeader, setShowMHeader] = useState(true);
  211. let lastscroll=0;
  212. const checkNavActive=(lv)=>{
  213. if (currRoute&&currRoute.lv)return currRoute.lv.startsWith(lv);
  214. else return false;
  215. }
  216. const getHeaderHeight = () => {
  217. const header = document.querySelector('#appbar');
  218. if (header!=null)return `${header.offsetHeight || 0}px`
  219. else return '0px';
  220. }
  221. const handleScroll=()=>{
  222. if (!barRef.showHeader)return;
  223. let scrollTop = document.documentElement.scrollTop; //滚动条滚动高度
  224. let dt=scrollTop-lastscroll;
  225. lastscroll=scrollTop;
  226. let show=false;
  227. if (dt>20){
  228. show=false;
  229. setShowMHeader(show);
  230. }
  231. else if (dt<-20){
  232. show=true;
  233. setShowMHeader(show);
  234. }
  235. }
  236. const changeHeaderHeight=()=>{
  237. document.documentElement.style.setProperty('--header-active', getHeaderHeight());
  238. initHeaderHeight(getHeaderHeight());
  239. }
  240. const resizeObserver = new ResizeObserver(entries => {
  241. for (let entry of entries) {
  242. // 获取目标元素的高度
  243. const { height } = entry.contentRect;
  244. // 可在这里处理高度变化的逻辑
  245. changeHeaderHeight();
  246. }
  247. });
  248. useEffect(() => {
  249. // 这里的代码会在组件挂载时执行
  250. // console.log('Component mounted');
  251. window.addEventListener('scroll', handleScroll);
  252. if (barRef.current) {
  253. resizeObserver.observe(barRef.current);
  254. }
  255. return () => {
  256. // 可选的清除函数,当组件卸载时执行
  257. // console.log('Component unmounted');
  258. window.removeEventListener('scroll', handleScroll);
  259. if (barRef.current) {
  260. resizeObserver.unobserve(barRef.current);
  261. }
  262. };
  263. }, [pathname]);
  264. useEffect(()=>{
  265. setShowMHeader(showHeader);
  266. barRef.showHeader=showHeader;
  267. },[showHeader]);
  268. useEffect(()=>{
  269. let len=chain.length;
  270. setShowHeader(len<=1);
  271. },[pathname]);
  272. const headerAni=useSpring({
  273. from: {transform: 'translateY(-100%) translateZ(0px)'},
  274. transform:showMHeader?'translateY(0%) translateZ(0px)':'translateY(-100%) translateZ(0px)',
  275. config: {
  276. easing: easings.easeOutBack,
  277. duration:300
  278. },
  279. });
  280. const changePath=(path)=>{
  281. navigate(path);
  282. }
  283. const renderHeaderRoom=()=>{
  284. const active=styles.active;
  285. return (
  286. <animated.div className={`${styles.header_room}`} style={headerAni}>
  287. <div className={`${styles['mobile-top-navigate-list']}`}>
  288. <button className={`${styles['mb-top-navigate-item']} ${pathname==='/'?active:''}`} onClick={()=>{
  289. changePath('/');
  290. }}>
  291. <svg className={"icon"}>
  292. <use xlinkHref={`${symbol_m}#icon_Home`} />
  293. </svg>
  294. <span>{i18n.t("links.home")}</span>
  295. </button>
  296. {
  297. LINKS.map((link, index) => (
  298. <button key={index} className={`${styles['mb-top-navigate-item']} ${checkNavActive(link.lv)?active:''}`} onClick={()=>{
  299. changePath(link.path);
  300. }}>
  301. <svg className={"icon"}>
  302. <use xlinkHref={`${symbol_m}#${link.icon}`} />
  303. </svg>
  304. <span>{i18n.t(link.title)}</span>
  305. </button>
  306. ))
  307. }
  308. </div>
  309. </animated.div>
  310. );
  311. }
  312. const renderMobile=()=>{
  313. return (
  314. <>
  315. <MobileNav />
  316. {
  317. renderHeaderRoom()
  318. }
  319. </>
  320. )
  321. }
  322. const renderDesktop=()=>{
  323. return (
  324. <DesktopNav/>
  325. );
  326. }
  327. return (
  328. <div id={'appbar'} ref={barRef} className={`${styles.appbar} ${isMobile()?styles.mobile:''} ${isMobile()&&!showHeader?styles.hide_header:''}`}>
  329. {
  330. isMobile()?renderMobile():renderDesktop()
  331. }
  332. </div>
  333. )
  334. }
  335. export default PageHeader