calendar
Appointment calendar
Appointment calendar
Загрузка превью…
src/calendar/c-calendar-19.vue
<script setup lang="ts">
import { ref } from "vue"
import { Button } from "@/components/ui/button"
import { Card, CardContent, CardFooter, CardHeader, CardTitle } from "@/components/ui/card"
import { ScrollArea } from "@/components/ui/scroll-area"
import { Calendar as CCalendar } from "@/components/ui/calendar"
const fixedDate = new Date(2026, 0, 15)
const date = ref<Date | undefined>(fixedDate)
const selectedTime = ref<string | null>("10:00")
const timeSlots = Array.from({ length: 37 }, (_, i) => {
const totalMinutes = i * 15
const hour = Math.floor(totalMinutes / 60) + 9
const minute = totalMinutes % 60
return `${hour.toString().padStart(2, "0")}:${minute.toString().padStart(2, "0")}`
})
const bookedDates = [0, 1, 2].map((i) => new Date(fixedDate.getFullYear(), fixedDate.getMonth(), fixedDate.getDate() + i))
function isBooked(d: Date): boolean {
return bookedDates.some((b) => b.getFullYear() === d.getFullYear() && b.getMonth() === d.getMonth() && b.getDate() === d.getDate())
}
const bookingText = new Intl.DateTimeFormat("en-US", { weekday: "long", day: "numeric", month: "long", timeZone: "UTC" }).format(
new Date(Date.UTC(fixedDate.getFullYear(), fixedDate.getMonth(), fixedDate.getDate()))
)
</script>
<template>
<Card class="gap-0 p-0">
<CardHeader class="flex h-max items-center justify-start border-b px-4! py-3!">
<CardTitle>Book your appointment</CardTitle>
</CardHeader>
<CardContent class="relative p-0 md:pr-48">
<div class="p-4">
<CCalendar mode="single" v-model="date" :month="fixedDate" :disabled-fn="isBooked" />
</div>
<div class="inset-y-0 right-0 flex w-full flex-col gap-4 border-t max-md:h-60 md:absolute md:w-48 md:border-t-0 md:border-l">
<ScrollArea class="h-full">
<div class="flex flex-col gap-2 p-4">
<Button
v-for="time in timeSlots"
:key="time"
:variant="selectedTime === time ? 'default' : 'outline'"
class="w-full shadow-none"
@click="selectedTime = time"
>
{{ time }}
</Button>
</div>
</ScrollArea>
</div>
</CardContent>
<CardFooter class="flex flex-col gap-4 border-t px-4 py-3! md:flex-row">
<div class="flex max-w-64 items-center gap-2 text-sm">
<template v-if="date && selectedTime">
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="size-4 shrink-0">
<circle cx="12" cy="12" r="10" />
<path d="m9 12 2 2 4-4" />
</svg>
<span class="text-sm">
Your meeting is booked for
<span class="font-medium"> {{ bookingText }} </span>
at <span class="font-medium">{{ selectedTime }}</span>
</span>
</template>
<template v-else>Select a date and time for your meeting.</template>
</div>
<Button :disabled="!date || !selectedTime" class="w-full md:ml-auto md:w-auto" variant="outline">Confirm</Button>
</CardFooter>
</Card>
</template>
Установка
npx shadcn-vue@latest add https://revueui.rootapi.dev/r/c-calendar-19.jsonЗависимости реестра
Источник: порт из ReUI (Keenthemes, MIT)