This is an automated email from the ASF dual-hosted git repository.

shuai pushed a commit to branch ui-optimization
in repository https://gitbox.apache.org/repos/asf/answer.git


The following commit(s) were added to refs/heads/ui-optimization by this push:
     new 1131b50d fix: Navigation style optimization
1131b50d is described below

commit 1131b50d0e1fe1813b932cbe1b6597e35302f1da
Author: shuai <[email protected]>
AuthorDate: Thu Apr 10 15:47:16 2025 +0800

    fix: Navigation style optimization
---
 i18n/en_US.yaml                                    |   2 +-
 i18n/zh_CN.yaml                                    |   2 +-
 .../Header/components/NavItems/index.tsx           |   8 +-
 .../Header/components/SearchInput/index.tsx        |  59 +++++
 ui/src/components/Header/index.scss                |  28 +-
 ui/src/components/Header/index.tsx                 | 284 ++++++++-------------
 ui/src/components/MobileSideNav/index.scss         |   4 +-
 7 files changed, 198 insertions(+), 189 deletions(-)

diff --git a/i18n/en_US.yaml b/i18n/en_US.yaml
index 12f46a33..484d6d29 100644
--- a/i18n/en_US.yaml
+++ b/i18n/en_US.yaml
@@ -1474,7 +1474,7 @@ ui:
     signup: Sign up
     logout: Log out
     verify: Verify
-    add_question: Add question
+    create: Create
     approve: Approve
     reject: Reject
     skip: Skip
diff --git a/i18n/zh_CN.yaml b/i18n/zh_CN.yaml
index 20c0a24c..29b78d0c 100644
--- a/i18n/zh_CN.yaml
+++ b/i18n/zh_CN.yaml
@@ -1441,7 +1441,7 @@ ui:
     signup: 注册
     logout: 退出
     verify: 验证
-    add_question: 我要提问
+    create: 创建
     approve: 批准
     reject: 拒绝
     skip: 跳过
