From a0c654aafc624ecf15fd99b324984e0a5cb4e1ef Mon Sep 17 00:00:00 2001 From: Franc Camps-Febrer Date: Thu, 3 Jan 2019 18:41:57 +0100 Subject: [PATCH] Reactifyf scale Y --- src/components/Timeline.jsx | 30 ++++++++++-- src/components/TimelineCategories.jsx | 55 +++++++++++++++++++++ src/js/timeline/timeline.js | 70 ++------------------------- src/scss/timeline.scss | 6 +++ 4 files changed, 92 insertions(+), 69 deletions(-) create mode 100644 src/components/TimelineCategories.jsx diff --git a/src/components/Timeline.jsx b/src/components/Timeline.jsx index 5a119f3..78a313b 100644 --- a/src/components/Timeline.jsx +++ b/src/components/Timeline.jsx @@ -13,6 +13,7 @@ import TimelineLogic from '../js/timeline/timeline.js'; import TimelineLabels from './TimelineLabels.jsx'; import TimelineMarkers from './TimelineMarkers.jsx' import TimelineEvents from './TimelineEvents.jsx'; +import TimelineCategories from './TimelineCategories.jsx'; class Timeline extends React.Component { constructor(props) { @@ -27,12 +28,15 @@ class Timeline extends React.Component { height_controls: 115, margin_left: 120 }, - softTimeUpdate: 0 + softTimeUpdate: 0, + scaleY: null }; } componentDidMount() { - this.methods = Object.assign({}, this.props.methods, { onSoftUpdate: (toggle) => { this.setState({ softTimeUpdate: toggle }) }}); + this.methods = Object.assign({}, this.props.methods, { + onSoftUpdate: (toggle) => { this.setState({ softTimeUpdate: toggle }) } + }); this.timeline = new TimelineLogic(this.svgRef.current, this.props.ui, this.methods); this.timeline.update(this.props.domain.categories, this.props.app.timerange); this.computeDims(); @@ -42,9 +46,23 @@ class Timeline extends React.Component { componentWillReceiveProps(nextProps) { if (hash(nextProps) !== hash(this.props)) { this.timeline.update(nextProps.domain.categories, nextProps.app.timerange); + + let groupYs = Array.apply(null, Array(nextProps.domain.categories.length)); + groupYs = groupYs.map((g, i) => (i + 1) * 80 / groupYs.length); + + this.setState({ scaleY: d3.scaleOrdinal().domain(nextProps.domain.categories).range(groupYs) }); } } + /** + * Get y height of eventPoint, considering the ordinal Y scale + * @param {object} eventPoint: regular eventPoint data + */ + getEventY(eventPoint) { + return this.state.scaleY(eventPoint.category); + } + + onClickArrow() { this.setState((prevState, props) => { return {isFolded: !prevState.isFolded}; @@ -84,6 +102,10 @@ class Timeline extends React.Component { height={dims.height} > + { this.onMoveTime(dir) }} @@ -100,12 +122,12 @@ class Timeline extends React.Component { this.timeline.getEventX(e)} - getEventY={(e) => this.timeline.getEventY(e)} + getEventY={(e) => this.getEventY(e)/*this.timeline.getEventY(e)*/} /> this.timeline.getEventX(e)} - getEventY={(e) => this.timeline.getEventY(e)} + getEventY={(e) => this.getEventY(e)/*this.timeline.getEventY(e)*/} getCategoryColor={this.props.methods.getCategoryColor} onSelect={this.props.methods.onSelect} /> diff --git a/src/components/TimelineCategories.jsx b/src/components/TimelineCategories.jsx new file mode 100644 index 0000000..9d2e625 --- /dev/null +++ b/src/components/TimelineCategories.jsx @@ -0,0 +1,55 @@ +import React from 'react'; + +class TimelineCategories extends React.Component { + + constructor() { + super(); + this.grabRef = React.createRef() + this.state = { + isInitialized: false + } + } + + componentDidUpdate() { + if (!this.state.isInitialized && this.props.timeline) { + const drag = d3.drag() + .on('start', this.props.timeline.onDragStart) + .on('drag', this.props.timeline.onDrag) + .on('end', this.props.timeline.onDragEnd); + + d3.select(this.grabRef.current) + .call(drag); + + this.setState({ isInitialized: true }); + } + } + + getY(idx) { + return (idx + 1) * 80 / this.props.categories.length + } + + renderCategory(category, idx) { + return ( + + + {category.category} + + ) + } + + render () { + return ( + + {this.props.categories.map((cat, idx) => this.renderCategory(cat, idx))} + + + ); + } +} + +export default TimelineCategories; \ No newline at end of file diff --git a/src/js/timeline/timeline.js b/src/js/timeline/timeline.js index b844363..9e65864 100644 --- a/src/js/timeline/timeline.js +++ b/src/js/timeline/timeline.js @@ -2,7 +2,6 @@ TIMELINE Displays events over the course of the night Allows brushing and selecting periods of time in it - TODO: is it possible to express this idiomatically as React? */ import { parseDate } from '../utilities'; import hash from 'object-hash'; @@ -35,9 +34,6 @@ export default function(svg, ui, methods) { .domain(timerange) .range([margin.left, WIDTH]); - scale.y = d3.scaleOrdinal() - - /** * Initilize SVG elements and groups */ @@ -79,10 +75,6 @@ export default function(svg, ui, methods) { .tickSize(0) .tickFormat(d3.timeFormat('%H:%M')); - axis.y = - d3.axisLeft(scale.y) - .tickValues([]); - updateAxis(); /** @@ -103,7 +95,6 @@ export default function(svg, ui, methods) { WIDTH = getCurrentWidth() - WIDTH_CONTROLS; scale.x.range([margin.left, WIDTH]); - axis.y.tickSize(WIDTH - margin.left); dom.axis.y.attr('transform', `translate(${WIDTH}, 0)`) render(null); } @@ -111,14 +102,6 @@ export default function(svg, ui, methods) { } addResizeListener(); - /** - * Get y height of eventPoint, considering the ordinal Y scale - * @param {object} eventPoint: regular eventPoint data - */ - function getEventY(eventPoint) { - const yGroup = eventPoint.category; - return scale.y(yGroup); - } /** * Get x position of eventPoint, considering the time scale @@ -184,6 +167,7 @@ export default function(svg, ui, methods) { * Setup drag behavior */ function onDragStart(ev) { + console.log('ohoh') d3.event.sourceEvent.stopPropagation(); dragPos0 = d3.event.x; toggleTransition(false); @@ -213,30 +197,6 @@ export default function(svg, ui, methods) { methods.onUpdateTimerange(scale.x.domain()); } - const drag = d3.drag() - .on('start', onDragStart) - .on('drag', onDrag) - .on('end', onDragEnd); - - /** - * Updates data displayed by this timeline, but only render if necessary - * @param {Object} domain: Redux state domain subtree - * @param {Object} app: Redux state app subtree - */ - function updateAxis() { - let groupYs = Array.apply(null, Array(categories.length)); - groupYs = groupYs.map((g, i) => { - return (i + 1) * HEIGHT / groupYs.length; - }); - scale.y = d3.scaleOrdinal() - .domain(categories) - .range(groupYs); - - axis.y = - d3.axisLeft(scale.y) - .tickValues(categories.map(c => c.category)); - } - /** * Render axis on timeline and viewbox boundaries @@ -251,27 +211,6 @@ export default function(svg, ui, methods) { .transition() .duration(transitionDuration) .call(axis.x1); - - axis.y.tickSize(WIDTH - margin.left); - - if (!dom.axis.dragGrabber) { - dom.axis.dragGrabber = dom.svg.insert('rect', ':first-child') - .attr('class', 'drag-grabber') - .attr('x', margin.left) - .attr('y', margin.top) - .attr('width', WIDTH - margin.left) - .attr('height', HEIGHT) - .call(drag); - } - - if (!dom.axis.y) { - dom.axis.y = dom.svg.insert('g', ':first-child') - .attr('transform', `translate(${WIDTH}, 0)`) - .attr('class', 'yAxis'); - } - - dom.axis.y - .call(axis.y); } @@ -289,15 +228,16 @@ export default function(svg, ui, methods) { } function render() { - updateAxis(); renderAxis(); } return { getEventX, - getEventY, applyZoom, moveTime, - update + update, + onDragStart, + onDrag, + onDragEnd }; } diff --git a/src/scss/timeline.scss b/src/scss/timeline.scss index 54d63a3..8e495b3 100644 --- a/src/scss/timeline.scss +++ b/src/scss/timeline.scss @@ -176,6 +176,12 @@ cursor: -webkit-grab; cursor: -moz-grab; } + + .tick text { + font-size: 10px; + font-family: 'Lato'; + text-anchor: end; + } } .drag-grabber {