make it responsive (#820)

This commit is contained in:
Connor Turland 2016-10-23 16:12:07 -04:00 committed by GitHub
parent a44edbb17e
commit cda0c21a0b
5 changed files with 190 additions and 89 deletions

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

View file

@ -36,7 +36,7 @@
height: 72px; height: 72px;
background-image: url("<%= asset_data_uri('newmap_sprite.png') %>"); background-image: url("<%= asset_data_uri('newmap_sprite.png') %>");
background-repeat: no-repeat; background-repeat: no-repeat;
background-position: 0 0; background-position: 0 0;
position: absolute; position: absolute;
left: 50%; left: 50%;
margin-left: -36px; margin-left: -36px;
@ -96,7 +96,7 @@
.mapperList { .mapperList {
display: none; display: none;
padding: 8px; padding: 8px;
list-style-type: none; list-style-type: none;
li { li {
@ -117,7 +117,7 @@
padding-left: 10px; padding-left: 10px;
font-size: 14px; font-size: 14px;
} }
} }
} }
} }
.mapHasMapper { .mapHasMapper {
@ -149,7 +149,7 @@
&:hover .circle { &:hover .circle {
background-color: #222; background-color: #222;
} }
} }
.menuItems { .menuItems {
position: absolute; position: absolute;
@ -167,8 +167,8 @@
&:hover { &:hover {
background-color: #DDD; background-color: #DDD;
} }
} }
} }
} }
.mapScreenshot { .mapScreenshot {
@ -253,6 +253,25 @@
float: left; float: left;
font-family: 'din-medium', sans-serif; font-family: 'din-medium', sans-serif;
text-align: center; text-align: center;
div {
background: url('<%= asset_path('metadata.png') %>') no-repeat;
width: 32px;
height: 32px;
margin: 0 auto;
}
.numTopicsIcon {
background-position: 0 -32px;
}
.numStarsIcon {
background-position: 0 0;
}
.numSynapsesIcon {
background-position: -32px -32px;
}
.numContributorsIcon {
background-position: -32px 0;
}
} }
} }

View file

@ -2,16 +2,33 @@
display: none; display: none;
} }
/* Smartphones (portrait and landscape) ----------- */ @media only screen and (max-device-width : 720px) and (min-device-width : 504px) {
@media only screen and (max-device-width : 480px) { .sidebarSearch .tt-hint, .sidebarSearch .sidebarSearchField {
.upperLeftUI, .upperRightUI, .openCheatsheet, .mapInfoIcon, .uv-icon, .chat-box, #exploreMapsHeader { width: 160px !important;
}
}
@media only screen and (max-device-width : 390px) {
.map .mapCard .mobileMetadata {
width: 190px;
}
}
@media only screen and (min-device-width : 390px) {
.map .mapCard .mobileMetadata {
width: 390px;
}
}
/* Smartphones (portrait and landscape) ----------- the minimum space that two map cards can fit side by side */
@media only screen and (max-device-width : 504px) {
.upperLeftUI, .upperRightUI, .openCheatsheet, .mapInfoIcon, .feedback-icon, .chat-box, #exploreMapsHeader {
display: none !important; display: none !important;
} }
#mobile_header { #mobile_header {
display: block; display: block;
} }
.homeWrapper { .homeWrapper {
width: 96%; width: 96%;
padding: 0 2%; padding: 0 2%;
@ -38,11 +55,11 @@
.learnMoreCTA { .learnMoreCTA {
display: none !important; display: none !important;
} }
#yield { #yield {
height: 100%; height: 100%;
} }
.new_session, .new_user, .edit_user, .login, .forgotPassword { .new_session, .new_user, .edit_user, .login, .forgotPassword {
position: relative; position: relative;
top: auto; top: auto;
@ -51,11 +68,11 @@
padding: 16px 10%; padding: 16px 10%;
margin: 50px auto 0 auto; margin: 50px auto 0 auto;
} }
.centerGreyForm input[type="text"], .centerGreyForm input[type="email"], .centerGreyForm input[type="password"] { .centerGreyForm input[type="text"], .centerGreyForm input[type="email"], .centerGreyForm input[type="password"] {
width: 100%; width: 100%;
} }
.wrapper .mapInfoBox { .wrapper .mapInfoBox {
position: fixed; position: fixed;
top: 50px; top: 50px;
@ -64,12 +81,12 @@
width: 100%; width: 100%;
max-width: 360px; max-width: 360px;
} }
#wrapper .requestInvite { #wrapper .requestInvite {
width: 100%; width: 100%;
padding: 0; padding: 0;
} }
#exploreMaps > div { #exploreMaps > div {
margin-top: 70px; margin-top: 70px;
} }
@ -94,35 +111,73 @@
span { span {
vertical-align: middle; vertical-align: middle;
padding: 16px; padding: 16px;
display: inline-block; display: inline-block;
} }
} }
.map.newMap:hover .newMapImage { .map.newMap:hover .newMapImage {
background-position: 0 -40px; background-position: 0 -40px;
} }
}
/* Smartphones (portrait) ----------- */
@media only screen and (max-width : 400px) {
.map { .map {
width: 100%; width: 100%;
margin: 0 0 30px 0; margin: 0 0 30px 0;
height: auto; height: auto;
}
.mapCard {
height: auto;
}
.mapCard .title {
text-align: left;
}
.mapCard .mapScreenshot {
display: none;
}
.mapCard {
height: auto;
padding: 0;
&:hover {
.mainContent {
filter: none;
}
}
.mobileHasMapper, .mobileHasConversation {
.mapperList {
padding: 8px 16px;
list-style-type: none;
li {
&.live {
height: 32px;
padding-left: 32px;
font-size: 16px;
}
img {
width: 24px;
height: 24px;
border-radius: 12px;
display: inline-block;
vertical-align: middle;
}
span {
padding-left: 10px;
font-size: 14px;
}
}
}
}
.mobileHasMapper {
background: url('<%= asset_path('junto.png') %>') no-repeat 12px 0;
}
.mobileHasConversation {
background: url('<%= asset_path('junto.gif') %>') no-repeat 12px 0;
}
.mobileMetadata {
margin: 0 auto;
}
.title {
text-align: left;
display: block;
height: auto;
padding: 16px;
}
.desc {
padding: 0 16px;
}
}
}
} }
#mobile_header { #mobile_header {

