interference2024/src/lib/components/DateRangeSlider.svelte
2024-10-11 15:02:59 +02:00

163 строки
4.5 KiB
Svelte

<script>
// a custom slider for time range selection
import { createEventDispatcher } from 'svelte';
import { scaleLinear, scaleTime } from 'd3-scale';
import { slidable } from '../../actions/slidable';
import { timeRangeFilter, fullTimeRange, defaultTimeRange } from '../../stores/filters';
export let lockInMode = false;
export let label = '';
export let showLabel = true;
export let min = 0;
export let max = 10;
export let value;
export let showHandleLabels = true;
export let startColor = 'white';
export let middleColor = null;
export let stopColor = 'rgb(255, 0, 0)';
export let barOpacity = 1;
export let showBorder = true;
// Convert the numeric value numbers to dates
$: convertScale = scaleTime().domain($defaultTimeRange).range(value)
const dispatch = createEventDispatcher();
const handleWidth = 17;
const pos = {
left: 0,
right: 0
};
let sliderWidth = 0;
function handleSlide(e, side) {
const newPos = pos[side] + e.detail.dx;
if (newPos < 0 || newPos > sliderWidth) return;
if (side === 'left' && newPos > pos.right) return;
if (side === 'left' && newPos < scale.range()[0]) return;
if (side === 'right' && newPos < pos.left) return;
if (side === 'right' && newPos > scale.range()[1]) return;
pos[side] = newPos;
}
function handleSlideEnd(e, side) {
if (lockInMode) {
dispatch('changed', [Math.round(scale.invert(pos.left), 0),
Math.round(scale.invert(pos.right), 0)]);
} else {
dispatch('changed', [scale.invert(pos.left), scale.invert(pos.right)]);
$timeRangeFilter = [convertScale.invert(scale.invert(pos.left)), convertScale.invert(scale.invert(pos.right))]
}
}
$: scale = scaleLinear()
.domain([min, max])
.range([handleWidth / 2, sliderWidth - 1.7 * handleWidth]);
$: pos.left = scale(value[0]) || 0;
$: pos.right = scale(value[1]) || 0;
$: background = `linear-gradient(90deg, ${startColor}, ${middleColor ? middleColor + ', ' : ''}${stopColor})`;
//$: background = `linear-gradient(90deg, #ffffff, #000000)`;
</script>
<div class="slider"
bind:clientWidth={sliderWidth}
style="--handle-width: {handleWidth}px;">
{#if (showLabel)}
<div class="label">
{label}
</div>
{/if}
<div class="slider-body" class:border={showBorder}>
<div class="slider-selected-range"
style="width: {sliderWidth - 2 * handleWidth}px;
margin-left: {1 * handleWidth}px;
opacity: {barOpacity};
background: {background};"></div>
<div class="slider-handle"
class:no-label={!showHandleLabels}
style="left: {(Math.abs(value[0] - value[1]) < 0.1) ? pos.left - 5 : pos.left}px;"
use:slidable
on:slide={(e) => handleSlide(e, 'left')}
on:slideend={(e) => handleSlideEnd(e, 'left')}>
<span class="disable-select">{showHandleLabels ? Math.round(scale.invert(pos.left), 0) : ''}</span>
</div>
<div class="slider-handle"
class:no-label={!showHandleLabels}
style="left: {(Math.abs(value[0] - value[1]) < 0.1) ? pos.right + 5 : pos.right}px;"
use:slidable
on:slide={(e) => handleSlide(e, 'right')}
on:slideend={(e) => handleSlideEnd(e, 'right')}>
<span class="disable-select">{showHandleLabels ? Math.round(scale.invert(pos.right), 0) : ''}</span>
</div>
</div>
</div>
<style>
.slider {
display: flex;
flex-direction: column;
font-family: var(--font-02);
width: 200px;
max-width: 200px;
margin: 0.3rem 0.3rem 0 0.3rem;
position: relative;
pointer-events: all;
}
.label {
margin: 0 0 0.1rem 0;
font-size: 0.7rem;
color: var(--usa-blue);
}
.slider-body {
display: flex;
align-items: center;
width: 100%;
height: 1.7rem;
padding: 0.1rem 0;
font-size: 0.7rem;
background-color: var(--bg);
border: none;
position: relative;
}
.border {
border: 2px solid var(--usa-blue);
border-radius: 3px;
}
.slider-selected-range {
height: 8px;
border: none;
border-radius: 2px;
position: absolute;
z-index: 100;
}
.slider-handle {
width: var(--handle-width);
height: var(--handle-width);
border: 2px solid var(--usa-blue);
border-radius: 50%;
background-color: var(--bg);
cursor: pointer;
position: absolute;
z-index: 10000;
}
.slider-handle > span {
width: 100%;
height: 100%;
font-size: 0.7rem;
text-align: center;
color: var(--usa-blue);
position: absolute;
}
</style>