From ddbee03f507db24b11d7ddd8ed60a96c24af478a Mon Sep 17 00:00:00 2001 From: Lachlan Kermode Date: Thu, 22 Oct 2020 20:09:13 +0300 Subject: [PATCH] Feature/ux fixes (#167) * fix card toggle * fix bug and bar marker * reinstate timeline arrows * adjust (hard to interpret) category y calculation * shadows for markers as well * return markers when there are no categories * remove year in timeline * make notifications optional * WIP: render hovered counts * show number on hover * lint * revert to filteredLocations * linting * return mapClustersToLocations * :lipstick: * lint Co-authored-by: efarooqui --- src/components/Layout.js | 6 +- src/components/Map.jsx | 13 +- src/components/Timeline.jsx | 7 +- src/components/TimelineAxis.jsx | 4 +- .../presentational/Map/Clusters.jsx | 167 ++++++++++-------- .../presentational/Timeline/Handles.js | 38 ++-- .../presentational/Timeline/Markers.js | 51 ++++-- src/scss/timeline.scss | 1 - src/store/initial.js | 1 + 9 files changed, 153 insertions(+), 135 deletions(-) diff --git a/src/components/Layout.js b/src/components/Layout.js index b6ea66f..3581ae8 100644 --- a/src/components/Layout.js +++ b/src/components/Layout.js @@ -288,7 +288,7 @@ class Dashboard extends React.Component { null} onHighlight={this.handleHighlight} onToggleCardstack={() => actions.updateSelected([])} getCategoryColor={this.getCategoryColor} @@ -316,11 +316,11 @@ class Dashboard extends React.Component { onClose: actions.toggleInfoPopup }} /> - + /> : null} { + // console.log(map.getZoom()) this.updateClusters() this.alignLayers() }) @@ -199,11 +196,9 @@ class Map extends React.Component { } } - onClusterSelect (e) { - const { id } = e.target - const { longitude, latitude } = e.target.attributes + onClusterSelect ({ id, latitude, longitude }) { const expansionZoom = Math.max(this.superclusterIndex.getClusterExpansionZoom(parseInt(id)), this.superclusterIndex.options.minZoom) - this.map.flyTo(new L.LatLng(latitude.value, longitude.value), expansionZoom) + this.map.flyTo(new L.LatLng(latitude, longitude), expansionZoom) } getClientDims () { diff --git a/src/components/Timeline.jsx b/src/components/Timeline.jsx index 3e55d27..f01056f 100644 --- a/src/components/Timeline.jsx +++ b/src/components/Timeline.jsx @@ -81,7 +81,7 @@ class Timeline extends React.Component { categories = categories.filter(cat => !features.GRAPH_NONLOCATED.categories.includes(cat.id)) } const catHeight = trackHeight / (categories.length) - const shiftUp = trackHeight / (categories.length) / 2 + const shiftUp = trackHeight / (categories.length) / 3 const marginShift = marginTop === 0 ? 0 : marginTop const manualAdjustment = trackHeight <= 60 ? (trackHeight <= 30 ? -8 : -5) : 0 const catsYpos = categories.map((g, i) => { @@ -138,7 +138,6 @@ class Timeline extends React.Component { * @param {String} direction: 'forward' / 'backwards' */ onMoveTime (direction) { - this.props.methods.onSelect() const extent = this.getTimeScaleExtent() const newCentralTime = d3.timeMinute.offset(this.state.scaleX.domain()[0], extent / 2) @@ -277,9 +276,10 @@ class Timeline extends React.Component { return this.state.dims.trackHeight / 2 } - const { category, project } = event + const { category } = event if (GRAPH_NONLOCATED && GRAPH_NONLOCATED.categories.includes(category)) { + const { project } = event return this.state.dims.marginTop + domain.projects[project].offset + this.props.ui.eventRadius } if (!this.state.scaleY) return 0 @@ -359,6 +359,7 @@ class Timeline extends React.Component { selected={this.props.app.selected} getEventX={ev => this.getDatetimeX(ev.datetime)} getEventY={this.getY} + categories={this.props.domain.categories} transitionDuration={this.state.transitionDuration} styles={this.props.ui.styles} features={this.props.features} diff --git a/src/components/TimelineAxis.jsx b/src/components/TimelineAxis.jsx index 58999ef..a93ebd3 100644 --- a/src/components/TimelineAxis.jsx +++ b/src/components/TimelineAxis.jsx @@ -20,8 +20,8 @@ class TimelineAxis extends React.Component { sndFmt = '' // 1yr } else if (this.props.extent > 43200) { - sndFmt = '%Y' - fstFmt = '%d %b' + sndFmt = '%d %b' + fstFmt = '' } else { sndFmt = '%d %b' fstFmt = '%H:%M' diff --git a/src/components/presentational/Map/Clusters.jsx b/src/components/presentational/Map/Clusters.jsx index 0f59688..b999541 100644 --- a/src/components/presentational/Map/Clusters.jsx +++ b/src/components/presentational/Map/Clusters.jsx @@ -1,7 +1,7 @@ -import React from 'react' +import React, { useState } from 'react' import { Portal } from 'react-portal' import colors from '../../../common/global.js' -import { calcClusterOpacity, calcClusterSize } from '../../../common/utilities' +import { calcClusterOpacity, calcClusterSize, isLatitude, isLongitude } from '../../../common/utilities' const DefsClusters = () => ( @@ -12,97 +12,108 @@ const DefsClusters = () => ( ) +function Cluster ({ cluster, size, projectPoint, totalPoints, styles, renderHover, onClick }) { + /** + { + geometry: { + coordinates: [longitude, latitude] + }, + properties: { + cluster: true|false, + cluster_id: int, + point_count: int, + point_count_abbreviated: int + }, + type: "Feature" + } + */ + const { cluster_id: clusterId } = cluster.properties + const { coordinates } = cluster.geometry + const [longitude, latitude] = coordinates + if (!isLatitude(latitude) || !isLongitude(longitude)) return null + const { x, y } = projectPoint([latitude, longitude]) + const [hovered, setHovered] = useState(false) + + return ( + onClick({ id: clusterId, latitude, longitude })} + onMouseEnter={() => setHovered(true)} + onMouseLeave={() => setHovered(false)} + > + + {hovered ? renderHover(cluster) : null} + + + ) +} + function ClusterEvents ({ projectPoint, - styleCluster, onSelect, isRadial, svg, clusters }) { - function calculateTotalPoints () { - return clusters.reduce((total, cl) => { - if (cl && cl.properties) { - total += cl.properties.point_count - } - return total - }, 0) + const totalPoints = clusters.reduce((total, cl) => { + if (cl && cl.properties) { + total += cl.properties.point_count + } + return total + }, 0) + + const styles = { + fill: isRadial ? "url('#clusterGradient')" : colors.fallbackEventColor, + stroke: colors.darkBackground, + strokeWidth: 0 } - function renderClusterBySize (cluster) { - const { point_count: pointCount, cluster_id: clusterId } = cluster.properties - const { coordinates } = cluster.geometry - const [longitude, latitude] = coordinates - - const totalPoints = calculateTotalPoints() - - const styles = { - fill: isRadial ? "url('#clusterGradient')" : colors.fallbackEventColor, - stroke: colors.darkBackground, - strokeWidth: 0, - fillOpacity: calcClusterOpacity(pointCount, totalPoints) - } - - return ( - - {} - - ) - } - - function renderCluster (cluster) { - /** - { - geometry: { - coordinates: [longitude, latitude] - }, - properties: { - cluster: true|false, - cluster_id: int, - point_count: int, - point_count_abbreviated: int - }, - type: "Feature" - } - */ - const { coordinates } = cluster.geometry - const [longitude, latitude] = coordinates - if (!latitude || !longitude) return null - const { x, y } = projectPoint([latitude, longitude]) - - const customStyles = styleCluster ? styleCluster(cluster) : null - const extraRender = () => ( - - {customStyles[1]} - - ) - - return ( - onSelect(e)} - > - {renderClusterBySize(cluster)} - {extraRender ? extraRender() : null} - - ) + function renderHover (txt) { + return {txt} } return ( {isRadial ? : null} - {clusters.map(renderCluster)} + {clusters.map(c => { + const pointCount = c.properties.point_count + const clusterSize = calcClusterSize(pointCount, totalPoints) + return <> + + {renderHover(pointCount)} + } + /> + })} ) diff --git a/src/components/presentational/Timeline/Handles.js b/src/components/presentational/Timeline/Handles.js index d3c55e1..3606a14 100644 --- a/src/components/presentational/Timeline/Handles.js +++ b/src/components/presentational/Timeline/Handles.js @@ -1,26 +1,24 @@ import React from 'react' const TimelineHandles = ({ dims, onMoveTime }) => { - return
- // temporarilty disabled while we get functionality working again - // return ( - // - // onMoveTime('backwards')} - // > - // - // - // - // onMoveTime('forward')} - // > - // - // - // - // - // ) + return ( + + onMoveTime('backwards')} + > + + + + onMoveTime('forward')} + > + + + + + ) } export default TimelineHandles diff --git a/src/components/presentational/Timeline/Markers.js b/src/components/presentational/Timeline/Markers.js index 3678d1e..6712cb4 100644 --- a/src/components/presentational/Timeline/Markers.js +++ b/src/components/presentational/Timeline/Markers.js @@ -1,18 +1,20 @@ import React from 'react' import colors from '../../../common/global' +import { getEventCategories } from '../../../common/utilities' const TimelineMarkers = ({ styles, eventRadius, getEventX, getEventY, + categories, transitionDuration, selected, dims, features }) => { - function renderMarker (event) { - function renderCircle () { + function renderMarker (acc, event) { + function renderCircle (y) { return @@ -35,8 +37,8 @@ const TimelineMarkers = ({ return } - const isDot = (!!event.location && !!event.longitude) || (features.GRAPH_NONLOCATED && event.projectOffset !== -1) - switch (event.shape) { - case 'circle': - return renderCircle() - case 'bar': - return renderBar() - case 'diamond': - return renderCircle() - case 'star': - return renderCircle() - default: - return isDot ? renderCircle() : renderBar() + const isDot = (!!event.location && !!event.longitude) || (features.GRAPH_NONLOCATED && event.projectOffset !== -1) + const evShadows = getEventCategories(event, categories).map(cat => getEventY({ ...event, category: cat.id })) + + function renderMarkerForEvent (y) { + switch (event.shape) { + case 'circle': + case 'diamond': + case 'star': + acc.push(renderCircle(y)) + break + case 'bar': + acc.push(renderBar(y)) + break + default: + return isDot ? acc.push(renderCircle(y)) : acc.push(renderBar(y)) + } } + + if (evShadows.length > 0) { + evShadows.forEach(renderMarkerForEvent) + } else { + renderMarkerForEvent(getEventY(event)) + } + return acc } return ( - {selected.map(event => renderMarker(event))} + {selected.reduce(renderMarker, [])} ) } diff --git a/src/scss/timeline.scss b/src/scss/timeline.scss index d6a4ab3..058c7a6 100644 --- a/src/scss/timeline.scss +++ b/src/scss/timeline.scss @@ -210,7 +210,6 @@ $timeline-height: 170px; .timeline-marker { fill: none; - transition: transform 0.2s ease; } .coevent { diff --git a/src/store/initial.js b/src/store/initial.js index d83736d..61f6ea8 100644 --- a/src/store/initial.js +++ b/src/store/initial.js @@ -27,6 +27,7 @@ const initial = { * or by the characteristics of the client, browser, etc. */ app: { + debug: true, errors: { source: false },