Skip to content

Commit

Permalink
feat(time-layer): add new component for date slider, range mode
Browse files Browse the repository at this point in the history
  • Loading branch information
AlitaBernachot committed Sep 18, 2023
1 parent 840938f commit a9a49bd
Show file tree
Hide file tree
Showing 10 changed files with 355 additions and 21 deletions.
4 changes: 3 additions & 1 deletion public/assets/locales/client.de.json
Original file line number Diff line number Diff line change
Expand Up @@ -4282,5 +4282,7 @@
"asta_flik_parcels / REFERENCE_PARCEL : V": "V-Parzellen (Prämienfähig)",
"Kaatzen_territoire : KatzenTerritoire": "Katzen Territorium",
"Kaatzen_territoire / KatzenTerritoire : Nadine": "Tegi",
"Kaatzen_territoire / KatzenTerritoire : Tom": "Tigro"
"Kaatzen_territoire / KatzenTerritoire : Tom": "Tigro",
"Modifier la date de début": "",
"Modifier la date de fin": ""
}
4 changes: 3 additions & 1 deletion public/assets/locales/client.en.json
Original file line number Diff line number Diff line change
Expand Up @@ -4035,5 +4035,7 @@
"asta_flik_parcels / REFERENCE_PARCEL : V": "V-parcels (eligible)",
"Kaatzen_territoire : KatzenTerritoire": "Cat Areas",
"Kaatzen_territoire / KatzenTerritoire : Nadine": "Tegi",
"Kaatzen_territoire / KatzenTerritoire : Tom": "Tigro"
"Kaatzen_territoire / KatzenTerritoire : Tom": "Tigro",
"Modifier la date de début": "",
"Modifier la date de fin": ""
}
4 changes: 3 additions & 1 deletion public/assets/locales/client.fr.json
Original file line number Diff line number Diff line change
Expand Up @@ -4284,5 +4284,7 @@
"asta_flik_parcels / REFERENCE_PARCEL : V": "Parcelle V (éligible)",
"Kaatzen_territoire : KatzenTerritoire": "Territoire de chats",
"Kaatzen_territoire / KatzenTerritoire : Nadine": "Tegi",
"Kaatzen_territoire / KatzenTerritoire : Tom": "Tigro"
"Kaatzen_territoire / KatzenTerritoire : Tom": "Tigro",
"Modifier la date de début": "Modifier la date de début",
"Modifier la date de fin": "Modifier la date de fin"
}
4 changes: 3 additions & 1 deletion public/assets/locales/client.lb.json
Original file line number Diff line number Diff line change
Expand Up @@ -4042,5 +4042,7 @@
"asta_flik_parcels / REFERENCE_PARCEL : V": "V-Parzellen (primmefäheg)",
"Kaatzen_territoire : KatzenTerritoire": "Kaatzen-Plaatzen",
"Kaatzen_territoire / KatzenTerritoire : Nadine": "Tegi",
"Kaatzen_territoire / KatzenTerritoire : Tom": "Tigro"
"Kaatzen_territoire / KatzenTerritoire : Tom": "Tigro",
"Modifier la date de début": "",
"Modifier la date de fin": ""
}
51 changes: 51 additions & 0 deletions src/__fixtures__/themes.api.fixture.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5976,6 +5976,57 @@ export const themesApiFixture = (): ConfigModel => ({
"mode": "value",
"widget": "slider"
}
},
{
"id": 99999,
"name": "layer_slider_range",
"metadata": {
"time_config": "{\"default_time\":\"2022-08\",\"time_links\":{\"2001-08\":\"ortho_2001\",\"2004-08\":\"ortho_2004\",\"2007-08\":\"ortho_2007\",\"2010-08\":\"ortho_2010\",\"2013-08\":\"ortho_2013\",\"2016-08\":\"ortho_2016\",\"2017-08\":\"ortho_2017\",\"2018-08\":\"ortho_2018\",\"2019-01\":\"ortho_2019_winter\",\"2019-08\":\"ortho_2019\",\"2020-08\":\"ortho_2020\",\"2021-08\":\"ortho_2021\",\"2022-08\":\"ortho_2022\"}}",
"time_layers": {
"2001-08-01T00:00:00Z": "ortho_2001",
"2004-08-01T00:00:00Z": "ortho_2004",
"2007-08-01T00:00:00Z": "ortho_2007",
"2010-08-01T00:00:00Z": "ortho_2010",
"2013-08-01T00:00:00Z": "ortho_2013",
"2016-08-01T00:00:00Z": "ortho_2016",
"2017-08-01T00:00:00Z": "ortho_2017",
"2018-08-01T00:00:00Z": "ortho_2018",
"2019-01-01T00:00:00Z": "ortho_2019_winter",
"2019-08-01T00:00:00Z": "ortho_2019",
"2020-08-01T00:00:00Z": "ortho_2020",
"2021-08-01T00:00:00Z": "ortho_2021",
"2022-08-01T00:00:00Z": "ortho_2022"
}
},
"dimensions": {},
"type": "WMTS",
"url": "http://wmts.geoportail.lu/mapproxy_4_v3/wmts/1.0.0/WMTSCapabilities.xml",
"layer": "layer_slider_range",
"imageType": "image/jpeg",
"time": {
"minValue": "2001-08-01T00:00:00Z",
"maxValue": "2022-08-01T00:00:00Z",
"values": [
"2001-08-01T00:00:00Z",
"2004-08-01T00:00:00Z",
"2007-08-01T00:00:00Z",
"2010-08-01T00:00:00Z",
"2013-08-01T00:00:00Z",
"2016-08-01T00:00:00Z",
"2017-08-01T00:00:00Z",
"2018-08-01T00:00:00Z",
"2019-01-01T00:00:00Z",
"2019-08-01T00:00:00Z",
"2020-08-01T00:00:00Z",
"2021-08-01T00:00:00Z",
"2022-08-01T00:00:00Z"
],
"resolution": "month",
"minDefValue": "2022-08-01T00:00:00Z",
"maxDefValue": null,
"mode": "range",
"widget": "slider"
}
}
],
"metadata": {},
Expand Down
55 changes: 50 additions & 5 deletions src/assets/main.css
Original file line number Diff line number Diff line change
Expand Up @@ -213,25 +213,70 @@
color: var(--alert-warning-tertiary);
}