diff --git a/ui/src/components/Header/components/NavItems/index.tsx 
b/ui/src/components/Header/components/NavItems/index.tsx
index badd1b79..3dbed4e4 100644
--- a/ui/src/components/Header/components/NavItems/index.tsx
+++ b/ui/src/components/Header/components/NavItems/index.tsx
@@ -54,7 +54,7 @@ const Index: FC<Props> = ({ redDot, userInfo, logOut }) => {
         <NavLink
           to="/users/notifications/inbox"
           title={t('inbox', { keyPrefix: 'notifications' })}
-          className="icon-link nav-link d-flex align-items-center 
justify-content-center p-0 me-3 position-relative">
+          className="icon-link nav-link d-flex align-items-center 
justify-content-center p-0 me-2 position-relative">
           <Icon name="bell-fill" className="fs-4" />
           {(redDot?.inbox || 0) > 0 && (
             <div className="unread-dot bg-danger">
@@ -68,7 +68,7 @@ const Index: FC<Props> = ({ redDot, userInfo, logOut }) => {
         <NavLink
           to="/users/notifications/achievement"
           title={t('achievement', { keyPrefix: 'notifications' })}
-          className="icon-link nav-link d-flex align-items-center 
justify-content-center p-0 me-3 position-relative">
+          className="icon-link nav-link d-flex align-items-center 
justify-content-center p-0 me-2 position-relative">
           <Icon name="trophy-fill" className="fs-4" />
           {(redDot?.achievement || 0) > 0 && (
             <div className="unread-dot bg-danger">
@@ -95,7 +95,7 @@ const Index: FC<Props> = ({ redDot, userInfo, logOut }) => {
           />
         </Dropdown.Toggle>
 
-        <Dropdown.Menu>
+        <Dropdown.Menu className="position-absolute">
           <Dropdown.Item
             href={`${REACT_BASE_PATH}/users/${userInfo.username}`}
             onClick={handleLinkClick}>
@@ -137,7 +137,7 @@ const Index: FC<Props> = ({ redDot, userInfo, logOut }) => {
             </Nav>
           </Dropdown.Toggle>
 
-          <Dropdown.Menu>
+          <Dropdown.Menu className="position-absolute">
             {ucAgent.agent_info.url ? (
               <Dropdown.Item href={ucAgent.agent_info.url}>
                 {ucAgent.agent_info.name}
diff --git a/ui/src/components/Header/components/SearchInput/index.tsx 
b/ui/src/components/Header/components/SearchInput/index.tsx
new file mode 100644
index 00000000..11dacff2
--- /dev/null
+++ b/ui/src/components/Header/components/SearchInput/index.tsx
@@ -0,0 +1,59 @@
+import { FC, useState, useEffect } from 'react';
+import { Form, FormControl } from 'react-bootstrap';
+import { useSearchParams, useNavigate, useLocation } from 'react-router-dom';
+import { useTranslation } from 'react-i18next';
+
+import { Icon } from '@/components';
+
+const SearchInput: FC<{ className?: string }> = ({ className }) => {
+  const { t } = useTranslation('translation', { keyPrefix: 'header' });
+  const navigate = useNavigate();
+  const location = useLocation();
+  const [urlSearch] = useSearchParams();
+  const q = urlSearch.get('q');
+  const [searchStr, setSearch] = useState('');
+  const handleInput = (val) => {
+    setSearch(val);
+  };
+  const handleSearch = (evt) => {
+    evt.preventDefault();
+    if (!searchStr) {
+      return;
+    }
+    const searchUrl = `/search?q=${encodeURIComponent(searchStr)}`;
+    navigate(searchUrl);
+  };
+
+  useEffect(() => {
+    if (q && location.pathname === '/search') {
+      handleInput(q);
+    }
+  }, [q]);
+
+  useEffect(() => {
+    // clear search input when navigate to other page
+    if (location.pathname !== '/search' && searchStr) {
+      setSearch('');
+    }
+  }, [location.pathname]);
+  return (
+    <Form
+      action="/search"
+      className={`w-100 position-relative mx-auto ${className}`}
+      onSubmit={handleSearch}>
+      <div className="search-wrap" onClick={handleSearch}>
+        <Icon name="search" className="search-icon" />
+      </div>
+      <FormControl
+        type="search"
+        placeholder={t('search.placeholder')}
+        className="placeholder-search"
+        value={searchStr}
+        name="q"
+        onChange={(e) => handleInput(e.target.value)}
+      />
+    </Form>
+  );
+};
+
+export default SearchInput;
diff --git a/ui/src/components/Header/index.scss 
b/ui/src/components/Header/index.scss
index b6943eb5..9f2dd799 100644
--- a/ui/src/components/Header/index.scss
+++ b/ui/src/components/Header/index.scss
@@ -21,7 +21,6 @@
 @import 'bootstrap/scss/variables';
 #header {
   z-index: 1041;
-  transform: translate3d(0, 0, 0);
   --bs-navbar-padding-y: 0.75rem;
   background: var(--bs-primary);
   box-shadow:
@@ -31,13 +30,30 @@
     max-height: 2rem;
   }
 
+  .create-icon {
+    color: var(--bs-nav-link-color);
+  }
+
   .nav-link {
     &.icon-link {
-      width: 36px;
-      height: 36px;
+      width: 38px;
+      height: 38px;
     }
   }
 
+  .nav-text {
+    flex: 1;
+    white-space: nowrap;
+    overflow: hidden;
+    text-overflow: ellipsis;
+    padding: 0 10px;
+    min-width: 0;
+  }
+
+  .search-mobile {
+    color: var(--bs-nav-link-color);
+  }
+
   .answer-navBar {
     font-size: 1rem;
     padding: 0.25rem 0.5rem;
@@ -99,15 +115,15 @@
     }
   }
 
-  .maxw-400 {
-    max-width: 400px;
+  .maxw-560 {
+    max-width: 560px;
   }
 
   .search-wrap {
     position: absolute;
     top: 0;
     left: 0;
-    padding: 11px 13px;
+    padding: 10px 13px;
     line-height: 1;
   }
 }
diff --git a/ui/src/components/Header/index.tsx 
b/ui/src/components/Header/index.tsx
index a1928b36..58144808 100644
--- a/ui/src/components/Header/index.tsx
+++ b/ui/src/components/Header/index.tsx
@@ -18,15 +18,9 @@
  */
 
 import { FC, memo, useState, useEffect } from 'react';
-import { Navbar, Nav, Form, FormControl, Col } from 'react-bootstrap';
+import { Navbar, Nav, Button } from 'react-bootstrap';
 import { useTranslation } from 'react-i18next';
-import {
-  useSearchParams,
-  Link,
-  useNavigate,
-  useLocation,
-  useMatch,
-} from 'react-router-dom';
+import { Link, NavLink, useLocation, useMatch } from 'react-router-dom';
 
 import classnames from 'classnames';
 
@@ -43,23 +37,22 @@ import { logout, useQueryNotificationStatus } from 
'@/services';
 import { Icon, MobileSideNav } from '@/components';
 
 import NavItems from './components/NavItems';
+import SearchInput from './components/SearchInput';
 
 import './index.scss';
 
 const Header: FC = () => {
-  const navigate = useNavigate();
   const location = useLocation();
-  const [urlSearch] = useSearchParams();
-  const q = urlSearch.get('q');
   const { user, clear: clearUserStore } = loggedUserInfoStore();
   const { t } = useTranslation();
-  const [searchStr, setSearch] = useState('');
   const siteInfo = siteInfoStore((state) => state.siteInfo);
   const brandingInfo = brandingStore((state) => state.branding);
   const loginSetting = loginSettingStore((state) => state.login);
   const { updateReview } = sideNavStore();
   const { data: redDot } = useQueryNotificationStatus();
   const [showMobileSideNav, setShowMobileSideNav] = useState(false);
+
+  const [showMobileSearchInput, setShowMobileSearchInput] = useState(false);
   /**
    * Automatically append `tag` information when creating a question
    */
@@ -76,18 +69,6 @@ const Header: FC = () => {
     });
   }, [redDot]);
 
-  const handleInput = (val) => {
-    setSearch(val);
-  };
-  const handleSearch = (evt) => {
-    evt.preventDefault();
-    if (!searchStr) {
-      return;
-    }
-    const searchUrl = `/search?q=${encodeURIComponent(searchStr)}`;
-    navigate(searchUrl);
-  };
-
   const handleLogout = async (evt) => {
     evt.preventDefault();
     await logout();
@@ -96,16 +77,7 @@ const Header: FC = () => {
   };
 
   useEffect(() => {
-    if (q && location.pathname === '/search') {
-      handleInput(q);
-    }
-  }, [q]);
-
-  useEffect(() => {
-    // clear search input when navigate to other page
-    if (location.pathname !== '/search' && searchStr) {
-      setSearch('');
-    }
+    setShowMobileSearchInput(false);
     setShowMobileSideNav(false);
   }, [location.pathname]);
 
@@ -119,6 +91,7 @@ const Header: FC = () => {
     const handleResize = () => {
       if (window.innerWidth >= 1199.9) {
         setShowMobileSideNav(false);
+        setShowMobileSearchInput(false);
       }
     };
 
@@ -129,151 +102,112 @@ const Header: FC = () => {
   }, []);
 
   return (
-    <>
-      <Navbar
-        variant={navbarStyle === 'theme-colored' ? 'dark' : ''}
-        expand="xl"
-        className={classnames('sticky-top', navbarStyle)}
-        id="header">
-        <div className="w-100 d-flex align-items-center px-3">
-          <Navbar.Toggle
-            className="answer-navBar me-2"
+    <Navbar
+      variant={navbarStyle === 'theme-colored' ? 'dark' : ''}
+      expand="xl"
+      className={classnames('sticky-top', navbarStyle)}
+      id="header">
+      <div className="w-100 d-flex align-items-center px-3">
+        <Navbar.Toggle
+          className="answer-navBar me-2"
+          onClick={() => {
+            setShowMobileSideNav(!showMobileSideNav);
+            setShowMobileSearchInput(false);
+          }}
+        />
+
+        <Navbar.Brand
+          to="/"
+          as={Link}
+          className="lh-1 me-0 me-sm-5 p-0 nav-text">
+          {brandingInfo.logo ? (
+            <>
+              <img
+                className="d-none d-xl-block logo me-0"
+                src={brandingInfo.logo}
+                alt={siteInfo.name}
+              />
+
+              <img
+                className="xl-none logo me-0"
+                src={brandingInfo.mobile_logo || brandingInfo.logo}
+                alt={siteInfo.name}
+              />
+            </>
+          ) : (
+            <span>{siteInfo.name}</span>
+          )}
+        </Navbar.Brand>
+
+        <SearchInput className="d-none d-lg-block maxw-560" />
+
+        <Nav className="d-block d-lg-none me-2">
+          <Button
+            variant="link"
             onClick={() => {
-              setShowMobileSideNav(!showMobileSideNav);
+              setShowMobileSideNav(false);
+              setShowMobileSearchInput(!showMobileSearchInput);
             }}
-          />
-
-          <div className="d-flex justify-content-between align-items-center 
nav-grow flex-nowrap">
-            <Navbar.Brand to="/" as={Link} className="lh-1 me-0 me-sm-5 p-0">
-              {brandingInfo.logo ? (
-                <>
-                  <img
-                    className="d-none d-xl-block logo me-0"
-                    src={brandingInfo.logo}
-                    alt={siteInfo.name}
-                  />
-
-                  <img
-                    className="xl-none logo me-0"
-                    src={brandingInfo.mobile_logo || brandingInfo.logo}
-                    alt={siteInfo.name}
-                  />
-                </>
-              ) : (
-                <span>{siteInfo.name}</span>
-              )}
-            </Navbar.Brand>
-
-            {/* mobile nav */}
-            <div className="d-flex xl-none align-items-center flex-lg-nowrap">
-              {user?.username ? (
-                <NavItems
-                  redDot={redDot}
-                  userInfo={user}
-                  logOut={(e) => handleLogout(e)}
-                />
-              ) : (
-                <>
-                  <Link
-                    className={classnames('me-2 btn btn-link', {
-                      'link-light': navbarStyle === 'theme-colored',
-                      'link-primary': navbarStyle !== 'theme-colored',
-                    })}
-                    onClick={() => floppyNavigation.storageLoginRedirect()}
-                    to={userCenter.getLoginUrl()}>
-                    {t('btns.login')}
-                  </Link>
-                  {loginSetting.allow_new_registrations && (
-                    <Link
-                      className={classnames(
-                        'btn',
-                        navbarStyle === 'theme-colored'
-                          ? 'btn-light'
-                          : 'btn-primary',
-                      )}
-                      to={userCenter.getSignUpUrl()}>
-                      {t('btns.signup')}
-                    </Link>
-                  )}
-                </>
-              )}
-            </div>
-          </div>
-
-          <div className="d-none d-xl-flex flex-grow-1 me-auto">
-            <Col lg={8} className="d-none d-xl-block ps-0">
-              <Form
-                action="/search"
-                className="w-100 maxw-400 position-relative"
-                onSubmit={handleSearch}>
-                <div className="search-wrap" onClick={handleSearch}>
-                  <Icon name="search" className="search-icon" />
-                </div>
-                <FormControl
-                  type="search"
-                  placeholder="sddfsdf"
-                  className="placeholder-search"
-                  value={searchStr}
-                  name="q"
-                  onChange={(e) => handleInput(e.target.value)}
-                />
-              </Form>
-            </Col>
-
-            {/* pc nav */}
-            <Col
-              lg={4}
-              className="d-none d-xl-flex justify-content-start 
justify-content-sm-end">
-              {user?.username ? (
-                <Nav className="d-flex align-items-center flex-lg-nowrap">
-                  <Nav.Item className="me-3">
-                    <Link
-                      to={askUrl}
-                      className={classnames('text-capitalize text-nowrap btn', 
{
-                        'btn-light': navbarStyle !== 'theme-light',
-                        'btn-primary': navbarStyle === 'theme-light',
-                      })}>
-                      {t('btns.add_question')}
-                    </Link>
-                  </Nav.Item>
-
-                  <NavItems
-                    redDot={redDot}
-                    userInfo={user}
-                    logOut={handleLogout}
-                  />
-                </Nav>
-              ) : (
-                <>
-                  <Link
-                    className={classnames('me-2 btn btn-link', {
-                      'link-light': navbarStyle === 'theme-colored',
-                      'link-primary': navbarStyle !== 'theme-colored',
-                    })}
-                    onClick={() => floppyNavigation.storageLoginRedirect()}
-                    to={userCenter.getLoginUrl()}>
-                    {t('btns.login')}
-                  </Link>
-                  {loginSetting.allow_new_registrations && (
-                    <Link
-                      className={classnames(
-                        'btn',
-                        navbarStyle === 'theme-colored'
-                          ? 'btn-light'
-                          : 'btn-primary',
-                      )}
-                      to={userCenter.getSignUpUrl()}>
-                      {t('btns.signup')}
-                    </Link>
-                  )}
-                </>
-              )}
-            </Col>
-          </div>
+            className="p-0 btn-no-border icon-link nav-link d-flex 
align-items-center justify-content-center">
+            <Icon name="search" className="lh-1 fs-4" />
+          </Button>
+        </Nav>
+
+        {/* pc nav */}
+        {user?.username ? (
+          <Nav className="d-flex align-items-center flex-nowrap flex-row 
ms-auto">
+            <Nav.Item className="me-2 d-block d-xl-none">
+              <NavLink
+                to={askUrl}
+                className="d-block icon-link nav-link text-center">
+                <Icon name="plus-lg" className="lh-1 fs-4" />
+              </NavLink>
+            </Nav.Item>
+
+            <Nav.Item className="me-2 d-none d-xl-block">
+              <NavLink
+                to={askUrl}
+                className="nav-link d-flex align-items-center text-capitalize 
text-nowrap">
+                <Icon name="plus-lg" className="me-2 lh-1 fs-4" />
+                <span>{t('btns.create')}</span>
+              </NavLink>
+            </Nav.Item>
+
+            <NavItems redDot={redDot} userInfo={user} logOut={handleLogout} />
+          </Nav>
+        ) : (
+          <>
+            <Link
+              className={classnames('me-2 btn btn-link', {
+                'link-light': navbarStyle === 'theme-colored',
+                'link-primary': navbarStyle !== 'theme-colored',
+              })}
+              onClick={() => floppyNavigation.storageLoginRedirect()}
+              to={userCenter.getLoginUrl()}>
+              {t('btns.login')}
+            </Link>
+            {loginSetting.allow_new_registrations && (
+              <Link
+                className={classnames(
+                  'btn',
+                  navbarStyle === 'theme-colored' ? 'btn-light' : 
'btn-primary',
+                )}
+                to={userCenter.getSignUpUrl()}>
+                {t('btns.signup')}
+              </Link>
+            )}
+          </>
+        )}
+      </div>
+
+      {showMobileSearchInput && (
+        <div className="w-100 px-3 mt-2 d-block d-lg-none">
+          <SearchInput />
         </div>
-      </Navbar>
+      )}
+
       <MobileSideNav show={showMobileSideNav} onHide={setShowMobileSideNav} />
-    </>
+    </Navbar>
   );
 };
 
diff --git a/ui/src/components/MobileSideNav/index.scss 
b/ui/src/components/MobileSideNav/index.scss
index fbb7c945..983e6825 100644
--- a/ui/src/components/MobileSideNav/index.scss
+++ b/ui/src/components/MobileSideNav/index.scss
@@ -1,7 +1,7 @@
 #mobileSideNav {
-  top: 60px !important;
+  top: 62px !important;
   width: 240px !important;
-  height: calc(100vh - 60px) !important;
+  height: calc(100vh - 62px) !important;
   overflow-y: auto;
   flex: none;
 }

Reply via email to