Commit 01d45b96 authored by Elisabeth Reuhl's avatar Elisabeth Reuhl
Browse files

Merge remote-tracking branch 'origin/master'

parents 0b617cad dfc235d1
import React, {useEffect, useRef, useState} from "react";
import { Card, Grid } from "@material-ui/core";
import { Grid } from "@material-ui/core";
import { useTranslation } from "react-i18next";
import { useStyles } from "../../styles";
import { TimelineChart } from "./TimelineChart";
//import {select, scaleBand, axisBottom, axisLeft, scaleLinear, max, min, ascending, descending} from "d3";
//import {getTimeRangeOfTimelineData, newGroupByPeriods} from "../../utils";
import {getDimensions} from "../../utils";
export const Timeline = (props) => {
const { t, i18n } = useTranslation();
const classes = useStyles();
const [dimensions, setDimensions] = useState({width: 0, height: 0, margin: {top: 0, right: 0, left: 0, bottom: 0}});
const { timelineObjectsData } = props;
const filteredTimelineData = timelineObjectsData&&timelineObjectsData.filter( datapoint => datapoint.periodSpans?.[0]!==undefined||datapoint.periodSpans?.length>1);
useEffect( () => {
let currentDimensions = getDimensions("timelineContainer");
if (currentDimensions&&currentDimensions.width!==dimensions?.width)
setDimensions(currentDimensions);
console.log("state dimensions", dimensions)
//svg dimensions
const margin = {top: 5, right: 20, left: 20, bottom: 30};
//todo: read current dimensions on window resize
//console.log("tlc", document.getElementById("timelineContainer"));
const timelineContainer = document.getElementById("timelineContainer");
const containerHeight = timelineContainer?.clientHeight,
containerWidth = timelineContainer?.clientWidth;
const width = containerWidth - margin.left - margin.right,
height = containerHeight - margin.top - margin.bottom;
const dimensions = {margin: margin, width: width, height: height};
/*useEffect( () => {
console.log("tlc", document.getElementById("timelineContainer").clientWidth);
},[] )*/
/*useEffect(() => {
//svg dimensions
const containerHeight = parseInt(select("#timelineContainer").style("height")),
containerWidth = parseInt(select("#timelineContainer").style("width"));
const width = containerWidth - margin.left - margin.right,
height = containerHeight - margin.top - margin.bottom;
svg.attr("width", width + margin.left + margin.right)
.attr("height", height + margin.top + margin.bottom);
...deleted the rest, kept this for reference
*/
}, []);
return (
<>
......@@ -59,7 +31,7 @@ export const Timeline = (props) => {
</Grid>
<Grid id="timelineContainer" className={classes.dashboardTileContent} item container direction="column" spacing={2}>
<Grid item>
<TimelineChart filteredTimelineData={filteredTimelineData} dimensions={dimensions} />
<TimelineChart filteredTimelineData={filteredTimelineData} dimensions={dimensions/*getDimensions("timelineContainer")*/} />
</Grid>
</Grid>
</>
......
import React, {useEffect, useRef, useState} from "react";
import { Card, Grid } from "@material-ui/core";
import { useTranslation } from "react-i18next";
import { useStyles } from "../../styles";
import { select, scaleBand, axisBottom, scaleLinear, zoom } from "d3";
//import {select, scaleBand, axisBottom, axisLeft, scaleLinear, max, min, ascending, descending} from "d3";
import {getTimeRangeOfTimelineData, newGroupByPeriods} from "../../utils";
export const TimelineChart = (props) => {
......@@ -15,7 +13,6 @@ export const TimelineChart = (props) => {
//const filteredTimelineData = props.filteredTimelineData;
console.log("dimensions", props.dimensions)
const { width, height, margin } = props.dimensions;
const xDomain = getTimeRangeOfTimelineData(props.filteredTimelineData,"period");
const data = newGroupByPeriods(props.filteredTimelineData);
......@@ -24,19 +21,10 @@ export const TimelineChart = (props) => {
//console.log(timelineObjectsData);
console.log("filteredTimelineData: ", props.filteredTimelineData);
/*useEffect( () => {
if (filteredTimelineData) {
setData(newGroupByPeriods(filteredTimelineData));
//data&&data.size>0&&console.log([...data.values()].sort( (a,b) => a.periodSpan?.[0]-b.periodSpan?.[0] ));
}
//const byPeriodData = newGroupByPeriods(filteredTimelineData)
}, [props.filteredTimelineData]
)*/
//setting up the svg after first render
useEffect(() => {
console.log("width", width)
const { width, height, margin } = props.dimensions;
//console.log("width", width)
const svg = select(svgRef.current)
.attr("width", width + margin.left + margin.right)
.attr("height", height + margin.top + margin.bottom);
......@@ -47,13 +35,14 @@ export const TimelineChart = (props) => {
//draw timeline everytime filteredTimelineData changes
useEffect( () => {
drawTimeline(timelineData)
}, [props.filteredTimelineData] );
drawTimeline(timelineData, props.dimensions)
}, [props.filteredTimelineData, props.dimensions] );
//todo: still depending on outer scope for height, width, margin
const drawTimeline = (timelineConfig) => {
//todo: check if still depending on outer scope (like height, width, margin)
const drawTimeline = (timelineConfig, dimensions) => {
const { data, svgRef, xDomain } = timelineConfig;
const { width, height, margin } = dimensions;
const svg = select(svgRef.current);
if(!data||data.size===0) {
......@@ -63,9 +52,8 @@ export const TimelineChart = (props) => {
}
//console.log([...data.values()]);
const selection = svg.select(".timelineGroup").selectAll("rect").data([...data.values()], data => data.periodId);
console.log("initial selection", selection);
//console.log("initial selection", selection);
const selectionLabels = svg.select(".timelineGroup").selectAll(".label").data([...data.values()], data => data.periodId);
const periodIds = [...data.keys()];
......@@ -79,26 +67,52 @@ export const TimelineChart = (props) => {
.range([height,0])
.padding(0.2)
//add x axis to svg
const xAxis = axisBottom(xScale);
const xAxisDraw = svg.select(".xAxis")
.attr("transform", `translate(0,${height+margin.top})`)
.call(xAxis);
if (document.getElementById("clip")===null&&height&&width) {
svg.append("defs").append("clipPath")
.attr("id","clip")
.append("rect")
.attr("width", width)
.attr("height", height)
.attr("x", 0)
.attr("y", 0);
}
svg.select(".timelineGroup")
.attr("clip-path", "url(#clip)");
//set up zoom and pan
console.log(zoom());
const handleZoom = (event) => {
svg.select(".timelineGroup")
.attr("transform", event.transform);
const transform = event.transform;
//zoom and pan bars (geometric)
svg.select(".timelineGroup").selectAll(".bar")
.attr("transform", transform);
const xScaleNew = transform.rescaleX(xScale);
const yScaleNew = yScale.range([height,0].map( d => transform.applyY(d) ));
xAxis.scale(xScaleNew);
xAxisDraw.call(xAxis);
svg.selectAll(".label")
.attr("x", value => xScaleNew(value.periodSpan?.[0]))
.attr("y", value => yScaleNew(value.periodId));
};
const zimzoom = zoom()
.scaleExtent([1,5])
.translateExtent([[0,0], [width, height]])
.translateExtent([[0,0], [width, height+margin.bottom+margin.top]])
.on("zoom", handleZoom);
const initZoom = () => {
svg
.call(zimzoom);
}
//add x axis to svg
svg.select(".xAxis")
.attr("transform", `translate(0,${height})`)
.call(axisBottom(xScale))
//add bars (without extension) for each period on enter and return a selection of all entering and updating nodes
const selectionEnteringAndUpdating = selection.join(
enter => enter
......@@ -136,7 +150,10 @@ export const TimelineChart = (props) => {
.remove()
initZoom();
console.log("selection - ", selection)
}
const updateTimeline = () => {
}
/*
......@@ -282,9 +299,10 @@ export const TimelineChart = (props) => {
<div className="timeline">
<svg ref={svgRef}>
<g className="timelineGroup">
<g className="xAxis"></g>
<g className="yAxis"></g>
</g>
<g className="xAxis"></g>
<g className="yAxis"></g>
<g className="background" />
</svg>
</div>
......
......@@ -27,6 +27,20 @@ const useDebounce = (value, delay) => {
return debouncedValue;
}
//get dimensions height and width from an element in the dom
const getDimensions = (domContainerID) => {
const timelineContainer = document.getElementById(domContainerID);
if(!timelineContainer) console.log(`DOM element with ID ${domContainerID} not found`, timelineContainer);
const margin = {top: 5, right: 20, left: 20, bottom: 30};
const containerHeight = timelineContainer?.clientHeight,
containerWidth = timelineContainer?.clientWidth;
const width = containerWidth - margin.left - margin.right,
height = containerHeight - margin.top - margin.bottom;
return {margin: margin, width: width, height: height};
}
//TIMELINE HELPER FUNCTIONS
......@@ -35,11 +49,17 @@ const timelineAdapter = ( object ) => {
let periodData = {};
let timelineData = [];
const date = new Date();
const currentYear = date.getFullYear();
object.temporal?.flat().forEach( period => {
let periodStuff = {
periodName: period.title,
periodType: period.type,
periodSpan: period.begin||period.end ? [period.begin, period.end] : undefined
periodSpan: period.begin||period.end
? [period.begin, period.end!=="present"? period.end : currentYear]
: undefined
};
//only checks for senses when period has no begin
//assumes there are no periods with an end but no beginning
......@@ -274,4 +294,15 @@ function binTimespanObjects( {timespanObjects, approxAmountBins} ) {
}
export { useDebounce, timelineAdapter, timelineMapper, groupByPeriods, newGroupByPeriods, transformTimelineData, getTimeRangeOfTimelineData, prepareHistogramData,binTimespanObjects };
\ No newline at end of file
export {
useDebounce,
timelineAdapter,
timelineMapper,
groupByPeriods,
newGroupByPeriods,
transformTimelineData,
getTimeRangeOfTimelineData,
prepareHistogramData,
binTimespanObjects,
getDimensions
};
\ No newline at end of file
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment