make mapsWidth work and add mobile

This commit is contained in:
Connor Turland 2017-03-12 00:56:59 -05:00
parent 9013d49ca1
commit 032d0d8f4b
17 changed files with 156 additions and 147 deletions

View file

@ -169,9 +169,6 @@
.upperRightMapButtons {
right: 134px;
}
.mapPage .upperRightMapButtons, .topicPage .upperRightMapButtons {
top: 0;
}
.upperRightIcon {
width: 32px;
@ -180,13 +177,7 @@
background-repeat: no-repeat;
cursor: pointer;
}
.mapPage .mapElement .importDialog {
display: none;
background-position: 0 0;
}
.mapPage.canEditMap .mapElement .importDialog {
display: block;
}
.sidebarFilterIcon {
background-position: -32px 0;
}
@ -347,7 +338,7 @@
/* infoAndHelp */
.mapPage .openCheatsheet .tooltipsAbove, .topicPage .openCheatsheet .tooltipsAbove {
.openCheatsheet .tooltipsAbove {
right: 1px;
left: auto;
}
@ -399,9 +390,6 @@
background-position: 0 0;
}
.unauthenticated .mapPage .starMap {
display: none;
}
/* end infoAndHelp */
@ -560,10 +548,6 @@
left: -8px;
}
.openCheatsheet .tooltipsAbove {
left: -4px;
}
.sidebarAccountIcon .tooltipsUnder {
margin-left: -12px;
margin-top: 40px;

View file

@ -35,5 +35,6 @@
Metamaps.ServerData.mapIsStarred = <%= @map && current_user.starred_map?(@map) ? true : false %>
</script>
<% end %>
<div class="hidden"><%= render :partial => 'shared/filterBox' %></div>
<div id="loading"></div>
<%= render :partial => 'layouts/foot' %>

View file

@ -2,27 +2,27 @@
# @file
# this code generates the list of icons in the filter box in the upper right menu area
#%>
<%
<%
@mappers = []
@synapses = []
@metacodes = []
@metacodelist = ''
@mapperlist = ''
@synapselist = ''
# There are essentially three functions happening here one to fill data to
#@mappers with all people who have mapped on the selected map, which
#actually gets checked twice once for topics or within @metacodes and once
#for synapses on the map. @synapses get filled with all synapses on the map
# There are essentially three functions happening here one to fill data to
#@mappers with all people who have mapped on the selected map, which
#actually gets checked twice once for topics or within @metacodes and once
#for synapses on the map. @synapses get filled with all synapses on the map
#and metacodes is filled with all the metacodes that are being used on the map.
if @map
@alltopics.each_with_index do |topic, index|
@alltopics.each_with_index do |topic, index|
if @metacodes.index(topic.metacode) == nil
@metacodes.push(topic.metacode)
end
end
@allsynapses.each_with_index do |synapse, index|
end
@allsynapses.each_with_index do |synapse, index|
if @synapses.index{|s| s.desc == synapse.desc} == nil
@synapses.push(synapse)
end
@ -32,16 +32,16 @@
@mappers.push(mapping.user)
end
end
elsif @topic
@alltopics.each_with_index do |topic, index|
elsif @topic
@alltopics.each_with_index do |topic, index|
if @metacodes.index(topic.metacode) == nil
@metacodes.push(topic.metacode)
end
if @mappers.index(topic.user) == nil
@mappers.push(topic.user)
end
end
@allsynapses.each_with_index do |synapse, index|
end
@allsynapses.each_with_index do |synapse, index|
if @synapses.index{|s| s.desc == synapse.desc} == nil
@synapses.push(synapse)
end
@ -52,7 +52,7 @@
end
if @map || @topic
@metacodes.sort! {|x,y|
@metacodes.sort! {|x,y|
n1 = x.name || ""
n2 = y.name || ""
n1 <=> n2
@ -62,24 +62,24 @@
d2 = y.desc || ""
d1 <=> d2
}
@mappers.sort! {|x,y|
@mappers.sort! {|x,y|
n1 = x.name || ""
n2 = y.name || ""
n1 <=> n2
n1 <=> n2
}
@metacodes.each_with_index do |metacode, index|
@metacodes.each_with_index do |metacode, index|
@metacodelist += '<li data-id="' + metacode.id.to_s + '">'
@metacodelist += '<img src="' + asset_path(metacode.icon) + '" data-id="' + metacode.id.to_s + '" alt="' + metacode.name + '" />'
@metacodelist += '<p>' + metacode.name.downcase + '</p></li>'
end
@synapses.each_with_index do |synapse, index|
@synapses.each_with_index do |synapse, index|
d = synapse.desc || ""
@synapselist += '<li data-id="' + d + '">'
@synapselist += '<img src="' + asset_path('synapse16.png') + '" alt="synapse icon" /><p>' + d
@synapselist += '</p></li>'
end
@mappers.each_with_index do |mapper, index|
end
@mappers.each_with_index do |mapper, index|
@mapperlist += '<li data-id="' + mapper.id.to_s + '">'
@mapperlist += '<img src="' + mapper.image.url(:sixtyfour) + '" data-id="' + mapper.id.to_s + '" alt="' + mapper.name + '" />'
@mapperlist += '<p>' + mapper.name + '</p></li>'
@ -95,7 +95,7 @@
<div class="clearfloat"></div>
<ul>
<%= @mapperlist.html_safe %>
</ul>
</ul>
<div class="clearfloat"></div>
</div>
@ -106,7 +106,7 @@
<div class="clearfloat"></div>
<ul>
<%= @metacodelist.html_safe %>
</ul>
</ul>
<div class="clearfloat"></div>
</div>
@ -117,9 +117,8 @@
<div class="clearfloat"></div>
<ul>
<%= @synapselist.html_safe %>
</ul>
</ul>
<div class="clearfloat"></div>
</div>
</div> <!-- end .filterBox -->

View file

@ -26,8 +26,6 @@ const Filter = {
init: function() {
var self = Filter
$('.sidebarFilterIcon').click(self.toggleBox)
$('.sidebarFilterBox .showAllMetacodes').click(self.filterNoMetacodes)
$('.sidebarFilterBox .showAllSynapses').click(self.filterNoSynapses)
$('.sidebarFilterBox .showAllMappers').click(self.filterNoMappers)

View file

@ -19,7 +19,7 @@ const ImportDialog = {
self.closeLightbox = closeLightbox
$('#lightbox_content').append($(outdent`
<div class="lightboxContent" id="import-dialog-lightbox">
<div class="lightboxContent" id="import-dialog">
<div class="importDialogWrapper" />
</div>
`))

View file

@ -3,30 +3,42 @@
import React from 'react'
import ReactDOM from 'react-dom'
import { Router, browserHistory } from 'react-router'
import { merge } from 'lodash'
import { notifyUser } from './index.js'
import ImportDialog from './ImportDialog'
import Active from '../Active'
import DataModel from '../DataModel'
import { ExploreMaps, ChatView, TopicCard } from '../Views'
import Filter from '../Filter'
import JIT from '../JIT'
import Realtime from '../Realtime'
import Map, { InfoBox } from '../Map'
import Topic from '../Topic'
import Visualize from '../Visualize'
import makeRoutes from '../../components/makeRoutes'
let routes
// 220 wide + 16 padding on both sides
const MAP_WIDTH = 252
const MOBILE_VIEW_BREAKPOINT = 504
const MOBILE_VIEW_PADDING = 40
const MAX_COLUMNS = 4
const ReactApp = {
mapId: null,
unreadNotificationsCount: 0,
mapIsStarred: false,
init: function(serverData) {
mapsWidth: 0,
mobile: false,
init: function(serverData, openLightbox) {
const self = ReactApp
self.unreadNotificationsCount = serverData.unreadNotificationsCount
self.mapIsStarred = serverData.mapIsStarred
self.openLightbox = openLightbox
routes = makeRoutes()
self.resize()
self.setMobile()
self.render()
window && window.addEventListener('resize', self.resize)
},
handleUpdate: function(location) {
const self = ReactApp
@ -59,7 +71,8 @@ const ReactApp = {
const self = ReactApp
return merge({
unreadNotificationsCount: self.unreadNotificationsCount,
currentUser: Active.Mapper
currentUser: Active.Mapper,
mobile: self.mobile
},
self.getMapProps(),
self.getTopicProps(),
@ -72,13 +85,21 @@ const ReactApp = {
return {
mapId: self.mapId,
map: Active.Map,
mapIsStarred: self.mapIsStarred,
mapIsStarred: Map.mapIsStarred,
endActiveMap: Map.end,
launchNewMap: Map.launch,
toggleMapInfoBox: InfoBox.toggleBox,
infoBoxHtml: InfoBox.html,
toggleFilterBox: Filter.toggleBox,
infoBoxHtml: InfoBox.html
// filters
filterBoxHtml: $('.filterBox')[0].outerHTML,
openImportLightbox: () => ImportDialog.show(),
forkMap: Map.fork,
openHelpLightbox: () => self.openLightbox('cheatsheet'),
onMapStar: Map.star,
onMapUnstar: Map.unstar,
onZoomExtents: event => JIT.zoomExtents(event, Visualize.mGraph.canvas),
onZoomIn: JIT.zoomIn,
onZoomOut: JIT.zoomOut
}
},
getTopicCardProps: function() {
@ -109,7 +130,8 @@ const ReactApp = {
pending: ExploreMaps.pending,
onStar: ExploreMaps.onStar,
onRequest: ExploreMaps.onRequest,
onMapFollow: ExploreMaps.onMapFollow
onMapFollow: ExploreMaps.onMapFollow,
mapsWidth: ReactApp.mapsWidth
}
},
getChatProps: function() {
@ -132,6 +154,24 @@ const ReactApp = {
inputFocus: ChatView.inputFocus,
handleInputMessage: ChatView.handleInputMessage
}
},
setMobile: function() {
const self = ReactApp
self.mobile = document && document.body.clientWidth <= MOBILE_VIEW_BREAKPOINT
self.render()
},
resize: function() {
const self = ReactApp
const maps = ExploreMaps.collection
const currentUser = Active.Mapper
const user = maps && maps.id === 'mapper' ? ExploreMaps.mapper : null
const numCards = (maps ? maps.length : 0) + (user || currentUser ? 1 : 0)
const mapSpaces = Math.floor(document.body.clientWidth / 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
self.mapsWidth = mapsWidth
self.render()
}
}

View file

@ -18,7 +18,7 @@ const GlobalUI = {
init: function(serverData) {
const self = GlobalUI
self.ReactApp.init(serverData)
self.ReactApp.init(serverData, self.openLightbox)
self.Search.init(serverData)
self.CreateMap.init(serverData)
self.Account.init(serverData)

View file

@ -50,15 +50,6 @@ const JIT = {
*/
init: function(serverData) {
const self = JIT
$('.zoomIn').click(self.zoomIn)
$('.zoomOut').click(self.zoomOut)
const zoomExtents = function(event) {
self.zoomExtents(event, Visualize.mGraph.canvas)
}
$('.zoomExtents').click(zoomExtents)
self.topicDescImage = new Image()
self.topicDescImage.src = serverData['topic_description_signifier.png']

View file

@ -9,7 +9,7 @@ import Create from '../Create'
import DataModel from '../DataModel'
import DataModelMap from '../DataModel/Map'
import Filter from '../Filter'
import GlobalUI from '../GlobalUI'
import GlobalUI, { ReactApp } from '../GlobalUI'
import JIT from '../JIT'
import Loading from '../Loading'
import Realtime from '../Realtime'
@ -25,26 +25,18 @@ const Map = {
events: {
editedByActiveMapper: 'Metamaps:Map:events:editedByActiveMapper'
},
mapIsStarred: false,
init: function(serverData) {
var self = Map
self.mapIsStarred = serverData.mapIsStarred
$('#wrapper').mousedown(function(e) {
if (e.button === 1) return false
})
$('.starMap').click(function() {
if ($(this).is('.starred')) self.unstar()
else self.star()
})
$('.sidebarFork').click(function() {
self.fork()
})
GlobalUI.CreateMap.emptyForkMapForm = $('#fork_map').html()
self.updateStar()
InfoBox.init(serverData, function updateThumbnail() {
self.uploadMapScreenshot()
})
@ -102,8 +94,6 @@ const Map = {
$('.wrapper').addClass('commonsMap')
}
Map.updateStar()
// set filter mapper H3 text
$('#filter_by_mapper h3').html('MAPPERS')
@ -153,17 +143,6 @@ const Map = {
$('.viewOnly').removeClass('isViewOnly')
}
},
updateStar: function() {
if (!Active.Mapper || !DataModel.Stars) return
// update the star/unstar icon
if (DataModel.Stars.find(function(s) { return s.user_id === Active.Mapper.id })) {
$('.starMap').addClass('starred')
$('.starMap .tooltipsAbove').html('Unstar')
} else {
$('.starMap').removeClass('starred')
$('.starMap .tooltipsAbove').html('Star')
}
},
star: function() {
var self = Map
@ -172,7 +151,8 @@ const Map = {
DataModel.Stars.push({ user_id: Active.Mapper.id, map_id: Active.Map.id })
DataModel.Maps.Starred.add(Active.Map)
GlobalUI.notifyUser('Map is now starred')
self.updateStar()
self.mapIsStarred = true
ReactApp.render()
},
unstar: function() {
var self = Map
@ -181,7 +161,8 @@ const Map = {
$.post('/maps/' + Active.Map.id + '/unstar')
DataModel.Stars = DataModel.Stars.filter(function(s) { return s.user_id !== Active.Mapper.id })
DataModel.Maps.Starred.remove(Active.Map)
self.updateStar()
self.mapIsStarred = false
ReactApp.render()
},
fork: function() {
GlobalUI.openLightbox('forkmap')

View file

@ -60,6 +60,7 @@ const ExploreMaps = {
},
render: function() {
var self = ExploreMaps
ReactApp.resize()
ReactApp.render()
Loading.hide()
},

View file

@ -23,13 +23,13 @@ class App extends Component {
}
render () {
const { toast, currentUser, unreadNotificationsCount } = this.props
const { children, toast, currentUser, unreadNotificationsCount } = this.props
return <div className="wrapper" id="wrapper">
<UpperLeftUI currentUser={currentUser} />
<UpperRightUI currentUser={currentUser} unreadNotificationsCount={unreadNotificationsCount} />
<Toast message={toast} />
{currentUser && <a className='feedback-icon' target='_blank' href='https://hylo.com/c/metamaps'></a>}
{this.props.children}
{children}
</div>
}
}

View file

@ -2,10 +2,14 @@ import React, { Component, PropTypes } from 'react'
class FilterBox extends Component {
static propTypes = {
isMap: PropTypes.bool,
filterBoxHtml: PropTypes.string
}
render () {
return null
const { filterBoxHtml } = this.props
const html = {__html: filterBoxHtml}
return <div className="sidebarFilterBox upperRightBox" dangerouslySetInnerHTML={html}></div>
}
}

View file

@ -7,23 +7,28 @@ class InfoAndHelp extends Component {
mapIsStarred: PropTypes.bool,
currentUser: PropTypes.object,
map: PropTypes.object,
onHelpClick: PropTypes.func,
onMapStar: PropTypes.func,
onMapUnstar: PropTypes.func,
onInfoClick: PropTypes.func,
infoBoxhtml: PropTypes.string
}
render () {
const { mapIsStarred, map, currentUser, onInfoClick, infoBoxHtml } = this.props
const { mapIsStarred, map, currentUser, onInfoClick, infoBoxHtml, onMapStar, onMapUnstar, onHelpClick } = this.props
const starclassName = mapIsStarred ? 'starred' : ''
const tooltip = mapIsStarred ? 'Star' : 'Unstar'
const tooltip = mapIsStarred ? 'Unstar' : 'Star'
const onStarClick = mapIsStarred ? onMapUnstar : onMapStar
return <div className="infoAndHelp">
<MapInfoBox map={map} currentUser={currentUser} infoBoxHtml={infoBoxHtml} />
<div className={`starMap infoElement mapElement ${starclassName}`}>
<div className={`starMap infoElement mapElement ${starclassName}`} onClick={onStarClick}>
<div className="tooltipsAbove">{tooltip}</div>
</div>
<div className="mapInfoIcon infoElement mapElement" onClick={onInfoClick}>
<div className="tooltipsAbove">Map Info</div>
</div>
<div className="openCheatsheet openLightbox infoElement mapElement" data-open="cheatsheet">
<div className="openCheatsheet infoElement mapElement" onClick={onHelpClick}>
<div className="tooltipsAbove">Help</div>
</div>
<div className="clearfloat"></div>

View file

@ -5,25 +5,31 @@ import FilterBox from './FilterBox'
class MapButtons extends Component {
static propTypes = {
currentUser: PropTypes.object,
filterBoxOpen: PropTypes.bool
canEditMap: PropTypes.bool,
onImportClick: PropTypes.func,
onForkClick: PropTypes.func,
onFilterClick: PropTypes.func,
filterBoxHtml: PropTypes.string
}
render () {
const { currentUser, filterBoxOpen } = this.props
const { currentUser, canEditMap, filterBoxHtml, onFilterClick, onImportClick, onForkClick } = this.props
return <div className="mapElement upperRightEl upperRightMapButtons upperRightUI">
{currentUser && <div className="importDialog upperRightEl upperRightIcon mapElement openLightbox" data-open="import-dialog-lightbox">
{canEditMap && <div className="importDialog upperRightEl upperRightIcon mapElement" onClick={onImportClick}>
<div className="tooltipsUnder">
Import Data
</div>
</div>}
<div className="sidebarFilter upperRightEl">
<div className="sidebarFilterIcon upperRightIcon"><div className="tooltipsUnder">Filter</div></div>
{filterBoxOpen && <div className="sidebarFilterBox upperRightBox">
<FilterBox />
</div>}
<div className="sidebarFilterIcon upperRightIcon" onClick={onFilterClick}>
<div className="tooltipsUnder">Filter</div>
</div>
<FilterBox filterBoxHtml={filterBoxHtml} />
</div>
{currentUser && <div className="sidebarFork upperRightEl">
<div className="sidebarForkIcon upperRightIcon"><div className="tooltipsUnder">Save To New Map</div></div>
<div className="sidebarForkIcon upperRightIcon" onClick={onForkClick}>
<div className="tooltipsUnder">Save To New Map</div>
</div>
</div>}
<div className="clearfloat"></div>
</div>

View file

@ -2,13 +2,23 @@ import React, { Component, PropTypes } from 'react'
class MapControls extends Component {
static propTypes = {
onClickZoomExtents: PropTypes.func,
onClickZoomIn: PropTypes.func,
onClickZoomOut: PropTypes.func
}
render () {
const { onClickZoomExtents, onClickZoomIn, onClickZoomOut } = this.props
return <div className="mapControls mapElement">
<div className="zoomExtents mapControl"><div className="tooltips">Center View</div></div>
<div className="zoomIn mapControl"><div className="tooltips">Zoom In</div></div>
<div className="zoomOut mapControl"><div className="tooltips">Zoom Out</div></div>
<div className="zoomExtents mapControl" onClick={onClickZoomExtents}>
<div className="tooltips">Center View</div>
</div>
<div className="zoomIn mapControl" onClick={onClickZoomIn}>
<div className="tooltips">Zoom In</div>
</div>
<div className="zoomOut mapControl" onClick={onClickZoomOut}>
<div className="tooltips">Zoom Out</div>
</div>
</div>
}
}

View file

@ -14,6 +14,7 @@ class MapView extends Component {
map: PropTypes.object,
mapIsStarred: PropTypes.bool,
toggleFilterBox: PropTypes.func,
filterBoxHtml: PropTypes.string,
toggleMapInfoBox: PropTypes.func,
infoBoxHtml: PropTypes.string,
currentUser: PropTypes.object,
@ -62,7 +63,11 @@ class MapView extends Component {
}
render = () => {
const { map, mapIsStarred, currentUser, onOpen, onClose, toggleMapInfoBox, toggleFilterBox, infoBoxHtml } = this.props
const { map, currentUser, onOpen, onClose,
toggleMapInfoBox, toggleFilterBox, infoBoxHtml, filterBoxHtml,
openImportLightbox, forkMap, openHelpLightbox,
mapIsStarred, onMapStar, onMapUnstar,
onZoomExtents, onZoomIn, onZoomOut } = this.props
const { filterBoxOpen, chatOpen } = this.state
const onChatOpen = () => {
this.setState({chatOpen: true})
@ -72,17 +77,28 @@ class MapView extends Component {
this.setState({chatOpen: false})
onClose()
}
const canEditMap = map && map.authorizeToEdit(currentUser)
// TODO: stop using {...this.props} and make explicit
return <div className="mapWrapper">
<MapButtons currentUser={currentUser} onFilterClick={toggleFilterBox} />
<MapButtons currentUser={currentUser}
onImportClick={openImportLightbox}
onFilterClick={toggleFilterBox}
onForkClick={forkMap}
canEditMap={canEditMap}
filterBoxHtml={filterBoxHtml} />
<DataVis />
<TopicCard {...this.props} />
<MapChat {...this.props} onOpen={onChatOpen} onClose={onChatClose} chatOpen={chatOpen} />
<MapControls />
<MapControls onClickZoomExtents={onZoomExtents}
onClickZoomIn={onZoomIn}
onClickZoomOut={onZoomOut} />
<InfoAndHelp mapIsStarred={mapIsStarred}
currentUser={currentUser}
map={map}
onInfoClick={toggleMapInfoBox}
onMapStar={onMapStar}
onMapUnstar={onMapUnstar}
onHelpClick={openHelpLightbox}
infoBoxHtml={infoBoxHtml} />
</div>
}

View file

@ -4,12 +4,6 @@ import Header from './Header'
import MapperCard from './MapperCard'
import MapCard from './MapCard'
// 220 wide + 16 padding on both sides
const MAP_WIDTH = 252
const MOBILE_VIEW_BREAKPOINT = 504
const MOBILE_VIEW_PADDING = 40
const MAX_COLUMNS = 4
class Maps extends Component {
static propTypes = {
@ -23,37 +17,17 @@ class Maps extends Component {
pending: PropTypes.bool,
onStar: PropTypes.func,
onRequest: PropTypes.func,
onMapFollow: PropTypes.func
onMapFollow: PropTypes.func,
mapsWidth: PropTypes.number,
mobile: PropTypes.bool
}
static contextTypes = {
location: PropTypes.object
}
constructor(props) {
super(props)
this.state = { mapsWidth: 0 }
}
componentDidMount() {
window && window.addEventListener('resize', this.resize)
this.refs.maps && this.refs.maps.addEventListener('scroll', throttle(this.scroll, 500, { leading: true, trailing: false }))
this.resize()
}
componentWillUnmount() {
window && window.removeEventListener('resize', this.resize)
}
resize = () => {
const { maps, user, currentUser } = this.props
if (!maps) return
const numCards = maps.length + (user || currentUser ? 1 : 0)
const mapSpaces = Math.floor(document.body.clientWidth / 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.maps && this.maps.addEventListener('scroll', throttle(this.scroll, 500, { leading: true, trailing: false }))
}
scroll = () => {
@ -65,15 +39,14 @@ class Maps extends Component {
}
render = () => {
const { maps, currentUser, juntoState, pending, section, user, onStar, onRequest, onMapFollow } = this.props
const style = { width: this.state.mapsWidth + 'px' }
const mobile = document && document.body.clientWidth <= MOBILE_VIEW_BREAKPOINT
const { mobile, maps, mapsWidth, currentUser, juntoState, pending, section, user, onStar, onRequest, onMapFollow } = this.props
const style = { width: mapsWidth + 'px' }
if (!maps) return null // do loading here instead
return (
<div>
<div id='exploreMaps' ref='maps'>
<div id='exploreMaps' ref={x => this.maps = x}>
<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 }