New Dashboard
This commit is contained in:
129
src/components/ui/datetime-picker/DateTimePicker.svelte
Normal file
129
src/components/ui/datetime-picker/DateTimePicker.svelte
Normal file
@@ -0,0 +1,129 @@
|
||||
<!--
|
||||
- This file is a part of the SteamWar software.
|
||||
-
|
||||
- Copyright (C) 2025 SteamWar.de-Serverteam
|
||||
-
|
||||
- This program is free software: you can redistribute it and/or modify
|
||||
- it under the terms of the GNU Affero General Public License as published by
|
||||
- the Free Software Foundation, either version 3 of the License, or
|
||||
- (at your option) any later version.
|
||||
-
|
||||
- This program is distributed in the hope that it will be useful,
|
||||
- but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
- GNU Affero General Public License for more details.
|
||||
-
|
||||
- You should have received a copy of the GNU Affero General Public License
|
||||
- along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
-->
|
||||
|
||||
<script lang="ts">
|
||||
import { Calendar } from "@components/ui/calendar";
|
||||
import { Button } from "@components/ui/button";
|
||||
import { Popover, PopoverContent, PopoverTrigger } from "$lib/components/ui/popover";
|
||||
import { ScrollArea } from "$lib/components/ui/scroll-area";
|
||||
import { CalendarIcon } from "lucide-svelte";
|
||||
import { cn } from "@components/utils";
|
||||
import type {ZonedDateTime} from "@internationalized/date";
|
||||
|
||||
let {
|
||||
value = $bindable(),
|
||||
onChange
|
||||
}: {
|
||||
value: ZonedDateTime
|
||||
onChange?: ((date: ZonedDateTime | undefined) => void) | undefined
|
||||
} = $props();
|
||||
|
||||
let isOpen = $state(false);
|
||||
|
||||
const hours = Array.from({ length: 24 }, (_, i) => i);
|
||||
|
||||
function handleDateSelect(selectedDate: ZonedDateTime) {
|
||||
if (selectedDate) {
|
||||
value = selectedDate;
|
||||
if (onChange) {
|
||||
onChange(value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function handleTimeChange(type: "hour" | "minute", change: number) {
|
||||
if (change !== undefined) {
|
||||
if (type === "hour") {
|
||||
value = value.set({ hour: change });
|
||||
} else if (type === "minute") {
|
||||
value = value.set({ minute: change });
|
||||
}
|
||||
if (onChange) {
|
||||
onChange(value);
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<Popover bind:open={isOpen}>
|
||||
<PopoverTrigger>
|
||||
<Button
|
||||
variant="outline"
|
||||
class={cn(
|
||||
"w-full justify-start text-left font-normal",
|
||||
!value && "text-muted-foreground"
|
||||
)}
|
||||
>
|
||||
<CalendarIcon class="mr-2 h-4 w-4" />
|
||||
{#if value}
|
||||
{new Intl.DateTimeFormat("de-DE", {
|
||||
day: "2-digit",
|
||||
month: "2-digit",
|
||||
year: "numeric",
|
||||
hour: "2-digit",
|
||||
minute: "2-digit",
|
||||
}).format(value.toDate())}
|
||||
{:else}
|
||||
<span>DD.MM.YYYY, hh:mm</span>
|
||||
{/if}
|
||||
</Button>
|
||||
</PopoverTrigger>
|
||||
|
||||
<PopoverContent class="w-auto p-0">
|
||||
<div class="sm:flex">
|
||||
<Calendar
|
||||
mode="single"
|
||||
bind:value
|
||||
onValueChange={(date) => handleDateSelect(date)}
|
||||
initialFocus
|
||||
/>
|
||||
<div class="flex flex-col sm:flex-row sm:h-[300px] divide-y sm:divide-y-0 sm:divide-x">
|
||||
<ScrollArea class="w-64 sm:w-auto">
|
||||
<div class="flex sm:flex-col p-2">
|
||||
{#each [...hours].reverse() as hour}
|
||||
<Button
|
||||
size="icon"
|
||||
variant={value && value.hour === hour ? "default" : "ghost"}
|
||||
class="sm:w-full shrink-0 aspect-square"
|
||||
onclick={() => handleTimeChange("hour", hour)}
|
||||
>
|
||||
{hour}
|
||||
</Button>
|
||||
{/each}
|
||||
</div>
|
||||
</ScrollArea>
|
||||
|
||||
<ScrollArea class="w-64 sm:w-auto">
|
||||
<div class="flex sm:flex-col p-2">
|
||||
{#each Array.from({ length: 60 }, (_, i) => i) as minute}
|
||||
<Button
|
||||
size="icon"
|
||||
variant={value && value.minute === minute ? "default" : "ghost"}
|
||||
class="sm:w-full shrink-0 aspect-square"
|
||||
onclick={() => handleTimeChange("minute", minute)}
|
||||
>
|
||||
{minute.toString().padStart(2, '0')}
|
||||
</Button>
|
||||
{/each}
|
||||
</div>
|
||||
</ScrollArea>
|
||||
</div>
|
||||
</div>
|
||||
</PopoverContent>
|
||||
</Popover>
|
||||
Reference in New Issue
Block a user