diff --git a/src/assets/images/magnifying_glass.svg b/src/assets/images/magnifying_glass.svg new file mode 100755 index 0000000..dba367b --- /dev/null +++ b/src/assets/images/magnifying_glass.svg @@ -0,0 +1,14 @@ + + + + ico-magnifying_glass + Created with Sketch. + + + + + + + + + \ No newline at end of file diff --git a/src/components/TopNav/MobileNav.js b/src/components/TopNav/MobileNav.js index 2583a0c..b889ad4 100644 --- a/src/components/TopNav/MobileNav.js +++ b/src/components/TopNav/MobileNav.js @@ -3,8 +3,10 @@ import PropTypes from 'prop-types' import styles from './MobileNav.module.scss' import IconClose from '../../assets/images/icon-close.svg' import IconMenu from '../../assets/images/icon-menu.svg' +import IconMagnifyingGlass from '../../assets/images/magnifying_glass.svg' +import { config } from 'topcoder-react-utils' -const MobileNav = ({ showLeftMenu, onClickLeftMenu, logo, rightMenu }) => ( +const MobileNav = ({ showLeftMenu, onClickLeftMenu, logo, rightMenu }) => (
)}
+ {showLeftMenu && ( +
+ + { + if (event.key === 'Enter') { + window.location = `${config.URL.BASE}/search/members?q=${ + encodeURIComponent(event.target.value) + }` + } + }} + placeholder='Find members by username or skill' + aria-label='Find members by username or skill' + /> +
+ )} +
) MobileNav.propTypes = { diff --git a/src/components/TopNav/MobileNav.module.scss b/src/components/TopNav/MobileNav.module.scss index 2d08bb4..37359c7 100644 --- a/src/components/TopNav/MobileNav.module.scss +++ b/src/components/TopNav/MobileNav.module.scss @@ -43,3 +43,46 @@ right: 0; } } + +.icon { + @include mobile-only; + left: 18px; + position: absolute; + top: 16px; +} + +.search { + @include mobile-only; + padding: 6px 12px 0; + position: relative; + + input { + background: none; + border: 1px solid $gray-50; + box-shadow: none; + color: $white; + font: 16px "Roboto", Helvetica, Arial, sans-serif !important; + height: 32px; + outline: none; + padding-left: 30px !important; + + &::placeholder { + color: $gray-50; + } + + &:hover { + border: 1px solid $gray-50; + } + + &:focus { + border: 1px solid $blue; + } + } + + input:active, + input:focus, + input:hover { + box-shadow: none; + outline: none; + } +} \ No newline at end of file diff --git a/src/components/TopNav/PrimaryNav.js b/src/components/TopNav/PrimaryNav.js index 5bf8bb5..5bdc5c2 100644 --- a/src/components/TopNav/PrimaryNav.js +++ b/src/components/TopNav/PrimaryNav.js @@ -4,7 +4,11 @@ import cn from 'classnames' import ResizeDetector from 'react-resize-detector' import ChosenArrow from '../ChosenArrow' import IconArrowSmalldown from '../../assets/images/arrow-small-down.svg' +import MagnifyingGlass from '../../assets/images/magnifying_glass.svg' import styles from './PrimaryNav.module.scss' +import { config } from 'topcoder-react-utils' + +const BASE_URL = config.URL.BASE const PrimaryNav = ({ collapsed, @@ -26,93 +30,146 @@ const PrimaryNav = ({ createHandleClickMoreItem, createSetRef, showChosenArrow, - chosenArrowX + chosenArrowX, + searchOpened, + toggleSearchOpen }) => { const filterNotInMore = menu => !(moreMenu || []).find(x => x.id === menu.id) - + const activeTrigger = { + bottom: 50 // The main nav head bottom Y + } return ( -
-
- - {logo} - - {menu.map((level1, i) => ([ - , - /* Level 1 menu item */ +
+
+
- {level1.title} - , - /* Level 2 menu */ - level1.subMenu && ( -
+ {menu.map((level1, i) => ([ + , + /* Level 1 menu item */ + - {level1.subMenu.filter(filterNotInMore).map((level2, i) => ( - - {level2.title} - - ))} - {/* The More menu */} - {level1.id === activeLevel1Id && moreMenu && moreMenu.length > 0 && ( -
-
- -
- {moreMenu.map((menu, i) => ( - - {menu.title} - - ))} + {level2.title} + + ))} + {/* The More menu */} + {level1.id === activeLevel1Id && moreMenu && moreMenu.length > 0 && ( +
+
+ +
+ {moreMenu.map((menu, i) => ( + + {menu.title} + + ))} +
-
- )} + )} +
+ ) + ]))} + +
+
+ + {rightMenu && ( +
+ {rightMenu}
- ) - ]))} - + )} +
toggleSearchOpen(true)} + onBlur={(event) => { + if (event.pageY < activeTrigger.bottom) { + toggleSearchOpen(false) + } + }} + onMouseEnter={(event) => toggleSearchOpen(true)} + onMouseLeave={(event) => { + console.log(`${event.clientX} - ${event.clientY}`) + if (event.pageY < activeTrigger.bottom) { + toggleSearchOpen(false) + } + }} + onTouchStart={(event) => { + if (searchOpened) { + toggleSearchOpen(false) + } else { + toggleSearchOpen(true) + } + }} + > + +
+
- -
- { toggleSearchOpen(false) }} + > + { + if (event.key === 'Enter') { + window.location = `${BASE_URL}/search/members?q=${ + encodeURIComponent(event.target.value) + }` + } + }} + onBlur={() => toggleSearchOpen(false)} + aria-label='Find members by username or skill' + placeholder='Find members by username or skill' /> - {rightMenu && ( -
- {rightMenu} -
- )}
) @@ -138,7 +195,9 @@ PrimaryNav.propTypes = { createHandleClickMoreItem: PropTypes.func, createSetRef: PropTypes.func, showChosenArrow: PropTypes.bool, - chosenArrowX: PropTypes.number + chosenArrowX: PropTypes.number, + searchOpened: PropTypes.bool, + toggleSearchOpen: PropTypes.func } export default PrimaryNav diff --git a/src/components/TopNav/PrimaryNav.module.scss b/src/components/TopNav/PrimaryNav.module.scss index 66dc6cc..da5d3b8 100644 --- a/src/components/TopNav/PrimaryNav.module.scss +++ b/src/components/TopNav/PrimaryNav.module.scss @@ -229,3 +229,84 @@ } } } + +.searchIcon { + cursor: pointer; + display: inline-block; + height: 32px; + margin: 11px 4px 11px 6px; + padding-left: 12px; + position: relative; + border-left: 1px solid $gray-50; + width: 36px; + + svg { + height: 18px; + margin-top: 7px; + width: 18px; + path { + fill: $white; + } + } + + &:global.opened { + path { + fill: $turquoise; + } + + &::after { + content: ''; + display: block; + width: 0; + height: 0; + border-style: solid; + border-width: 6px 5px 0; + border-color: $turquoise transparent transparent transparent; + position: absolute; + left: 60%; + bottom: -3px; + margin-left: -6px; + background-color: transparent; + } + } +} + +.searchField { + background: $black; + height: 0; + margin-top: -1px; + opacity: 0.98; + overflow: hidden; + padding: 0 48px; + position: absolute; + text-align: center; + width: 100%; + transition: all 0.25s ease-in-out; + z-index: 10; + + &:global.opened { + height: 128px; + padding: 48px; + transition: all 0.25s ease-in-out; + } + + &:global.closed { + display: none; + } + + input, + input:active, + input:focus, + input:hover { + border: none; + border-bottom: 1px solid $white; + box-shadow: none; + font-size: 22px; + outline: none; + padding-bottom: 3px; + + &::placeholder { + color: $gray-15; + } + } +} \ No newline at end of file diff --git a/src/components/TopNav/index.js b/src/components/TopNav/index.js index 3552369..25be2f2 100644 --- a/src/components/TopNav/index.js +++ b/src/components/TopNav/index.js @@ -72,6 +72,7 @@ const TopNav = ({ const [activeLevel3Id, setActiveLevel3Id] = useState() const [showLevel3, setShowLevel3] = useState(false) const [forceHideLevel3, setforceHideLevel3] = useState(false) + const [searchOpened, setSearchOpened] = useState(false) const [showChosenArrow, setShowChosenArrow] = useState() const [chosenArrowX, setChosenArrowX] = useState() @@ -213,6 +214,11 @@ const TopNav = ({ const handleCloseMore = () => setOpenMore(false) + const handleSearchPanel = (x) => { + setSearchOpened(x) + cache.refs.searchInputBox.value = '' + } + const createHandleClickMoreItem = menuId => () => { setOpenMore(false) setActiveLevel2Id(menuId) @@ -453,6 +459,8 @@ const TopNav = ({ createSetRef={createSetRef} showChosenArrow={showChosenArrow} chosenArrowX={chosenArrowX} + searchOpened={searchOpened} + toggleSearchOpen={handleSearchPanel} /> {/* Level 3 menu */}