From 98494e683d5017b1a13224c6cb3df2abec6c9dde Mon Sep 17 00:00:00 2001 From: Franc Camps-Febrer Date: Fri, 4 Jan 2019 08:04:03 +0100 Subject: [PATCH] Readjust event handlers --- src/components/Timeline.jsx | 72 +++++++------ src/components/TimelineAxis.jsx | 5 +- src/components/TimelineCategories.jsx | 9 +- src/components/TimelineEvents.jsx | 2 +- src/js/timeline/timeline.js | 139 -------------------------- 5 files changed, 51 insertions(+), 176 deletions(-) delete mode 100644 src/js/timeline/timeline.js diff --git a/src/components/Timeline.jsx b/src/components/Timeline.jsx index 03ec64e..6a11e5c 100644 --- a/src/components/Timeline.jsx +++ b/src/components/Timeline.jsx @@ -33,27 +33,25 @@ class Timeline extends React.Component { softTimeUpdate: 0, scaleX: null, scaleY: null, - timerange: [null, null] + timerange: [null, null], + dragPos0: null }; } componentDidMount() { - this.methods = Object.assign({}, this.props.methods, { - onSoftUpdate: (toggle) => { this.setState({ softTimeUpdate: toggle }) } - }); - this.computeDims(); window.addEventListener('resize', () => { this.computeDims(); }); } componentWillReceiveProps(nextProps) { if (hash(nextProps) !== hash(this.props)) { - let groupYs = Array.apply(null, Array(nextProps.domain.categories.length)); - groupYs = groupYs.map((g, i) => (i + 1) * 80 / groupYs.length); + const categories = nextProps.domain.categories; + const cats = categories.map((g, i) => (i + 1) * 80 / categories.length); this.setState({ + timerange: nextProps.app.timerange, scaleX: d3.scaleTime().domain(nextProps.app.timerange).range([this.state.dims.margin_left, this.state.dims.width]), - scaleY: d3.scaleOrdinal().domain(nextProps.domain.categories).range(groupYs) + scaleY: d3.scaleOrdinal().domain(nextProps.domain.categories).range(cats) }); } } @@ -102,7 +100,7 @@ class Timeline extends React.Component { * @param {String} direction: 'forward' / 'backwards' */ onMoveTime(direction) { - this.methods.onSelect(); + this.props.methods.onSelect(); const extent = this.getTimeScaleExtent(); const newCentralTime = d3.timeMinute.offset(this.state.scaleX.domain()[0], extent / 2); @@ -117,9 +115,17 @@ class Timeline extends React.Component { } this.state.scaleX.domain([domain0, domainF]); - this.methods.onUpdateTimerange(this.state.scaleX.domain()); + this.props.methods.onUpdateTimerange(this.state.scaleX.domain()); } + /** + * Shift time range by moving forward or backwards + * WITHOUT updating the store + * @param {String} direction: 'forward' / 'backwards' + */ + onSoftTimeRangeUpdate(timerange) { + this.setState({ timerange }); + } /** * Apply zoom level to timeline @@ -134,42 +140,48 @@ class Timeline extends React.Component { d3.timeMinute.offset(newCentralTime, zoom.duration / 2) ]); - this.methods.onUpdateTimerange(this.state.scaleX.domain()); + this.props.methods.onUpdateTimerange(this.state.scaleX.domain()); + } + + toggleTransition(isTransition) { + this.setState({ transitionDuration: (isTransition) ? 300 : 0 }); } /* * Setup drag behavior */ - onDragStart(ev) { + onDragStart() { d3.event.sourceEvent.stopPropagation(); - dragPos0 = d3.event.x; - this.toggleTransition(false); + this.setState({ + dragPos0: d3.event.x + }, () => { + this.toggleTransition(false); + }); } /* * Drag and update */ onDrag() { - const drag0 = this.state.scaleX.invert(dragPos0).getTime(); + const drag0 = this.state.scaleX.invert(this.state.dragPos0).getTime(); const dragNow = this.state.scaleX.invert(d3.event.x).getTime(); const timeShift = (drag0 - dragNow) / 1000; - const newDomain0 = d3.timeSecond.offset(timerange[0], timeShift); - const newDomainF = d3.timeSecond.offset(timerange[1], timeShift); + const newDomain0 = d3.timeSecond.offset(this.state.timerange[0], timeShift); + const newDomainF = d3.timeSecond.offset(this.state.timerange[1], timeShift); this.state.scaleX.domain([newDomain0, newDomainF]); - //render(); + // Updates components without updating timerange - this.methods.onSoftUpdate(1); + this.onSoftTimeRangeUpdate([newDomain0, newDomainF]); } onDragEnd() { - toggleTransition(true); + this.toggleTransition(true); this.setState({ timerange: this.state.scaleX.domain() }, () => { - this.methods.onSoftUpdate(0); - this.methods.onUpdateTimerange(scale.x.domain()); + this.props.methods.onUpdateTimerange(this.state.scaleX.domain()); }); } @@ -186,13 +198,14 @@ class Timeline extends React.Component { { this.onDragStart() }} + onDrag={() => { this.onDrag() }} + onDragEnd={() => { this.onDragEnd() }} categories={this.props.domain.categories} /> this.getEventX(e)} getEventY={(e) => this.getEventY(e)} getCategoryColor={this.props.methods.getCategoryColor} + transitionDuration={this.state.transitionDuration} onSelect={this.props.methods.onSelect} /> @@ -231,9 +245,9 @@ class Timeline extends React.Component { return (
{ this.onClickArrow(); }} hideInfo={isNarrative} /> diff --git a/src/components/TimelineAxis.jsx b/src/components/TimelineAxis.jsx index e41b2f1..0daafe4 100644 --- a/src/components/TimelineAxis.jsx +++ b/src/components/TimelineAxis.jsx @@ -8,7 +8,6 @@ class TimelineAxis extends React.Component { this.xAxis1Ref = React.createRef(); this.state = { isInitialized: false, - transitionDuration: 300 } } @@ -35,12 +34,12 @@ class TimelineAxis extends React.Component { if (this.state.isInitialized) { d3.select(this.xAxis0Ref.current) .transition() - .duration(this.state.transitionDuration) + .duration(this.props.transitionDuration) .call(this.x0); d3.select(this.xAxis1Ref.current) .transition() - .duration(this.state.transitionDuration) + .duration(this.props.transitionDuration) .call(this.x1); } } diff --git a/src/components/TimelineCategories.jsx b/src/components/TimelineCategories.jsx index 241185f..c84fcb4 100644 --- a/src/components/TimelineCategories.jsx +++ b/src/components/TimelineCategories.jsx @@ -11,7 +11,7 @@ class TimelineCategories extends React.Component { } componentDidUpdate() { - if (!this.state.isInitialized && this.props.timeline) { + if (!this.state.isInitialized) { const drag = d3.drag() .on('start', this.props.onDragStart) .on('drag', this.props.onDrag) @@ -29,10 +29,11 @@ class TimelineCategories extends React.Component { } renderCategory(category, idx) { + const dims = this.props.dims; return ( - - {category.category} + + {category.category} ) } @@ -50,7 +51,7 @@ class TimelineCategories extends React.Component { class="drag-grabber" x={dims.margin_left} y="20" - width={dims.width - dims.margin_left} + width={dims.width - dims.margin_left - dims.width_controls} height={dims.trackHeight} > diff --git a/src/components/TimelineEvents.jsx b/src/components/TimelineEvents.jsx index 60c676e..9b1a752 100644 --- a/src/components/TimelineEvents.jsx +++ b/src/components/TimelineEvents.jsx @@ -17,7 +17,7 @@ class TimelineEvents extends React.Component { cy={0} style={{ 'transform': `translate(${this.props.getEventX(event)}px, ${this.props.getEventY(event)}px)`, - 'transition': 'transform 0.3s ease' + 'transition': `transform ${this.props.transitionDuration / 1000}s ease` }} r={5} fill={this.props.getCategoryColor(event.category)} diff --git a/src/js/timeline/timeline.js b/src/js/timeline/timeline.js deleted file mode 100644 index b0e138e..0000000 --- a/src/js/timeline/timeline.js +++ /dev/null @@ -1,139 +0,0 @@ -/* - TIMELINE - Displays events over the course of the night - Allows brushing and selecting periods of time in it -*/ -import { parseDate } from '../utilities'; -import hash from 'object-hash'; -import esLocale from '../data/es-MX.json'; - -export default function(svg, ui, methods) { - d3.timeFormatDefaultLocale(esLocale); - - let categories = []; - let timerange = [null, null]; - - // Dimension of the client - const WIDTH_CONTROLS = 100; - let WIDTH = getCurrentWidth() - WIDTH_CONTROLS; - const HEIGHT = 80; - - // NB: is it possible to do this with SCSS? - // A: Maybe, although we are using it programmatically here for now - const margin = { left: 120, top: 20 }; - - // Drag behavior - let dragPos0; - let transitionDuration = 300; - - /** - * Create scales - */ - const scale = {}; - scale.x = d3.scaleTime() - .domain(timerange) - .range([margin.left, WIDTH]); - - - - /** - * Adapt dimensions when resizing - */ - function getCurrentWidth() { - return d3.select(`#${ui.dom.timeline}`).node() - .getBoundingClientRect().width; - } - - - /** - * Resize timeline one window resice - */ - function addResizeListener() { - window.addEventListener('resize', () => { - if (d3.select(`#${ui.dom.timeline}`).node() !== null) { - WIDTH = getCurrentWidth() - WIDTH_CONTROLS; - - scale.x.range([margin.left, WIDTH]); - dom.axis.y.attr('transform', `translate(${WIDTH}, 0)`) - render(null); - } - }); - } - addResizeListener(); - - - /** - * Get x position of eventPoint, considering the time scale - * @param {object} eventPoint: regular eventPoint data - */ - function getEventX(eventPoint) { - return scale.x(parseDate(eventPoint.timestamp)); - } - - /** - * Returns the time scale (x) extent in minutes - */ - function getTimeScaleExtent() { - return (scale.x.domain()[1].getTime() - scale.x.domain()[0].getTime()) / 60000; - } - - function toggleTransition(isTransition) { - transitionDuration = (isTransition) ? 500 : 0; - } - - - /* - * Setup drag behavior - */ - function onDragStart(ev) { - d3.event.sourceEvent.stopPropagation(); - dragPos0 = d3.event.x; - toggleTransition(false); - } - - /* - * Drag and update - */ - function onDrag() { - const drag0 = scale.x.invert(dragPos0).getTime(); - const dragNow = scale.x.invert(d3.event.x).getTime(); - const timeShift = (drag0 - dragNow) / 1000; - - const newDomain0 = d3.timeSecond.offset(timerange[0], timeShift); - const newDomainF = d3.timeSecond.offset(timerange[1], timeShift); - - scale.x.domain([newDomain0, newDomainF]); - render(); - // Updates components without updating timerange - methods.onSoftUpdate(1); - } - - function onDragEnd() { - toggleTransition(true); - timerange = scale.x.domain(); - methods.onSoftUpdate(0); - methods.onUpdateTimerange(scale.x.domain()); - } - - - /** - * Updates displayable data on the timeline: events, selected and - * potentially adjusts time range - * @param {Object} newCategories: object of arrays of categories - * @param {Object} newTimerange: object of time range - */ - function update(newCategories, newTimerange) { - if (hash(categories) !== hash(newCategories)) categories = newCategories; - if (hash(timerange) !== hash(newTimerange)) timerange = newTimerange; - scale.x.domain(timerange); - render(); - } - - return { - getEventX, - update, - onDragStart, - onDrag, - onDragEnd - }; -}