View file

@ -24,17 +24,17 @@ class Menu extends Component {
render = () => { render = () => {
const { currentUser, map, onStar, onRequest } = this.props const { currentUser, map, onStar, onRequest } = this.props
const style = { display: this.state.open ? 'block' : 'none' } const style = { display: this.state.open ? 'block' : 'none' }
return <div className='dropdownMenu'> return <div className='dropdownMenu'>
<div className='menuToggle' onClick={ this.toggle }> <div className='menuToggle' onClick={ this.toggle }>
<div className='circle'></div> <div className='circle'></div>
<div className='circle'></div> <div className='circle'></div>
<div className='circle'></div> <div className='circle'></div>
</div> </div>
<ul className='menuItems' style={ style }> <ul className='menuItems' style={ style }>
<li className='star' onClick={ () => { this.toggle() && onStar(map) }}>Star Map</li> <li className='star' onClick={ () => { this.toggle() && onStar(map) }}>Star Map</li>
{ !map.authorizeToEdit(currentUser) && <li className='request' onClick={ () => { this.toggle() && onRequest(map) }}>Request Access</li> } { !map.authorizeToEdit(currentUser) && <li className='request' onClick={ () => { this.toggle() && onRequest(map) }}>Request Access</li> }
</ul> </ul>
</div> </div>
} }
@ -43,16 +43,47 @@ Menu.propTypes = {
currentUser: PropTypes.object.isRequired, currentUser: PropTypes.object.isRequired,
map: PropTypes.object.isRequired, map: PropTypes.object.isRequired,
onStar: PropTypes.func.isRequired, onStar: PropTypes.func.isRequired,
onRequest: PropTypes.func.isRequired onRequest: PropTypes.func.isRequired
} }
const Metadata = (props) => {
const { map } = props
return (<div>
<div className="metadataSection numTopics">
<div className="numTopicsIcon"></div>
{ map.get('topic_count') }<br/>
{ map.get('topic_count') === 1 ? 'topic' : 'topics' }
</div>
<div className="metadataSection numStars">
<div className="numStarsIcon"></div>
{ map.get('star_count') }<br/>
{ map.get('star_count') === 1 ? 'star' : 'stars' }
</div>
<div className="metadataSection numSynapses">
<div className="numSynapsesIcon"></div>
{ map.get('synapse_count') }<br/>
{ map.get('synapse_count') === 1 ? 'synapse' : 'synapses' }
</div>
<div className="metadataSection numContributors">
<div className="numContributorsIcon"></div>
{ map.get('contributor_count') }<br/>
{ map.get('contributor_count') === 1 ? 'contributor' : 'contributors' }
</div>
<div className="clearfloat"></div>
</div>)
}
const checkAndWrapInA = (shouldWrap, classString, mapId, element) => {
if (shouldWrap) return <a className={ classString } href={ `/maps/${mapId}` } data-router="true">{ element }</a>
else return element
}
class MapCard extends Component { class MapCard extends Component {
render = () => { render = () => {
const { map, juntoState, currentUser, onRequest, onStar } = this.props const { map, mobile, juntoState, currentUser, onRequest, onStar } = this.props
const hasMap = juntoState.liveMaps[map.id] const hasMap = juntoState.liveMaps[map.id]
const hasConversation = hasMap && find(values(hasMap), v => v === IN_CONVERSATION) const hasConversation = hasMap && find(values(hasMap), v => v === IN_CONVERSATION)
const hasMapper = hasMap && !hasConversation const hasMapper = hasMap && !hasConversation
const mapperList = hasMap && Object.keys(hasMap).map(id => juntoState.connectedPeople[id]) const mapperList = hasMap && Object.keys(hasMap).map(id => juntoState.connectedPeople[id])
@ -71,51 +102,41 @@ class MapCard extends Component {
return ( return (
<div className="map" id={ map.id }> <div className="map" id={ map.id }>
<div className={ 'permission ' + editPermission }> { checkAndWrapInA(mobile, '', map.id,
<div className='mapCard'> <div className={ 'permission ' + editPermission }>
<div className='mainContent'> <div className='mapCard'>
<div className='mapScreenshot'> <div className='mainContent'>
<img src={ map.get('screenshot_url') } /> { !mobile && <div className='mapScreenshot'>
</div> <img src={ map.get('screenshot_url') } />
<div className='title' title={ map.get('name') }> </div> }
<div className='innerTitle'>{ truncatedName }</div> <div className='title' title={ map.get('name') }>
</div> <div className='innerTitle'>{ truncatedName }</div>
<div className='creatorAndPerm'> </div>
<img className='creatorImage' src={ map.get('user_image') } /> { mobile && hasMapper && <div className='mobileHasMapper'><MapperList mappers={ mapperList } /></div> }
<span className='creatorName'>{ map.get('user_name') }</span> { mobile && hasConversation && <div className='mobileHasConversation'><MapperList mappers={ mapperList } /></div> }
{ !map.authorizeToEdit(currentUser) && <div className='cardViewOnly'>View Only</div> } { mobile && d && <div className="desc">{ d }</div> }
</div> { mobile && <div className='mobileMetadata'><Metadata map={ map } /></div> }
</div> <div className='creatorAndPerm'>
<a className='mapMetadata' href={ '/maps/' + map.id } data-router="true"> <img className='creatorImage' src={ map.get('user_image') } />
<div className="metadataSection numContributors"> <span className='creatorName'>{ map.get('user_name') }</span>
{ map.get('contributor_count') }<br/> { !map.authorizeToEdit(currentUser) && <div className='cardViewOnly'>View Only</div> }
{ map.get('contributor_count') === 1 ? 'contributor' : 'contributors' } </div>
</div> </div>
<div className="metadataSection numTopics"> { !mobile && checkAndWrapInA(true, 'mapMetadata', map.id,
{ map.get('topic_count') }<br/> <div>
{ map.get('topic_count') === 1 ? 'topic' : 'topics' } <Metadata map={ map } />
</div> <div className="scroll">
<div className="metadataSection numStars"> <div className="desc">
{ map.get('star_count') }<br/> { truncatedDesc }
{ map.get('star_count') === 1 ? 'star' : 'stars' } <div className="clearfloat"></div>
</div> </div>
<div className="metadataSection numSynapses"> </div>
{ map.get('synapse_count') }<br/> </div>) }
{ map.get('synapse_count') === 1 ? 'synapse' : 'synapses' } { !mobile && hasMapper && <div className='mapHasMapper'><MapperList mappers={ mapperList } /></div> }
</div> { !mobile && hasConversation && <div className='mapHasConversation'><MapperList mappers={ mapperList } /></div> }
<div className="clearfloat"></div> { !mobile && currentUser && <Menu currentUser={ currentUser } map={ map } onStar= { onStar } onRequest={ onRequest } /> }
<div className="scroll"> </div>
<div className="desc"> </div>) }
{ truncatedDesc }
<div className="clearfloat"></div>
</div>
</div>
</a>
{ hasMapper && <div className='mapHasMapper'><MapperList mappers={ mapperList } /></div> }
{ hasConversation && <div className='mapHasConversation'><MapperList mappers={ mapperList } /></div> }
{ currentUser && <Menu currentUser={ currentUser } map={ map } onStar= { onStar } onRequest={ onRequest } /> }
</div>
</div>
</div> </div>
) )
} }
@ -123,6 +144,7 @@ class MapCard extends Component {
MapCard.propTypes = { MapCard.propTypes = {
map: PropTypes.object.isRequired, map: PropTypes.object.isRequired,
mobile: PropTypes.bool.isRequired,
juntoState: PropTypes.object, juntoState: PropTypes.object,
currentUser: PropTypes.object, currentUser: PropTypes.object,
onStar: PropTypes.func.isRequired, onStar: PropTypes.func.isRequired,

View file

@ -5,6 +5,8 @@ import MapperCard from './MapperCard'
import MapCard from './MapCard' import MapCard from './MapCard'
const MAP_WIDTH = 252 const MAP_WIDTH = 252
const MOBILE_VIEW_BREAKPOINT = 504
const MOBILE_VIEW_PADDING = 20
const MAX_COLUMNS = 4 const MAX_COLUMNS = 4
class Maps extends Component { class Maps extends Component {
@ -28,7 +30,9 @@ class Maps extends Component {
const { maps, user, currentUser } = this.props const { maps, user, currentUser } = this.props
const numCards = maps.length + (user || currentUser ? 1 : 0) const numCards = maps.length + (user || currentUser ? 1 : 0)
const mapSpaces = Math.floor(document.body.clientWidth / MAP_WIDTH) const mapSpaces = Math.floor(document.body.clientWidth / MAP_WIDTH)
const mapsWidth = Math.min(MAX_COLUMNS, Math.min(numCards, mapSpaces)) * MAP_WIDTH const mapsWidth = document.body.clientWidth <= MOBILE_VIEW_BREAKPOINT ?
document.body.clientWidth - MOBILE_VIEW_PADDING :
Math.min(MAX_COLUMNS, Math.min(numCards, mapSpaces)) * MAP_WIDTH
this.setState({ mapsWidth }) this.setState({ mapsWidth })
} }
@ -43,6 +47,7 @@ class Maps extends Component {
render = () => { render = () => {
const { maps, currentUser, juntoState, pending, section, user, moreToLoad, loadMore, onStar, onRequest } = this.props const { maps, currentUser, juntoState, pending, section, user, moreToLoad, loadMore, onStar, onRequest } = this.props
const style = { width: this.state.mapsWidth + 'px' } const style = { width: this.state.mapsWidth + 'px' }
const mobile = document && document.body.clientWidth <= MOBILE_VIEW_BREAKPOINT
return ( return (
<div> <div>
@ -50,7 +55,7 @@ class Maps extends Component {
<div style={ style }> <div style={ style }>
{ user ? <MapperCard user={ user } /> : null } { user ? <MapperCard user={ user } /> : null }
{ currentUser && !user && !(pending && maps.length === 0) ? <div className="map newMap"><a href="/maps/new"><div className="newMapImage"></div><span>Create new map...</span></a></div> : null } { currentUser && !user && !(pending && maps.length === 0) ? <div className="map newMap"><a href="/maps/new"><div className="newMapImage"></div><span>Create new map...</span></a></div> : null }
{ maps.models.map(map => <MapCard key={ map.id } map={ map } juntoState={ juntoState } currentUser={ currentUser } onStar={ onStar } onRequest={ onRequest } />) } { maps.models.map(map => <MapCard key={ map.id } map={ map } mobile={ mobile } juntoState={ juntoState } currentUser={ currentUser } onStar={ onStar } onRequest={ onRequest } />) }
<div className='clearfloat'></div> <div className='clearfloat'></div>
</div> </div>
</div> </div>