.lux-time-slider-displayed-dates {
@apply flex w-full;
}

.lux-slidebar-thumb {
@apply bg-white h-5 w-[0.55rem] rounded-full border-tertiary absolute top-0 z-[2];
box-shadow: 0.1rem 0.1rem 0.1rem rgba(0, 0, 49, 0.2),
0 0 0.1rem rgba(0, 0, 75, 0.2);
border: 0.1rem solid rgba(0, 0, 30, 0.2);
}

.lux-time-slider input[type='range']::-webkit-slider-thumb {
@apply bg-white h-5 w-2 rounded-full border-tertiary;
@apply bg-white h-5 w-[0.55rem] rounded-full border-tertiary cursor-pointer;
-webkit-appearance: none;
box-shadow: 0.1rem 0.1rem 0.1rem rgba(0, 0, 49, 0.2),
0 0 0.1rem rgba(0, 0, 75, 0.2);
border: 0.1rem solid rgba(0, 0, 30, 0.2);
}

.lux-time-slider input[type='range']::-moz-range-thumb {
@apply bg-white h-5 w-[0.55rem] rounded-full border-tertiary cursor-pointer;
box-shadow: 0.1rem 0.1rem 0.1rem rgba(0, 0, 49, 0.2),
0 0 0.1rem rgba(0, 0, 75, 0.2);
border: 0.1rem solid rgba(0, 0, 30, 0.2);
-moz-appearance: none;
}

.lux-slidebar-thumb:focus,
.lux-slidebar-thumb:hover {
outline: 2px solid rgb(66, 66, 66);
box-shadow: 0.3rem 0.3rem 0.3rem rgba(0, 0, 49, 0.2),
0 0 0.3rem rgba(0, 0, 75, 0.2);
}

