diff --git a/app/assets/images/metadata.png b/app/assets/images/metadata.png new file mode 100644 index 00000000..5388c90a Binary files /dev/null and b/app/assets/images/metadata.png differ diff --git a/app/assets/stylesheets/mapcard.scss.erb b/app/assets/stylesheets/mapcard.scss.erb index 57fc315a..8f30b00d 100644 --- a/app/assets/stylesheets/mapcard.scss.erb +++ b/app/assets/stylesheets/mapcard.scss.erb @@ -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; + } } } diff --git a/app/assets/stylesheets/mobile.scss.erb b/app/assets/stylesheets/mobile.scss.erb index a646fea4..9ced854c 100644 --- a/app/assets/stylesheets/mobile.scss.erb +++ b/app/assets/stylesheets/mobile.scss.erb @@ -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 { diff --git a/frontend/src/components/Maps/MapCard.js b/frontend/src/components/Maps/MapCard.js index efa3d59f..085784d7 100644 --- a/frontend/src/components/Maps/MapCard.js +++ b/frontend/src/components/Maps/MapCard.js @@ -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
-
+
} @@ -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 (
+
+
+ { map.get('topic_count') }
+ { map.get('topic_count') === 1 ? 'topic' : 'topics' } +
+
+
+ { map.get('star_count') }
+ { map.get('star_count') === 1 ? 'star' : 'stars' } +
+
+
+ { map.get('synapse_count') }
+ { map.get('synapse_count') === 1 ? 'synapse' : 'synapses' } +
+
+
+ { map.get('contributor_count') }
+ { map.get('contributor_count') === 1 ? 'contributor' : 'contributors' } +
+
+
) +} + +const checkAndWrapInA = (shouldWrap, classString, mapId, element) => { + if (shouldWrap) return { element } + 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 (
-
-
-
-
- -
-
-
{ truncatedName }
-
-
- - { map.get('user_name') } - { !map.authorizeToEdit(currentUser) &&
View Only
} -
-
- -
- { map.get('contributor_count') }
- { map.get('contributor_count') === 1 ? 'contributor' : 'contributors' } -
-
- { map.get('topic_count') }
- { map.get('topic_count') === 1 ? 'topic' : 'topics' } -
-
- { map.get('star_count') }
- { map.get('star_count') === 1 ? 'star' : 'stars' } -
-
- { map.get('synapse_count') }
- { map.get('synapse_count') === 1 ? 'synapse' : 'synapses' } -
-
-
-
- { truncatedDesc } -
-
-
-
- { hasMapper &&
} - { hasConversation &&
} - { currentUser && } -
-
+ { checkAndWrapInA(mobile, '', map.id, +
+
+
+ { !mobile &&
+ +
} +
+
{ truncatedName }
+
+ { mobile && hasMapper &&
} + { mobile && hasConversation &&
} + { mobile && d &&
{ d }
} + { mobile &&
} +
+ + { map.get('user_name') } + { !map.authorizeToEdit(currentUser) &&
View Only
} +
+
+ { !mobile && checkAndWrapInA(true, 'mapMetadata', map.id, +
+ +
+
+ { truncatedDesc } +
+
+
+
) } + { !mobile && hasMapper &&
} + { !mobile && hasConversation &&
} + { !mobile && currentUser && } +
+
) }
) } @@ -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, diff --git a/frontend/src/components/Maps/index.js b/frontend/src/components/Maps/index.js index fee6c332..421cf056 100644 --- a/frontend/src/components/Maps/index.js +++ b/frontend/src/components/Maps/index.js @@ -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 (
@@ -50,7 +55,7 @@ class Maps extends Component {
{ user ? : null } { currentUser && !user && !(pending && maps.length === 0) ?
Create new map...
: null } - { maps.models.map(map => ) } + { maps.models.map(map => ) }