mirror of
https://gitlab.dit.htwk-leipzig.de/htwk-software/htwkalender-pwa.git
synced 2025-08-07 04:09:17 +02:00
feat:#5 add slider for time range selection
This commit is contained in:
@@ -14,6 +14,7 @@ import InputSwitch from "primevue/inputswitch";
|
|||||||
import Card from "primevue/card";
|
import Card from "primevue/card";
|
||||||
import DataView from "primevue/dataview";
|
import DataView from "primevue/dataview";
|
||||||
import Dialog from "primevue/dialog";
|
import Dialog from "primevue/dialog";
|
||||||
|
import Slider from 'primevue/slider';
|
||||||
import ToggleButton from "primevue/togglebutton";
|
import ToggleButton from "primevue/togglebutton";
|
||||||
import "primeicons/primeicons.css";
|
import "primeicons/primeicons.css";
|
||||||
import "primeflex/primeflex.css";
|
import "primeflex/primeflex.css";
|
||||||
@@ -58,6 +59,7 @@ app.component("InputText", InputText);
|
|||||||
app.component("InputSwitch", InputSwitch);
|
app.component("InputSwitch", InputSwitch);
|
||||||
app.component("Card", Card);
|
app.component("Card", Card);
|
||||||
app.component("DataView", DataView);
|
app.component("DataView", DataView);
|
||||||
|
app.component("Slider", Slider);
|
||||||
app.component("ToggleButton", ToggleButton);
|
app.component("ToggleButton", ToggleButton);
|
||||||
app.component("SpeedDial", SpeedDial);
|
app.component("SpeedDial", SpeedDial);
|
||||||
app.component("TabView", TabView);
|
app.component("TabView", TabView);
|
||||||
|
@@ -1,4 +1,5 @@
|
|||||||
<template>
|
<template>
|
||||||
|
|
||||||
<DynamicPage
|
<DynamicPage
|
||||||
:hide-content="availableRooms.length === 0"
|
:hide-content="availableRooms.length === 0"
|
||||||
:headline="$t('freeRooms.freeRooms')"
|
:headline="$t('freeRooms.freeRooms')"
|
||||||
@@ -22,28 +23,52 @@
|
|||||||
touchUI
|
touchUI
|
||||||
/>
|
/>
|
||||||
<div class="break"/>
|
<div class="break"/>
|
||||||
<Calendar
|
<Calendar
|
||||||
v-model="start"
|
v-model="start"
|
||||||
type="time"
|
v-if="mobilePage"
|
||||||
placeholder="start"
|
@update:model-value="() => {timeRange[0] = start.getHours() * 60 + start.getMinutes();}"
|
||||||
time-only
|
type="time"
|
||||||
hour-format="24"
|
placeholder="start"
|
||||||
date-format="HH:mm"
|
time-only
|
||||||
:class="[{'p-invalid':isLater}, flexSpecs]"
|
hour-format="24"
|
||||||
panel-class="min-w-min"
|
date-format="HH:mm"
|
||||||
touchUI
|
:class="[{'p-invalid':isLater}, flexSpecs]"
|
||||||
/>
|
panel-class="min-w-min"
|
||||||
<Calendar
|
touchUI
|
||||||
v-model="end"
|
/>
|
||||||
type="time"
|
<Calendar
|
||||||
time-only
|
v-model="end"
|
||||||
hour-format="24"
|
v-if="mobilePage"
|
||||||
placeholder="end"
|
@update:model-value="() => {timeRange[1] = end.getHours() * 60 + end.getMinutes();}"
|
||||||
date-format="HH:mm"
|
type="time"
|
||||||
:class="[{'p-invalid':isLater}, flexSpecs]"
|
time-only
|
||||||
panel-class="min-w-min"
|
hour-format="24"
|
||||||
touchUI
|
placeholder="end"
|
||||||
/>
|
date-format="HH:mm"
|
||||||
|
:class="[{'p-invalid':isLater}, flexSpecs]"
|
||||||
|
panel-class="min-w-min"
|
||||||
|
touchUI
|
||||||
|
/>
|
||||||
|
<div
|
||||||
|
class="flex-grow-1 relative mb-2"
|
||||||
|
:class="flexSpecs"
|
||||||
|
v-if="!mobilePage"
|
||||||
|
>
|
||||||
|
<Tag :value="formatTime(start)" class="opacity-0 pointer-events-none text-xl lg:text-base"/>
|
||||||
|
<Tag :value="formatTime(start)" class="absolute time-tag text-xl lg:text-base" :style="{left: timeToPercentString(timeRange[0])}" :class="startMoved.value? 'moved': ''" />
|
||||||
|
<Tag :value="formatTime(end)" class="absolute time-tag text-xl lg:text-base" :style="{left: timeToPercentString(timeRange[1])}" :class="endMoved.value? 'moved': ''" />
|
||||||
|
</div>
|
||||||
|
<div class="break"/>
|
||||||
|
<Slider
|
||||||
|
v-model="timeRange"
|
||||||
|
@change="updateTimeRange"
|
||||||
|
range
|
||||||
|
:class="flexSpecs"
|
||||||
|
:min="0"
|
||||||
|
:max="24*60"
|
||||||
|
:step="5"
|
||||||
|
v-if="!mobilePage"
|
||||||
|
/>
|
||||||
</template>
|
</template>
|
||||||
<template #content>
|
<template #content>
|
||||||
<DataTable v-model:filters="filters" :value="availableRooms" data-key="id" filter-display="row" paginator :rows="10" :global-filter-fields="['room']" >
|
<DataTable v-model:filters="filters" :value="availableRooms" data-key="id" filter-display="row" paginator :rows="10" :global-filter-fields="['room']" >
|
||||||
@@ -58,18 +83,73 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { computed, Ref, ref } from "vue";
|
import { computed, inject, Ref, ref } from "vue";
|
||||||
import DynamicPage from "@/view/DynamicPage.vue";
|
import DynamicPage from "@/view/DynamicPage.vue";
|
||||||
import { requestFreeRooms } from "@/api/requestFreeRooms.ts";
|
import { requestFreeRooms } from "@/api/requestFreeRooms.ts";
|
||||||
import { FilterMatchMode } from "primevue/api";
|
import { FilterMatchMode } from "primevue/api";
|
||||||
|
import { padStart } from "@fullcalendar/core/internal";
|
||||||
|
|
||||||
|
const mobilePage = inject("mobilePage") as Ref<boolean>;
|
||||||
const filters = ref({
|
const filters = ref({
|
||||||
room: { value: null, matchMode: FilterMatchMode.CONTAINS },
|
room: { value: null, matchMode: FilterMatchMode.CONTAINS },
|
||||||
});
|
});
|
||||||
|
|
||||||
const date: Ref<Date> = ref(new Date(Date.now()));
|
const date: Ref<Date> = ref(new Date(Date.now()));
|
||||||
const start: Ref<Date> = ref(new Date(Date.now()));
|
const start: Ref<Date> = ref(new Date(0));
|
||||||
const end: Ref<Date> = ref(new Date(Date.now() + 3600000));
|
const end: Ref<Date> = ref(new Date(0));
|
||||||
|
|
||||||
|
class Moved {
|
||||||
|
value: boolean = false;
|
||||||
|
timeout: NodeJS.Timeout | null = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
const startMoved: Ref<Moved> = ref(new Moved());
|
||||||
|
const endMoved: Ref<Moved> = ref(new Moved());
|
||||||
|
|
||||||
|
start.value.setHours(new Date().getHours());
|
||||||
|
end.value.setHours(Math.min(new Date().getHours() + 3, 23));
|
||||||
|
if (end.value.getHours() === 23) {
|
||||||
|
start.value.setHours(22);
|
||||||
|
end.value.setMinutes(55);
|
||||||
|
}
|
||||||
|
|
||||||
|
const timeRange: Ref<number[]> = ref([
|
||||||
|
start.value.getHours() * 60 + start.value.getMinutes(),
|
||||||
|
end.value.getHours() * 60 + end.value.getMinutes()
|
||||||
|
]);
|
||||||
|
|
||||||
|
function buttonMovedTimeout(time: Number, timeDate: Date, moved: Ref<Moved>): void {
|
||||||
|
if (time !== timeDate.getHours() * 60 + timeDate.getMinutes()) {
|
||||||
|
if (moved.value.timeout !== null) {
|
||||||
|
clearTimeout(moved.value.timeout);
|
||||||
|
}
|
||||||
|
moved.value.value = true;
|
||||||
|
moved.value.timeout = setTimeout(() => {
|
||||||
|
moved.value.value = false;
|
||||||
|
}, 1000);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function updateTimeRange(): void {
|
||||||
|
buttonMovedTimeout(timeRange.value[0], start.value, startMoved);
|
||||||
|
buttonMovedTimeout(timeRange.value[1], end.value, endMoved);
|
||||||
|
|
||||||
|
if (timeRange.value[0] > timeRange.value[1]) {
|
||||||
|
timeRange.value[1] = timeRange.value[0];
|
||||||
|
}
|
||||||
|
start.value.setHours(Math.floor(timeRange.value[0] / 60));
|
||||||
|
start.value.setMinutes(timeRange.value[0] % 60);
|
||||||
|
end.value.setHours(Math.floor(timeRange.value[1] / 60));
|
||||||
|
end.value.setMinutes(timeRange.value[1] % 60);
|
||||||
|
}
|
||||||
|
|
||||||
|
function formatTime(time: Date): string {
|
||||||
|
return padStart(time.getHours().toString(), 2) + ":" + padStart(time.getMinutes().toString(), 2);
|
||||||
|
}
|
||||||
|
|
||||||
|
function timeToPercentString(time: Number): string {
|
||||||
|
return `${Number(time) * 100 / (24 * 60)}%`;
|
||||||
|
}
|
||||||
|
|
||||||
async function loadFreeRooms(): Promise<void> {
|
async function loadFreeRooms(): Promise<void> {
|
||||||
availableRooms.value = [];
|
availableRooms.value = [];
|
||||||
@@ -99,4 +179,31 @@ const availableRooms: Ref<{id: number, room: string}[]> = ref([]);
|
|||||||
flex-basis: 100%;
|
flex-basis: 100%;
|
||||||
height: 0;
|
height: 0;
|
||||||
}
|
}
|
||||||
</style>
|
|
||||||
|
.time-tag {
|
||||||
|
transform: translateX(-50%);
|
||||||
|
border: 2px solid var(--primary-color);
|
||||||
|
background-color: var(--primary-color);
|
||||||
|
opacity: 0.6;
|
||||||
|
transition: opacity 0.2s ease-in-out;
|
||||||
|
}
|
||||||
|
|
||||||
|
.time-tag.moved {
|
||||||
|
opacity: 1;
|
||||||
|
z-index: 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
.time-tag::before {
|
||||||
|
content: "";
|
||||||
|
position: absolute;
|
||||||
|
bottom: -0.8rem;
|
||||||
|
left: 50%;
|
||||||
|
width: 0;
|
||||||
|
height: 0;
|
||||||
|
transform: translateY(-100%);
|
||||||
|
border-style: solid;
|
||||||
|
border-width: 0.8rem 0.5rem 0 0.5rem;
|
||||||
|
border-color:var(--primary-color) transparent transparent transparent;
|
||||||
|
transform: translateX(-50%);
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
Reference in New Issue
Block a user