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;
background-image: url("<%= asset_data_uri('newmap_sprite.png') %>");
background-repeat: no-repeat;
background-position: 0 0;
background-position: 0 0;
position: absolute;
left: 50%;
margin-left: -36px;
@ -96,7 +96,7 @@
.mapperList {
display: none;
padding: 8px;
padding: 8px;
list-style-type: none;
li {
@ -117,7 +117,7 @@
padding-left: 10px;
font-size: 14px;
}
}
}
}
}
.mapHasMapper {
@ -149,7 +149,7 @@
&:hover .circle {
background-color: #222;
}
}
}
.menuItems {
position: absolute;
@ -167,8 +167,8 @@
&:hover {
background-color: #DDD;
}
}
}
}
}
}
.mapScreenshot {
@ -253,6 +253,25 @@
float: left;
font-family: 'din-medium', sans-serif;
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;
}
/* Smartphones (portrait and landscape) ----------- */
@media only screen and (max-device-width : 480px) {
.upperLeftUI, .upperRightUI, .openCheatsheet, .mapInfoIcon, .uv-icon, .chat-box, #exploreMapsHeader {
@media only screen and (max-device-width : 720px) and (min-device-width : 504px) {
.sidebarSearch .tt-hint, .sidebarSearch .sidebarSearchField {
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;
}
#mobile_header {
display: block;
}
.homeWrapper {
width: 96%;
padding: 0 2%;
@ -38,11 +55,11 @@
.learnMoreCTA {
display: none !important;
}
#yield {
height: 100%;
}
.new_session, .new_user, .edit_user, .login, .forgotPassword {
position: relative;
top: auto;
@ -51,11 +68,11 @@
padding: 16px 10%;
margin: 50px auto 0 auto;
}
.centerGreyForm input[type="text"], .centerGreyForm input[type="email"], .centerGreyForm input[type="password"] {
width: 100%;
}
.wrapper .mapInfoBox {
position: fixed;
top: 50px;
@ -64,12 +81,12 @@
width: 100%;
max-width: 360px;
}
#wrapper .requestInvite {
width: 100%;
padding: 0;
}
#exploreMaps > div {
margin-top: 70px;
}
@ -94,35 +111,73 @@
span {
vertical-align: middle;
padding: 16px;
display: inline-block;
display: inline-block;
}
}
.map.newMap:hover .newMapImage {
background-position: 0 -40px;
}
}
/* Smartphones (portrait) ----------- */
@media only screen and (max-width : 400px) {
.map {
width: 100%;
margin: 0 0 30px 0;
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 {

View file

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

View file

@ -5,6 +5,8 @@ import MapperCard from './MapperCard'
import MapCard from './MapCard'
const MAP_WIDTH = 252
const MOBILE_VIEW_BREAKPOINT = 504
const MOBILE_VIEW_PADDING = 20
const MAX_COLUMNS = 4
class Maps extends Component {
@ -28,7 +30,9 @@ class Maps extends Component {
const { maps, user, currentUser } = this.props
const numCards = maps.length + (user || currentUser ? 1 : 0)
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 })
}
@ -43,6 +47,7 @@ class Maps extends Component {
render = () => {
const { maps, currentUser, juntoState, pending, section, user, moreToLoad, loadMore, onStar, onRequest } = this.props
const style = { width: this.state.mapsWidth + 'px' }
const mobile = document && document.body.clientWidth <= MOBILE_VIEW_BREAKPOINT
return (
<div>
@ -50,7 +55,7 @@ class Maps extends Component {
<div style={ style }>
{ 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 }
{ 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>
</div>