diff --git a/components/Dropdown/Dropdown.jsx b/components/Dropdown/Dropdown.jsx index 4ac9c3b39..32f4cdf5c 100644 --- a/components/Dropdown/Dropdown.jsx +++ b/components/Dropdown/Dropdown.jsx @@ -1,12 +1,18 @@ -'use strict' - require('./Dropdown.scss') -const React = require('react') -const Dropdown = { - getInitialState() { - return { isHidden: true } - }, +import React, { Component } from 'react' + +class Dropdown extends Component { + constructor(props) { + super(props) + + this.state = { isHidden: true } + + this.onClickOutside = this.onClickOutside.bind(this) + this.onClick = this.onClick.bind(this) + this.onClickOtherDropdown = this.onClickOtherDropdown.bind(this) + } + onClickOutside(evt) { let currNode = evt.target let isDropdown = false @@ -18,12 +24,16 @@ const Dropdown = { } currNode = currNode.parentNode + + if(!currNode.tagName) + break } while(currNode.tagName) if(!isDropdown) { this.setState({ isHidden: true }) } - }, + } + onClick(evt) { const dropdownClicked = new Event('dropdownClicked') @@ -31,21 +41,25 @@ const Dropdown = { this.setState({ isHidden: !this.state.isHidden }) evt.stopPropagation() - }, + } + onClickOtherDropdown() { this.setState({ isHidden: true }) - }, + } + componentDidMount() { document.removeEventListener('click', this.onClickOutside) document.removeEventListener('dropdownClicked', this.onClickOtherDropdown) document.addEventListener('click', this.onClickOutside) document.addEventListener('dropdownClicked', this.onClickOtherDropdown) - }, + } + componentWillUnmount() { document.removeEventListener('click', this.onClickOutside) document.removeEventListener('dropdownClicked', this.onClickOtherDropdown) - }, + } + render() { const pointerShadow = this.props.pointerShadow const noPointer = this.props.noPointer @@ -70,14 +84,24 @@ const Dropdown = { return (
- { this.props.children[0] } + { + this.props.children.map((child) => { + if(child.props.className.indexOf('dropdown-menu-header') > -1) + return child + }) + }
- { this.props.children[1] } + { + this.props.children.map((child) => { + if(child.props.className.indexOf('dropdown-menu-list') > -1) + return child + }) + }
) } } -module.exports = React.createClass(Dropdown) +export default Dropdown diff --git a/components/Dropdown/Dropdown.scss b/components/Dropdown/Dropdown.scss index f87c97e63..876e386de 100644 --- a/components/Dropdown/Dropdown.scss +++ b/components/Dropdown/Dropdown.scss @@ -1,4 +1,4 @@ -@import "work/work-includes"; +@import "topcoder/tc-includes"; .Dropdown { margin-top: 30px; diff --git a/components/Dropdown/DropdownExamples.jsx b/components/Dropdown/DropdownExamples.jsx index c76fba8d7..e6324037f 100644 --- a/components/Dropdown/DropdownExamples.jsx +++ b/components/Dropdown/DropdownExamples.jsx @@ -1,8 +1,8 @@ -'use strict' - require('./DropdownExamples.scss') -const Dropdown = require('./Dropdown.jsx') -const React = require('react') + +import Dropdown from './Dropdown' +import React from 'react' + const items = [ 'Review', 'Web Arena', @@ -11,7 +11,7 @@ const items = [ const DropdownExamples = { render() { - const dom = ( + return (
@@ -93,8 +93,6 @@ const DropdownExamples = {
) - - return dom } } diff --git a/components/ExampleNav/ExampleNavContainer.coffee b/components/ExampleNav/ExampleNavContainer.coffee index 50925b510..2bc448b8f 100644 --- a/components/ExampleNav/ExampleNavContainer.coffee +++ b/components/ExampleNav/ExampleNavContainer.coffee @@ -9,7 +9,8 @@ navs = 'UserDropdownMenuExamples', 'QuickLinksExample', 'SearchSuggestionsExamples', - 'SearchBarExample' + 'SearchBarExample', + 'NavbarExample' ] FileUploader: [ 'FileUploaderContainerExamples' diff --git a/components/Navbar/Navbar.jsx b/components/Navbar/Navbar.jsx new file mode 100755 index 000000000..4af6b2e52 --- /dev/null +++ b/components/Navbar/Navbar.jsx @@ -0,0 +1,35 @@ +require('./Navbar.scss') + +import MenuBar from '../MenuBar/MenuBar' +import React, { Component } from 'react' +import SearchBar from '../SearchBar/SearchBar' +import QuickLinks from '../QuickLinks/QuickLinks' +import UserDropdownMenu from '../UserDropdownMenu/UserDropdownMenu' + +const primaryNavigationItems = [ + {img: '../components/MenuBar/nav-community.svg', text: 'Community', link: 'javascript:;'}, + {img: '../components/MenuBar/nav-compete.svg', text: 'Compete', link: 'javascript:;', selected: true}, + {img: '../components/MenuBar/nav-learn.svg', text: 'Learn', link: 'javascript:;'} +] + +class Navbar extends Component { + render() { + return ( +
+
+
+
+ +
+ +
+
+
+ +
+
+ ) + } +} + +export default Navbar diff --git a/components/Navbar/Navbar.scss b/components/Navbar/Navbar.scss new file mode 100755 index 000000000..1d3f2564d --- /dev/null +++ b/components/Navbar/Navbar.scss @@ -0,0 +1,156 @@ +@import "topcoder/tc-includes"; + +$navbar-bg: #FAFAFB; +$border-color: #CFCFD2; +$placeholder-bg: #B47DD6; + +/* Breakpoints - update as necessary */ +$mobile: 768px; +$super-wide: 1376px; + +.Navbar { + height: 60px; + background-color: $navbar-bg; + border-bottom: 1px solid $border-color; + padding: 10px 20px; + position: fixed; + top: 0; + left: 0; + width: 100%; + + @media screen and (max-width: $mobile) { + height: 40px; + padding-top: 0; + padding-bottom: 0; + } + + .topcoder-logo { + width: 56px; + height: 24px; + background-color: $placeholder-bg; + margin-right: 40px; + flex: 0 0 auto; + + @media screen and (min-width: $super-wide) { + width: 155px; + } + + @media screen and (max-width: $mobile) { + width: 40px; + margin-right: 15px; + order: 1; + } + } + + .search-bar-wrap { + flex: 1 1 auto; + margin-right: 35px; + + @media screen and (min-width: $super-wide) { + margin-right: 32px; + } + + @media screen and (max-width: $mobile) { + flex: 0 0 60px; + order: 2; + background-color: $accent-gray; + width: 60px; + height: 40px; + display: flex; + margin-right: 10px; + } + + .icon-placeholder { + display: none; + + @media screen and (max-width: $mobile) { + display: block; + height: 25px; + width: 25px; + background-color: $placeholder-bg; + margin: auto; + } + } + + .SearchBar { + margin-right: 45px; + max-width: 789px; + + @media screen and (max-width: $mobile) { + display: none; + } + } + } + + .MenuBar { + margin-right: 30px; + + @media screen and (max-width: $mobile) { + order: 1; + margin-right: 7px; + } + + &.horizontal li { + position: relative; + + a { + @media screen and (max-width: $mobile) { + padding-bottom: 0; + position: absolute; + top: 50%; + transform: translateY(-50%); + } + } + } + } + + .collapse-group { + flex: 0 0 auto; + position: relative; + + @media screen and (max-width :$mobile) { + width: 40px; + height: 40px; + order: 3; + cursor: pointer; + } + + .icon-placeholder { + + @media screen and (max-width: $mobile) { + display: block; + height: 24px; + width: 24px; + background-color: $placeholder-bg; + position: absolute; + top: 50%; + left: 50%; + transform: translate(-50%, -50%); + } + } + + .quick-links-wrap { + margin-right: 30px; + display: inline-block; + vertical-align: middle; + + @media screen and (max-width: $mobile) { + display: none; + } + + .Dropdown { + right: -46px; + top: 10px; + } + } + + .UserDropdownMenu { + flex-shrink: 0; + background-color: transparent; + + @media screen and (max-width: $mobile) { + display: none; + } + } + } +} \ No newline at end of file diff --git a/components/Navbar/NavbarExample.jsx b/components/Navbar/NavbarExample.jsx new file mode 100755 index 000000000..9aba6f991 --- /dev/null +++ b/components/Navbar/NavbarExample.jsx @@ -0,0 +1,8 @@ +import Navbar from './Navbar' +import React from 'react' + +const NavbarExample = () => ( + +) + +module.exports = NavbarExample diff --git a/components/QuickLinks/QuickLinks.jsx b/components/QuickLinks/QuickLinks.jsx index 7c30bc3c5..fcec8056d 100644 --- a/components/QuickLinks/QuickLinks.jsx +++ b/components/QuickLinks/QuickLinks.jsx @@ -1,12 +1,11 @@ -'use strict' - require('./QuickLinks.scss') -const Dropdown = require('../Dropdown/Dropdown.jsx') -const React = require('react') -import StandardListItem from '../StandardListItem/StandardListItem.jsx' +import React, { Component } from 'react' +import Dropdown from '../Dropdown/Dropdown' + +import StandardListItem from '../StandardListItem/StandardListItem' -const QuickLinks = { +class QuickLinks extends Component { render() { return (
@@ -32,4 +31,4 @@ const QuickLinks = { } } -module.exports = React.createClass(QuickLinks) +export default QuickLinks \ No newline at end of file diff --git a/components/QuickLinks/QuickLinks.scss b/components/QuickLinks/QuickLinks.scss index 9c5e62fd5..18238b450 100644 --- a/components/QuickLinks/QuickLinks.scss +++ b/components/QuickLinks/QuickLinks.scss @@ -1,4 +1,4 @@ -@import "work/work-includes"; +@import "topcoder/tc-includes"; $logo-placeholder-bg: #B47DD6; @@ -6,17 +6,23 @@ $logo-placeholder-bg: #B47DD6; position: relative; display: inline-block; + .dropdown-menu-header { + cursor: pointer; + } + .Dropdown { - width: 278px; + width: auto; margin-top: 20px; left: initial; right: -16px; .dropdown-menu-list { padding: 20px; + white-space: nowrap; .dropdown-menu-list-item { display: inline-block; + cursor: pointer; .icon-placeholder { width: 30px; diff --git a/components/QuickLinks/QuickLinksExample.jsx b/components/QuickLinks/QuickLinksExample.jsx index 4706f2c5c..d7ff74f87 100644 --- a/components/QuickLinks/QuickLinksExample.jsx +++ b/components/QuickLinks/QuickLinksExample.jsx @@ -1,17 +1,12 @@ -'use strict' - require('./QuickLinksExample.scss') -const QuickLinks = require('./QuickLinks.jsx') -const React = require('react') -const QuickLinksExample = { - render() { - return ( -
- -
- ) - } -} +import React from'react' +import QuickLinks from './QuickLinks' + +const QuickLinksExample = () => ( +
+ +
+) -module.exports = React.createClass(QuickLinksExample) +module.exports = QuickLinksExample diff --git a/components/QuickLinks/QuickLinksExample.scss b/components/QuickLinks/QuickLinksExample.scss index cf323cbbc..fe33ad19e 100644 --- a/components/QuickLinks/QuickLinksExample.scss +++ b/components/QuickLinks/QuickLinksExample.scss @@ -1,4 +1,4 @@ -@import "work/work-includes"; +@import "topcoder/tc-includes"; .example-wrap { width: 332px; diff --git a/components/Router/Router.cjsx b/components/Router/Router.cjsx index 38d811c9a..77c98f961 100644 --- a/components/Router/Router.cjsx +++ b/components/Router/Router.cjsx @@ -24,7 +24,8 @@ DropdownExamples = require '../Dropdown/DropdownExamples.jsx' UserDropdownMenuExamples = require '../UserDropdownMenu/UserDropdownMenuExamples.jsx' QuickLinksExample = require '../QuickLinks/QuickLinksExample.jsx' SearchSuggestionsExamples = require '../SearchSuggestions/SearchSuggestionsExamples.jsx' -SearchBarExample = require '../SearchBar/SearchBarExamples.jsx' +SearchBarExample = require '../SearchBar/SearchBarExamples.jsx' +NavbarExample = require '../Navbar/NavbarExample.jsx' { Router, Route, Link, IndexRoute, browserHistory } = require 'react-router' @@ -69,6 +70,8 @@ component = -> + + diff --git a/components/SearchBar/SearchBar.jsx b/components/SearchBar/SearchBar.jsx index 889ceb0e7..61d06ddcb 100644 --- a/components/SearchBar/SearchBar.jsx +++ b/components/SearchBar/SearchBar.jsx @@ -1,33 +1,42 @@ -'use strict' - require('./SearchBar.scss') -const React = require('react') -const SearchSuggestions = require('../SearchSuggestions/SearchSuggestions.jsx') + +import React, {Component} from 'react' +import SearchSuggestions from '../SearchSuggestions/SearchSuggestions' //states: empty, filled, focused -const SearchBar = { - getInitialState() { - return { searchState: 'empty' } - }, +class SearchBar extends Component { + constructor(props) { + super(props) + this.state = { searchState: 'empty' } + this.onFocus = this.onFocus.bind(this) + this.onBlur = this.onBlur.bind(this) + this.onKeyUp = this.onKeyUp.bind(this) + this.clearSearch = this.clearSearch.bind(this) + } + onFocus() { this.setState({ searchState: 'focused' }) - }, + } + onBlur() { if(this.state.searchValue) { this.setState({ searchState: 'filled' }) } else { this.setState({ searchState: 'empty' }) } - }, + } + onKeyUp() { this.setState({ searchValue: this.refs.searchValue.value }) - }, + } + clearSearch() { this.refs.searchValue.value = null this.setState({ searchValue: this.refs.searchValue.value }) this.setState({ searchState: 'empty' }) - }, + } + render() { /* Sample JSON data */ const recentList = ['Photoshop', 'IBM Bluemix', 'Sketch', 'iOS Icon Design Challenges', 'React.js'] @@ -35,6 +44,7 @@ const SearchBar = { const searchState = this.state.searchState const searchValue = this.state.searchValue + let classString = 'SearchBar' let typeaheadText = '' let isPartial = false @@ -54,20 +64,20 @@ const SearchBar = { } } else { - popularForDisplay = null - typeaheadText = null + popularForDisplay = '' + typeaheadText = '' } if(searchState === 'empty') { classString += ' state-empty' - typeaheadText = null + typeaheadText = '' } else if(searchState === 'focused') { classString += ' state-focused' } else if(searchState === 'filled') { classString += ' state-filled' } - const dom = ( + return (
{ typeaheadText } @@ -79,8 +89,7 @@ const SearchBar = {
) - return dom } } -module.exports = React.createClass(SearchBar) +export default SearchBar diff --git a/components/SearchBar/SearchBar.scss b/components/SearchBar/SearchBar.scss index 1c77f2788..e38311848 100644 --- a/components/SearchBar/SearchBar.scss +++ b/components/SearchBar/SearchBar.scss @@ -1,5 +1,4 @@ -@import "work/work-includes"; -@import "topcoder/_tc-colors"; +@import "topcoder/tc-includes"; $border-color: #D8D8DB; $active-icon-wrap-bg: #888894; @@ -14,6 +13,68 @@ $active-icon-wrap-bg: #888894; padding-left: 15px; padding-right: 46px; + &.state-empty { + .search-bar__clear { + display: none; + } + } + + &.state-empty:before { + content: "Search Topcoder for challenges, people or content"; + position: absolute; + left: 15px; + top: 50%; + transform: translateY(-50%); + color: $accent-gray; + font-size: 15px; + line-height: 20px; + width: 75%; + white-space: nowrap; + overflow-x: hidden; + text-overflow: ellipsis; + + @media screen and (max-width: 768px) { + content: "Search Topcoder"; + } + } + + &.state-focused:before, + &.state-filled:before { + content: ""; + } + + &.state-focused { + border-color: $accent-gray; + + .search-typeahead-text { + display: block; + } + + .search-icon-wrap { + background-color: $active-icon-wrap-bg; + } + + .SearchSuggestions { + display: block; + } + } + + &.state-filled { + border-color: $border-color; + + .search-typeahead-text { + display: none; + } + + .search-icon-wrap { + background-color: $active-icon-wrap-bg; + } + + .SearchSuggestions { + display: none; + } + } + .search-bar__text, .search-typeahead-text { outline: 0px; @@ -76,62 +137,4 @@ $active-icon-wrap-bg: #888894; font-weight: 600; } } -} - -.SearchBar.state-empty { - .search-bar__clear { - display: none; - } -} - -.SearchBar.state-empty:before { - content: "Search Topcoder for challenges, people or content"; - position: absolute; - left: 15px; - top: 50%; - transform: translateY(-50%); - color: $accent-gray; - font-size: 17px; - line-height: 20px; - - @media screen and (max-width: 768px) { - content: "Search Topcoder"; - } -} - -.SearchBar.state-focused:before, -.SearchBar.state-filled:before { - content: ""; -} - -.SearchBar.state-focused { - border-color: $accent-gray; - - .search-typeahead-text { - display: block; - } - - .search-icon-wrap { - background-color: $active-icon-wrap-bg; - } - - .SearchSuggestions { - display: block; - } -} - -.SearchBar.state-filled { - border-color: $border-color; - - .search-typeahead-text { - display: none; - } - - .search-icon-wrap { - background-color: $active-icon-wrap-bg; - } - - .SearchSuggestions { - display: none; - } } \ No newline at end of file diff --git a/components/SearchBar/SearchBarExamples.jsx b/components/SearchBar/SearchBarExamples.jsx index 19a4b4155..27b6957b0 100644 --- a/components/SearchBar/SearchBarExamples.jsx +++ b/components/SearchBar/SearchBarExamples.jsx @@ -1,12 +1,8 @@ -'use strict' +import SearchBar from './SearchBar' +import React from 'react' -const SearchBar = require('./SearchBar.jsx') -const React = require('react') +const SearchBarExamples = () => ( + +) -const SearchBarExamples = { - render() { - return - } -} - -module.exports = React.createClass(SearchBarExamples) +module.exports = SearchBarExamples diff --git a/components/SearchSuggestions/SearchSuggestions.jsx b/components/SearchSuggestions/SearchSuggestions.jsx index f0ea1e8e3..eb2ee9123 100644 --- a/components/SearchSuggestions/SearchSuggestions.jsx +++ b/components/SearchSuggestions/SearchSuggestions.jsx @@ -1,14 +1,16 @@ -'use strict' - require('./SearchSuggestions.scss') -const React = require('react') -import StandardListItem from '../StandardListItem/StandardListItem.jsx' + +import React, { Component } from 'react' +import StandardListItem from '../StandardListItem/StandardListItem' import Panel from '../Panel/Panel' -const SearchSuggestions = { - getInitialState() { - return { iSEmpty: true } - }, +class SearchSuggestions extends Component { + constructor(props) { + super(props) + + this.state = { iSEmpty: true } + } + render() { const recentList = this.props.recentSearch const popularList = this.props.popularSearch @@ -18,7 +20,7 @@ const SearchSuggestions = {
Recent Search
-
+
@@ -27,14 +29,14 @@ const SearchSuggestions = {
    { - recentList.map((search, i) => { - return
  • + !recentList ? '' : recentList.map((search, i) => { + return
  • }) }
{ - popularList ? null : ( - + popularList ? '' : ( + Learn more about the new Search here ) @@ -54,7 +56,7 @@ const SearchSuggestions = {
    { popularList.map((search, i) => { - return
  • + return
  • }) }
@@ -72,4 +74,4 @@ const SearchSuggestions = { } } -module.exports = React.createClass(SearchSuggestions) \ No newline at end of file +export default SearchSuggestions \ No newline at end of file diff --git a/components/SearchSuggestions/SearchSuggestions.scss b/components/SearchSuggestions/SearchSuggestions.scss index 8f1f076c5..205399bb8 100644 --- a/components/SearchSuggestions/SearchSuggestions.scss +++ b/components/SearchSuggestions/SearchSuggestions.scss @@ -1,4 +1,4 @@ -@import 'topcoder/_tc-colors'; +@import 'topcoder/tc-includes'; .SearchSuggestions { margin-bottom: 30px; diff --git a/components/SearchSuggestions/SearchSuggestionsExamples.jsx b/components/SearchSuggestions/SearchSuggestionsExamples.jsx index 7ffec7415..a9ac097cb 100644 --- a/components/SearchSuggestions/SearchSuggestionsExamples.jsx +++ b/components/SearchSuggestions/SearchSuggestionsExamples.jsx @@ -1,7 +1,6 @@ -'use strict' +import React from 'react' +import SearchSuggestions from './SearchSuggestions' -const React = require('react') -const SearchSuggestions = require('./SearchSuggestions.jsx') const recentList = ['Photoshop', 'IBM Bluemix', 'Sketch', 'iOS Icon Design Challenges', 'React.js'] const popularList = ['Java', 'Javascript', 'CoffeeScript'] diff --git a/components/UserDropdownMenu/UserDropdownMenu.jsx b/components/UserDropdownMenu/UserDropdownMenu.jsx index 3b1d2efe2..933d23824 100644 --- a/components/UserDropdownMenu/UserDropdownMenu.jsx +++ b/components/UserDropdownMenu/UserDropdownMenu.jsx @@ -1,9 +1,8 @@ -'use strict' - require('./UserDropdownMenu.scss') -const React = require('react') -const Dropdown = require('../Dropdown/Dropdown.jsx') +import React, { Component } from 'react' +import Dropdown from '../Dropdown/Dropdown' + const userDropdownLists = [ [ { label: 'My Profile', link: 'javascript:;', id: 0 }, @@ -19,46 +18,45 @@ const userDropdownLists = [ ] ] -const UserDropdownMenu = { - getInitialState () { - return { isLoggedIn: true } - }, - render() { - let dom +class UserDropdownMenu extends Component { + constructor(props) { + super(props) - if (!this.state.isLoggedIn) { - dom =
- } else { - dom = ( -
- -
- - { this.props.username } - -
+ this.state = { isLoggedIn: true } + } + + render() { + const publicDOM =
-
- { - userDropdownLists.map((list, i) => { - return ( ) - }) - } + const loggedInDOM = ( +
+ +
+ + { this.props.username } + +
+ +
+ { + userDropdownLists.map((list, i) => { + return ( ) + }) + } -
-
-
- ) - } +
+
+
+ ) - return dom + return this.state.isLoggedIn ? loggedInDOM : publicDOM } } -module.exports = React.createClass(UserDropdownMenu) \ No newline at end of file +export default UserDropdownMenu \ No newline at end of file diff --git a/components/UserDropdownMenu/UserDropdownMenu.scss b/components/UserDropdownMenu/UserDropdownMenu.scss index eb3bc8e8a..fecdfcdb5 100644 --- a/components/UserDropdownMenu/UserDropdownMenu.scss +++ b/components/UserDropdownMenu/UserDropdownMenu.scss @@ -1,17 +1,23 @@ -@import "work/work-includes"; +@import "topcoder/tc-includes"; + +$user-dropdown-bg: #FAFAFB; +$placeholder-bg: #B47DD6; +$username-color: #737380; .UserDropdownMenu { padding: 15px 20px; - background-color: #FAFAFB; + background-color: $user-dropdown-bg; display: inline-block; position: relative; .dropdown-menu-header { + cursor: pointer; + .user-image { display: inline-block; width: 30px; height: 30px; - background-color: #B47DD6; + background-color: $placeholder-bg; margin-right: 10px; border-radius: 50%; vertical-align: middle; @@ -21,7 +27,7 @@ font-family: "Roboto", Arial, Helvetica, sans-serif; font-size: 14px; line-height: 16px; - color: #737380; + color: $username-color; margin-right: 10px; display: inline-block; @@ -43,8 +49,15 @@ width: 96%; ul { - padding-top: 0; - padding-bottom: 0; + padding: 0; + + .user-menu-item { + padding: 0 20px; + } + + .user-menu-item:hover { + background-color: $gray-light; + } } ul:first-child { @@ -52,8 +65,9 @@ } ul:not(:last-child) { - li:last-child { - border-bottom: 1px solid #F0F0F0; + li:last-child a { + border-bottom: 1px solid $gray-light; + display: block; } } } diff --git a/components/UserDropdownMenu/UserDropdownMenuExamples.jsx b/components/UserDropdownMenu/UserDropdownMenuExamples.jsx index ae6ea8ca8..0fbc7ea5a 100644 --- a/components/UserDropdownMenu/UserDropdownMenuExamples.jsx +++ b/components/UserDropdownMenu/UserDropdownMenuExamples.jsx @@ -1,7 +1,5 @@ -'use strict' - -const React = require('react') -const UserDropdownMenu = require('./UserDropdownMenu.jsx') +import React from 'react' +import UserDropdownMenu from './UserDropdownMenu' const UserDropdownMenuExamples = () => { return