added calendar component
This commit is contained in:
98
ui/src/components/ui/calendar/Calendar.vue
Normal file
98
ui/src/components/ui/calendar/Calendar.vue
Normal file
@@ -0,0 +1,98 @@
|
|||||||
|
<script setup>
|
||||||
|
import { reactiveOmit } from "@vueuse/core";
|
||||||
|
import { CalendarRoot, useForwardPropsEmits } from "reka-ui";
|
||||||
|
import { cn } from "@/lib/utils";
|
||||||
|
import {
|
||||||
|
CalendarCell,
|
||||||
|
CalendarCellTrigger,
|
||||||
|
CalendarGrid,
|
||||||
|
CalendarGridBody,
|
||||||
|
CalendarGridHead,
|
||||||
|
CalendarGridRow,
|
||||||
|
CalendarHeadCell,
|
||||||
|
CalendarHeader,
|
||||||
|
CalendarHeading,
|
||||||
|
CalendarNextButton,
|
||||||
|
CalendarPrevButton,
|
||||||
|
} from ".";
|
||||||
|
|
||||||
|
const props = defineProps({
|
||||||
|
defaultValue: { type: null, required: false },
|
||||||
|
defaultPlaceholder: { type: null, required: false },
|
||||||
|
placeholder: { type: null, required: false },
|
||||||
|
pagedNavigation: { type: Boolean, required: false },
|
||||||
|
preventDeselect: { type: Boolean, required: false },
|
||||||
|
weekStartsOn: { type: Number, required: false },
|
||||||
|
weekdayFormat: { type: String, required: false },
|
||||||
|
calendarLabel: { type: String, required: false },
|
||||||
|
fixedWeeks: { type: Boolean, required: false },
|
||||||
|
maxValue: { type: null, required: false },
|
||||||
|
minValue: { type: null, required: false },
|
||||||
|
locale: { type: String, required: false },
|
||||||
|
numberOfMonths: { type: Number, required: false },
|
||||||
|
disabled: { type: Boolean, required: false },
|
||||||
|
readonly: { type: Boolean, required: false },
|
||||||
|
initialFocus: { type: Boolean, required: false },
|
||||||
|
isDateDisabled: { type: Function, required: false },
|
||||||
|
isDateUnavailable: { type: Function, required: false },
|
||||||
|
dir: { type: String, required: false },
|
||||||
|
nextPage: { type: Function, required: false },
|
||||||
|
prevPage: { type: Function, required: false },
|
||||||
|
modelValue: { type: null, required: false },
|
||||||
|
multiple: { type: Boolean, required: false },
|
||||||
|
disableDaysOutsideCurrentView: { type: Boolean, required: false },
|
||||||
|
asChild: { type: Boolean, required: false },
|
||||||
|
as: { type: [String, Object, Function], required: false },
|
||||||
|
class: { type: null, required: false },
|
||||||
|
});
|
||||||
|
const emits = defineEmits(["update:modelValue", "update:placeholder"]);
|
||||||
|
|
||||||
|
const delegatedProps = reactiveOmit(props, "class");
|
||||||
|
|
||||||
|
const forwarded = useForwardPropsEmits(delegatedProps, emits);
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<CalendarRoot
|
||||||
|
v-slot="{ grid, weekDays }"
|
||||||
|
data-slot="calendar"
|
||||||
|
:class="cn('p-3', props.class)"
|
||||||
|
v-bind="forwarded"
|
||||||
|
>
|
||||||
|
<CalendarHeader>
|
||||||
|
<CalendarHeading />
|
||||||
|
|
||||||
|
<div class="flex items-center gap-1">
|
||||||
|
<CalendarPrevButton />
|
||||||
|
<CalendarNextButton />
|
||||||
|
</div>
|
||||||
|
</CalendarHeader>
|
||||||
|
|
||||||
|
<div class="flex flex-col gap-y-4 mt-4 sm:flex-row sm:gap-x-4 sm:gap-y-0">
|
||||||
|
<CalendarGrid v-for="month in grid" :key="month.value.toString()">
|
||||||
|
<CalendarGridHead>
|
||||||
|
<CalendarGridRow>
|
||||||
|
<CalendarHeadCell v-for="day in weekDays" :key="day">
|
||||||
|
{{ day }}
|
||||||
|
</CalendarHeadCell>
|
||||||
|
</CalendarGridRow>
|
||||||
|
</CalendarGridHead>
|
||||||
|
<CalendarGridBody>
|
||||||
|
<CalendarGridRow
|
||||||
|
v-for="(weekDates, index) in month.rows"
|
||||||
|
:key="`weekDate-${index}`"
|
||||||
|
class="mt-2 w-full"
|
||||||
|
>
|
||||||
|
<CalendarCell
|
||||||
|
v-for="weekDate in weekDates"
|
||||||
|
:key="weekDate.toString()"
|
||||||
|
:date="weekDate"
|
||||||
|
>
|
||||||
|
<CalendarCellTrigger :day="weekDate" :month="month.value" />
|
||||||
|
</CalendarCell>
|
||||||
|
</CalendarGridRow>
|
||||||
|
</CalendarGridBody>
|
||||||
|
</CalendarGrid>
|
||||||
|
</div>
|
||||||
|
</CalendarRoot>
|
||||||
|
</template>
|
||||||
31
ui/src/components/ui/calendar/CalendarCell.vue
Normal file
31
ui/src/components/ui/calendar/CalendarCell.vue
Normal file
@@ -0,0 +1,31 @@
|
|||||||
|
<script setup>
|
||||||
|
import { reactiveOmit } from "@vueuse/core";
|
||||||
|
import { CalendarCell, useForwardProps } from "reka-ui";
|
||||||
|
import { cn } from "@/lib/utils";
|
||||||
|
|
||||||
|
const props = defineProps({
|
||||||
|
date: { type: null, required: true },
|
||||||
|
asChild: { type: Boolean, required: false },
|
||||||
|
as: { type: [String, Object, Function], required: false },
|
||||||
|
class: { type: null, required: false },
|
||||||
|
});
|
||||||
|
|
||||||
|
const delegatedProps = reactiveOmit(props, "class");
|
||||||
|
|
||||||
|
const forwardedProps = useForwardProps(delegatedProps);
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<CalendarCell
|
||||||
|
data-slot="calendar-cell"
|
||||||
|
:class="
|
||||||
|
cn(
|
||||||
|
'relative p-0 text-center text-sm focus-within:relative focus-within:z-20 [&:has([data-selected])]:rounded-md [&:has([data-selected])]:bg-accent',
|
||||||
|
props.class,
|
||||||
|
)
|
||||||
|
"
|
||||||
|
v-bind="forwardedProps"
|
||||||
|
>
|
||||||
|
<slot />
|
||||||
|
</CalendarCell>
|
||||||
|
</template>
|
||||||
43
ui/src/components/ui/calendar/CalendarCellTrigger.vue
Normal file
43
ui/src/components/ui/calendar/CalendarCellTrigger.vue
Normal file
@@ -0,0 +1,43 @@
|
|||||||
|
<script setup>
|
||||||
|
import { reactiveOmit } from "@vueuse/core";
|
||||||
|
import { CalendarCellTrigger, useForwardProps } from "reka-ui";
|
||||||
|
import { cn } from "@/lib/utils";
|
||||||
|
import { buttonVariants } from '@/components/ui/button';
|
||||||
|
|
||||||
|
const props = defineProps({
|
||||||
|
day: { type: null, required: true },
|
||||||
|
month: { type: null, required: true },
|
||||||
|
asChild: { type: Boolean, required: false },
|
||||||
|
as: { type: [String, Object, Function], required: false, default: "button" },
|
||||||
|
class: { type: null, required: false },
|
||||||
|
});
|
||||||
|
|
||||||
|
const delegatedProps = reactiveOmit(props, "class");
|
||||||
|
|
||||||
|
const forwardedProps = useForwardProps(delegatedProps);
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<CalendarCellTrigger
|
||||||
|
data-slot="calendar-cell-trigger"
|
||||||
|
:class="
|
||||||
|
cn(
|
||||||
|
buttonVariants({ variant: 'ghost' }),
|
||||||
|
'size-8 p-0 font-normal aria-selected:opacity-100 cursor-default',
|
||||||
|
'[&[data-today]:not([data-selected])]:bg-accent [&[data-today]:not([data-selected])]:text-accent-foreground',
|
||||||
|
// Selected
|
||||||
|
'data-[selected]:bg-primary data-[selected]:text-primary-foreground data-[selected]:opacity-100 data-[selected]:hover:bg-primary data-[selected]:hover:text-primary-foreground data-[selected]:focus:bg-primary data-[selected]:focus:text-primary-foreground',
|
||||||
|
// Disabled
|
||||||
|
'data-[disabled]:text-muted-foreground data-[disabled]:opacity-50',
|
||||||
|
// Unavailable
|
||||||
|
'data-[unavailable]:text-destructive-foreground data-[unavailable]:line-through',
|
||||||
|
// Outside months
|
||||||
|
'data-[outside-view]:text-muted-foreground',
|
||||||
|
props.class,
|
||||||
|
)
|
||||||
|
"
|
||||||
|
v-bind="forwardedProps"
|
||||||
|
>
|
||||||
|
<slot />
|
||||||
|
</CalendarCellTrigger>
|
||||||
|
</template>
|
||||||
25
ui/src/components/ui/calendar/CalendarGrid.vue
Normal file
25
ui/src/components/ui/calendar/CalendarGrid.vue
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
<script setup>
|
||||||
|
import { reactiveOmit } from "@vueuse/core";
|
||||||
|
import { CalendarGrid, useForwardProps } from "reka-ui";
|
||||||
|
import { cn } from "@/lib/utils";
|
||||||
|
|
||||||
|
const props = defineProps({
|
||||||
|
asChild: { type: Boolean, required: false },
|
||||||
|
as: { type: [String, Object, Function], required: false },
|
||||||
|
class: { type: null, required: false },
|
||||||
|
});
|
||||||
|
|
||||||
|
const delegatedProps = reactiveOmit(props, "class");
|
||||||
|
|
||||||
|
const forwardedProps = useForwardProps(delegatedProps);
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<CalendarGrid
|
||||||
|
data-slot="calendar-grid"
|
||||||
|
:class="cn('w-full border-collapse space-x-1', props.class)"
|
||||||
|
v-bind="forwardedProps"
|
||||||
|
>
|
||||||
|
<slot />
|
||||||
|
</CalendarGrid>
|
||||||
|
</template>
|
||||||
14
ui/src/components/ui/calendar/CalendarGridBody.vue
Normal file
14
ui/src/components/ui/calendar/CalendarGridBody.vue
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
<script setup>
|
||||||
|
import { CalendarGridBody } from "reka-ui";
|
||||||
|
|
||||||
|
const props = defineProps({
|
||||||
|
asChild: { type: Boolean, required: false },
|
||||||
|
as: { type: [String, Object, Function], required: false },
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<CalendarGridBody data-slot="calendar-grid-body" v-bind="props">
|
||||||
|
<slot />
|
||||||
|
</CalendarGridBody>
|
||||||
|
</template>
|
||||||
15
ui/src/components/ui/calendar/CalendarGridHead.vue
Normal file
15
ui/src/components/ui/calendar/CalendarGridHead.vue
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
<script setup>
|
||||||
|
import { CalendarGridHead } from "reka-ui";
|
||||||
|
|
||||||
|
const props = defineProps({
|
||||||
|
asChild: { type: Boolean, required: false },
|
||||||
|
as: { type: [String, Object, Function], required: false },
|
||||||
|
class: { type: null, required: false },
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<CalendarGridHead data-slot="calendar-grid-head" v-bind="props">
|
||||||
|
<slot />
|
||||||
|
</CalendarGridHead>
|
||||||
|
</template>
|
||||||
25
ui/src/components/ui/calendar/CalendarGridRow.vue
Normal file
25
ui/src/components/ui/calendar/CalendarGridRow.vue
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
<script setup>
|
||||||
|
import { reactiveOmit } from "@vueuse/core";
|
||||||
|
import { CalendarGridRow, useForwardProps } from "reka-ui";
|
||||||
|
import { cn } from "@/lib/utils";
|
||||||
|
|
||||||
|
const props = defineProps({
|
||||||
|
asChild: { type: Boolean, required: false },
|
||||||
|
as: { type: [String, Object, Function], required: false },
|
||||||
|
class: { type: null, required: false },
|
||||||
|
});
|
||||||
|
|
||||||
|
const delegatedProps = reactiveOmit(props, "class");
|
||||||
|
|
||||||
|
const forwardedProps = useForwardProps(delegatedProps);
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<CalendarGridRow
|
||||||
|
data-slot="calendar-grid-row"
|
||||||
|
:class="cn('flex', props.class)"
|
||||||
|
v-bind="forwardedProps"
|
||||||
|
>
|
||||||
|
<slot />
|
||||||
|
</CalendarGridRow>
|
||||||
|
</template>
|
||||||
30
ui/src/components/ui/calendar/CalendarHeadCell.vue
Normal file
30
ui/src/components/ui/calendar/CalendarHeadCell.vue
Normal file
@@ -0,0 +1,30 @@
|
|||||||
|
<script setup>
|
||||||
|
import { reactiveOmit } from "@vueuse/core";
|
||||||
|
import { CalendarHeadCell, useForwardProps } from "reka-ui";
|
||||||
|
import { cn } from "@/lib/utils";
|
||||||
|
|
||||||
|
const props = defineProps({
|
||||||
|
asChild: { type: Boolean, required: false },
|
||||||
|
as: { type: [String, Object, Function], required: false },
|
||||||
|
class: { type: null, required: false },
|
||||||
|
});
|
||||||
|
|
||||||
|
const delegatedProps = reactiveOmit(props, "class");
|
||||||
|
|
||||||
|
const forwardedProps = useForwardProps(delegatedProps);
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<CalendarHeadCell
|
||||||
|
data-slot="calendar-head-cell"
|
||||||
|
:class="
|
||||||
|
cn(
|
||||||
|
'text-muted-foreground rounded-md w-8 font-normal text-[0.8rem]',
|
||||||
|
props.class,
|
||||||
|
)
|
||||||
|
"
|
||||||
|
v-bind="forwardedProps"
|
||||||
|
>
|
||||||
|
<slot />
|
||||||
|
</CalendarHeadCell>
|
||||||
|
</template>
|
||||||
27
ui/src/components/ui/calendar/CalendarHeader.vue
Normal file
27
ui/src/components/ui/calendar/CalendarHeader.vue
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
<script setup>
|
||||||
|
import { reactiveOmit } from "@vueuse/core";
|
||||||
|
import { CalendarHeader, useForwardProps } from "reka-ui";
|
||||||
|
import { cn } from "@/lib/utils";
|
||||||
|
|
||||||
|
const props = defineProps({
|
||||||
|
asChild: { type: Boolean, required: false },
|
||||||
|
as: { type: [String, Object, Function], required: false },
|
||||||
|
class: { type: null, required: false },
|
||||||
|
});
|
||||||
|
|
||||||
|
const delegatedProps = reactiveOmit(props, "class");
|
||||||
|
|
||||||
|
const forwardedProps = useForwardProps(delegatedProps);
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<CalendarHeader
|
||||||
|
data-slot="calendar-header"
|
||||||
|
:class="
|
||||||
|
cn('flex justify-center pt-1 relative items-center w-full', props.class)
|
||||||
|
"
|
||||||
|
v-bind="forwardedProps"
|
||||||
|
>
|
||||||
|
<slot />
|
||||||
|
</CalendarHeader>
|
||||||
|
</template>
|
||||||
30
ui/src/components/ui/calendar/CalendarHeading.vue
Normal file
30
ui/src/components/ui/calendar/CalendarHeading.vue
Normal file
@@ -0,0 +1,30 @@
|
|||||||
|
<script setup>
|
||||||
|
import { reactiveOmit } from "@vueuse/core";
|
||||||
|
import { CalendarHeading, useForwardProps } from "reka-ui";
|
||||||
|
import { cn } from "@/lib/utils";
|
||||||
|
|
||||||
|
const props = defineProps({
|
||||||
|
asChild: { type: Boolean, required: false },
|
||||||
|
as: { type: [String, Object, Function], required: false },
|
||||||
|
class: { type: null, required: false },
|
||||||
|
});
|
||||||
|
|
||||||
|
defineSlots();
|
||||||
|
|
||||||
|
const delegatedProps = reactiveOmit(props, "class");
|
||||||
|
|
||||||
|
const forwardedProps = useForwardProps(delegatedProps);
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<CalendarHeading
|
||||||
|
v-slot="{ headingValue }"
|
||||||
|
data-slot="calendar-heading"
|
||||||
|
:class="cn('text-sm font-medium', props.class)"
|
||||||
|
v-bind="forwardedProps"
|
||||||
|
>
|
||||||
|
<slot :heading-value>
|
||||||
|
{{ headingValue }}
|
||||||
|
</slot>
|
||||||
|
</CalendarHeading>
|
||||||
|
</template>
|
||||||
37
ui/src/components/ui/calendar/CalendarNextButton.vue
Normal file
37
ui/src/components/ui/calendar/CalendarNextButton.vue
Normal file
@@ -0,0 +1,37 @@
|
|||||||
|
<script setup>
|
||||||
|
import { reactiveOmit } from "@vueuse/core";
|
||||||
|
import { ChevronRight } from "lucide-vue-next";
|
||||||
|
import { CalendarNext, useForwardProps } from "reka-ui";
|
||||||
|
import { cn } from "@/lib/utils";
|
||||||
|
import { buttonVariants } from '@/components/ui/button';
|
||||||
|
|
||||||
|
const props = defineProps({
|
||||||
|
nextPage: { type: Function, required: false },
|
||||||
|
asChild: { type: Boolean, required: false },
|
||||||
|
as: { type: [String, Object, Function], required: false },
|
||||||
|
class: { type: null, required: false },
|
||||||
|
});
|
||||||
|
|
||||||
|
const delegatedProps = reactiveOmit(props, "class");
|
||||||
|
|
||||||
|
const forwardedProps = useForwardProps(delegatedProps);
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<CalendarNext
|
||||||
|
data-slot="calendar-next-button"
|
||||||
|
:class="
|
||||||
|
cn(
|
||||||
|
buttonVariants({ variant: 'outline' }),
|
||||||
|
'absolute right-1',
|
||||||
|
'size-7 bg-transparent p-0 opacity-50 hover:opacity-100',
|
||||||
|
props.class,
|
||||||
|
)
|
||||||
|
"
|
||||||
|
v-bind="forwardedProps"
|
||||||
|
>
|
||||||
|
<slot>
|
||||||
|
<ChevronRight class="size-4" />
|
||||||
|
</slot>
|
||||||
|
</CalendarNext>
|
||||||
|
</template>
|
||||||
37
ui/src/components/ui/calendar/CalendarPrevButton.vue
Normal file
37
ui/src/components/ui/calendar/CalendarPrevButton.vue
Normal file
@@ -0,0 +1,37 @@
|
|||||||
|
<script setup>
|
||||||
|
import { reactiveOmit } from "@vueuse/core";
|
||||||
|
import { ChevronLeft } from "lucide-vue-next";
|
||||||
|
import { CalendarPrev, useForwardProps } from "reka-ui";
|
||||||
|
import { cn } from "@/lib/utils";
|
||||||
|
import { buttonVariants } from '@/components/ui/button';
|
||||||
|
|
||||||
|
const props = defineProps({
|
||||||
|
prevPage: { type: Function, required: false },
|
||||||
|
asChild: { type: Boolean, required: false },
|
||||||
|
as: { type: [String, Object, Function], required: false },
|
||||||
|
class: { type: null, required: false },
|
||||||
|
});
|
||||||
|
|
||||||
|
const delegatedProps = reactiveOmit(props, "class");
|
||||||
|
|
||||||
|
const forwardedProps = useForwardProps(delegatedProps);
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<CalendarPrev
|
||||||
|
data-slot="calendar-prev-button"
|
||||||
|
:class="
|
||||||
|
cn(
|
||||||
|
buttonVariants({ variant: 'outline' }),
|
||||||
|
'absolute left-1',
|
||||||
|
'size-7 bg-transparent p-0 opacity-50 hover:opacity-100',
|
||||||
|
props.class,
|
||||||
|
)
|
||||||
|
"
|
||||||
|
v-bind="forwardedProps"
|
||||||
|
>
|
||||||
|
<slot>
|
||||||
|
<ChevronLeft class="size-4" />
|
||||||
|
</slot>
|
||||||
|
</CalendarPrev>
|
||||||
|
</template>
|
||||||
12
ui/src/components/ui/calendar/index.js
Normal file
12
ui/src/components/ui/calendar/index.js
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
export { default as Calendar } from "./Calendar.vue";
|
||||||
|
export { default as CalendarCell } from "./CalendarCell.vue";
|
||||||
|
export { default as CalendarCellTrigger } from "./CalendarCellTrigger.vue";
|
||||||
|
export { default as CalendarGrid } from "./CalendarGrid.vue";
|
||||||
|
export { default as CalendarGridBody } from "./CalendarGridBody.vue";
|
||||||
|
export { default as CalendarGridHead } from "./CalendarGridHead.vue";
|
||||||
|
export { default as CalendarGridRow } from "./CalendarGridRow.vue";
|
||||||
|
export { default as CalendarHeadCell } from "./CalendarHeadCell.vue";
|
||||||
|
export { default as CalendarHeader } from "./CalendarHeader.vue";
|
||||||
|
export { default as CalendarHeading } from "./CalendarHeading.vue";
|
||||||
|
export { default as CalendarNextButton } from "./CalendarNextButton.vue";
|
||||||
|
export { default as CalendarPrevButton } from "./CalendarPrevButton.vue";
|
||||||
Reference in New Issue
Block a user