.lux-slidebar,
.lux-slidebar-track-full,
.lux-slidebar-track-selection {
@apply mt-2 mb-2 ml-0 mr-0 w-full h-1 bg-[#d3e5d7] rounded-sm appearance-none;
}

.lux-slidebar-fake {
@apply relative h-5;
}

.lux-slidebar-track {
@apply relative w-full;
}

.lux-slidebar-track-full {
@apply top-0 absolute w-full z-0;
}

.lux-slidebar-track-selection {
@apply top-0 absolute w-full z-[1];
background-color: #94ac9a;
}

.lux-time-slider-label {
@apply mr-1 min-w-[1.75rem] inline-block;
}

.lux-time-datepicker {
@apply border-[#767676] border-[1px] pl-1;
}

.lux-time-slidebar {
@apply mt-2 mb-2 ml-0 mr-0 w-full h-1 bg-[#d3e5d7] rounded-sm appearance-none cursor-pointer;
}
}

.fa-solid {
Expand Down
92 changes: 92 additions & 0 deletions src/components/common/slider-range/slider-range-thumb.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
<script setup lang="ts">
import { onUnmounted, Ref, computed, ref } from 'vue'
const props = defineProps<{
ariaLabel?: string
minValue: number
maxValue: number
totalSteps: number
selectedValue: number
}>()
const emit = defineEmits<{
(e: 'change', value: number): void
}>()
const sliderBarElement: Ref<HTMLElement | null> = ref(null)
const thumbElement: Ref<HTMLElement | null> = ref(null)
const thumbElementWidth = computed(() => thumbElement.value?.offsetWidth || 40)
const currentLeftOffset = computed(() => {
const offset = sliderBarElement.value?.offsetWidth
? (sliderBarElement.value?.offsetWidth * props.selectedValue) /
(props.totalSteps - 1)
: 0
return Math.round(offset - thumbElementWidth.value / 2)
})
const styleObject = computed(() => ({ left: `${currentLeftOffset.value}px` }))
let isDragging = false
onUnmounted(() => {
document.removeEventListener('mousemove', onMouseMove)
document.removeEventListener('mouseup', onMouseUp)
})
function onMoveThumb(value: number) {
emit('change', Math.min(Math.max(value, props.minValue), props.maxValue))
}
function onKeyDownLeft() {
onMoveThumb(props.selectedValue - 1)
}
function onKeyDownRight() {
onMoveThumb(props.selectedValue + 1)
}
function onMouseDown() {
isDragging = true
document.addEventListener('mousemove', onMouseMove)
document.addEventListener('mouseup', onMouseUp)
}
function onMouseMove(payload: MouseEvent) {
if (!isDragging) {
return
}
const value = sliderBarElement.value?.offsetWidth
? ((payload.clientX - thumbElementWidth.value / 2) *
(props.totalSteps - 1)) /
sliderBarElement.value?.offsetWidth
: 0
onMoveThumb(Math.floor(value))
}
function onMouseUp() {
isDragging = false
document.removeEventListener('mousemove', onMouseMove)
document.removeEventListener('mouseup', onMouseUp)
}
</script>

<template>
<div class="w-full" role="slider" ref="sliderBarElement">
<button
class="lux-slidebar-thumb"
ref="thumbElement"
:style="styleObject"
@keydown.space="onKeyDownRight"
@keydown.right="onKeyDownRight"
@keydown.left="onKeyDownLeft"
@keydown.delete="onKeyDownLeft"
@mousedown="onMouseDown"
@mousemove="onMouseMove"
@mouseup="onMouseUp"
:aria-label="ariaLabel"
:title="ariaLabel"
></button>
</div>
</template>
46 changes: 46 additions & 0 deletions src/components/common/slider-range/slider-range-track.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
<script setup lang="ts">
import { Ref, computed, ref } from 'vue'
const props = defineProps<{
selectedMinValue: number
selectedMaxValue: number
totalSteps: number
}>()
const sliderTrackElement: Ref<HTMLElement | null> = ref(null)
const sliderTrackSelectionElement: Ref<HTMLElement | null> = ref(null)
const currentLeftOffset = computed(() => {
const offsetLeft = sliderTrackElement.value?.offsetWidth
? (sliderTrackElement.value?.offsetWidth * props.selectedMinValue) /
(props.totalSteps - 1)
: 0
return Math.round(offsetLeft)
})
const currentWidthOffset = computed(() => {
let offsetWidth = 0
if (sliderTrackElement.value?.offsetWidth) {
const offsetRight =
(sliderTrackElement.value?.offsetWidth * props.selectedMaxValue) /
(props.totalSteps - 1)
offsetWidth = offsetRight - currentLeftOffset.value
}
return Math.round(offsetWidth)
})
const styleObject = computed(() => ({
left: `${currentLeftOffset.value}px`,
width: `${currentWidthOffset.value}px`,
}))
</script>

<template>
<div ref="sliderTrackElement" class="lux-slidebar-track">
<div
ref="sliderTrackSelectionElement"
class="lux-slidebar-track-selection"
:style="styleObject"
></div>
<div class="lux-slidebar-track-full"></div>
</div>
</template>
51 changes: 51 additions & 0 deletions src/components/common/slider-range/slider-range.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
<script setup lang="ts">
import SliderRangeThumb from './slider-range-thumb.vue'
import SliderRangeTrack from './slider-range-track.vue'
const props = defineProps<{
minValue: number
maxValue: number
selectedMinValue: number
selectedMaxValue: number
totalSteps: number
ariaLabelMin?: string
ariaLabelMax?: string
}>()
const emit = defineEmits<{
(e: 'change', min: number, max: number): void
}>()
function onChangeMin(value: number) {
emit('change', value, props.selectedMaxValue)
}
function onChangeMax(value: number) {
emit('change', props.selectedMinValue, value)
}
</script>

<template>
<div class="lux-slidebar-fake">
<slider-range-thumb
:ariaLabel="ariaLabelMin"
:minValue="minValue"
:maxValue="selectedMaxValue"
:selectedValue="selectedMinValue"
:totalSteps="totalSteps"
@change="onChangeMin"
/>
<slider-range-thumb
:totalSteps="totalSteps"
:ariaLabel="ariaLabelMax"
:minValue="selectedMinValue"
:maxValue="maxValue"
:selectedValue="selectedMaxValue"
@change="onChangeMax"
/>
<slider-range-track
:selectedMinValue="selectedMinValue"
:selectedMaxValue="selectedMaxValue"
:totalSteps="totalSteps"
/>
</div>
</template>
Loading

0 comments on commit a9a49bd

Please sign in to comment.