Mercurial > gemma
view client/src/components/TimeSlider.vue @ 5058:e916651d3f93 time-sliding
client: adjust time-slider position in case of opened diagram
author | Fadi Abbud <fadi.abbud@intevation.de> |
---|---|
date | Fri, 06 Mar 2020 17:21:20 +0100 |
parents | abe3a70526a6 |
children | ed1d963017e7 |
line wrap: on
line source
<template> <div id="slider" :class="[ 'd-flex box ui-element rounded bg-white flex-row', { expanded: showTimeSlider } ]" :style="reposition" > <div class="d-flex mt-1 mr-1"> <input class="form-control-sm mr-1" type="date" v-model="dateSelection" min="2015-01-01" :max="new Date().toISOString().split('T')[0]" /> <input type="time" min="00:00" max="23:59" v-model="timeSelection" class="form-control-sm" /> </div> <div id="sliderContainer" class="d-flex sliderContainer" style="width: 98%;" ></div> <div @click="close" class="d-flex box-control mr-0" style="width: 2%;"> <font-awesome-icon icon="times"></font-awesome-icon> </div> </div> </template> <style lang="sass" scoped> #slider position: absolute bottom: 0 min-width: 100vw &.expanded max-height: 100% max-width: 100% margin: 0 </style> <script> /* This is Free Software under GNU Affero General Public License v >= 3.0 * without warranty, see README.md and license for details. * * SPDX-License-Identifier: AGPL-3.0-or-later * License-Filename: LICENSES/AGPL-3.0.txt * * Copyright (C) 2020 by via donau * – Österreichische Wasserstraßen-Gesellschaft mbH * Software engineering by Intevation GmbH * * Author(s): * Fadi Abbud <fadiabbud@intevation.de> */ import { mapState } from "vuex"; import * as d3 from "d3"; import app from "@/main"; import { localeDateString } from "@/lib/datelocalization"; import { format, setHours, setMinutes } from "date-fns"; let zoom = null; export default { name: "timeslider", data() { return { newX: null }; }, watch: { ongoingRefresh() { if (this.ongoingRefresh) return; this.$store.commit("application/setSelectedTime", new Date()); this.$nextTick(this.redrawSlider); } }, computed: { ...mapState("application", ["showTimeSlider", "paneSetup"]), ...mapState("map", ["ongoingRefresh"]), reposition() { // reposition time slider in case of opened diagram if (["DEFAULT", "COMPARESURVEYS"].indexOf(this.paneSetup) === -1) { const height = document.getElementById("main").clientHeight + 1; return `bottom: ${height}px`; } else { return ""; } }, dateSelection: { get() { const date = this.$store.state.application.selectedTime; return format(date, "YYYY-MM-DD"); }, set(value) { if (!value) return; const date = new Date(value); this.$store.commit("application/setSelectedTime", date); zoom.translateTo( d3.select(".zoom"), this.getScale()(d3.isoParse(this.selectedTime.toISOString())), 0 ); zoom.scaleTo(d3.select(".zoom"), 50); } }, timeSelection: { get() { const time = this.$store.state.application.selectedTime; return format(time, "HH:mm"); }, set(value) { if (!value) return; let date = this.selectedTime; date = setHours(date, value.split(":")[0]); date = setMinutes(date, value.split(":")[1]); this.$store.commit("application/setSelectedTime", date); zoom.scaleTo(d3.select(".zoom"), 800); zoom.translateTo( d3.select(".zoom"), this.getScale()(d3.isoParse(this.selectedTime.toISOString())), 0 ); } }, selectedTime: { get() { return this.$store.state.application.selectedTime; }, set(value) { this.$store.commit("application/setSelectedTime", value); } } }, methods: { close() { this.$store.commit("application/showTimeSlider", false); }, redrawSlider() { this.createSlider(); zoom.translateTo( d3.select(".line"), this.newX(d3.isoParse(this.selectedTime.toISOString())), 1 ); }, createSlider() { const element = document.getElementById("sliderContainer"); const svgWidth = element ? element.clientWidth : 0, svgHeight = 40, marginTop = 20, marginLeft = 0; d3.timeFormatDefaultLocale(localeDateString); this.newX = this.getScale(); d3.select(".sliderContainer svg").remove(); let svg = d3 .select(".sliderContainer") .append("svg") .attr("width", svgWidth) .attr("height", svgHeight); zoom = d3 .zoom() .scaleExtent([0.8, 102000]) .translateExtent([[0, 0], [svgWidth, svgHeight]]) .extent([[0, 0], [(svgWidth, svgHeight)]]) .on("zoom", this.zoomed); svg .append("g") .attr("class", "axis--x") .attr("transform", `translate(${marginLeft}, ${marginTop})`) .call(this.getAxes()); // create rectanlge on the slider area to capture mouse events const eventRect = svg .append("rect") .attr("id", "zoom") .attr("class", "zoom") .attr("width", svgWidth) .attr("height", svgHeight) .attr("fill", "white") .attr("opacity", 0.2) .on("mouseover", () => { svg.select(".zoom").attr("cursor", "move"); }); eventRect.call(zoom).on("click", this.onClick); const toIsoDate = d => { return d.toISOString(); }; let drag = d3 .drag() .on("start", () => { d3.select(".line") .raise() .classed("active", true); }) .on("drag", this.onDrag) .on("end", () => { d3.select(".line").classed("active", false); }); // Create cursor to indicate to the selected time svg .append("rect") .attr("class", "line") .attr("id", "scrubber") .attr("x", this.newX(d3.isoParse(toIsoDate(this.selectedTime)))) .attr("y", 0) .attr("width", 2) .attr("height", svgHeight) .attr("stroke", "#17a2b8") .attr("stroke-width", 2) .attr("opacity", 0.6) .on("mouseover", () => { svg.select(".line").attr("cursor", "e-resize"); }) .call(drag); }, getScale() { return d3 .scaleTime() .range([0, document.getElementById("sliderContainer").clientWidth || 0]) .domain([d3.isoParse(new Date("2015-01-01")), d3.isoParse(new Date())]); }, getAxes() { const axesFormat = date => { return (d3.timeSecond(date) < date ? d3.timeFormat(".%L") : d3.timeMinute(date) < date ? d3.timeFormat(":%S") : d3.timeHour(date) < date ? d3.timeFormat("%H:%M") : d3.timeDay(date) < date ? d3.timeFormat("%H:%M") : d3.timeMonth(date) < date ? d3.timeWeek(date) < date ? d3.timeFormat(app.$gettext("%a %d")) : d3.timeFormat(app.$gettext("%b %d")) : d3.timeYear(date) < date ? d3.timeFormat("%B") : d3.timeFormat("%Y"))(date); }; return d3 .axisBottom(this.newX) .ticks(12) .tickFormat(axesFormat); }, zoomed() { let scale = this.getScale(); this.newX = d3.event.transform.rescaleX(scale); d3.select(".axis--x").call(this.getAxes()); d3.select(".line").attr("x", this.newX(d3.isoParse(this.selectedTime))); }, onClick() { // Extract the click location let point = d3.mouse(document.getElementById("zoom")), p = { x: point[0], y: point[1] }; d3.select(".line").attr("x", p.x); this.selectedTime = d3.isoParse(this.newX.invert(p.x + 2)); }, onDrag() { this.selectedTime = d3.isoParse(this.newX.invert(d3.event.x + 2)); d3.select(".line").attr("x", d3.event.x); } }, mounted() { setTimeout(this.createSlider, 150); } }; </script>