mirror of
https://github.com/vrcx-team/VRCX.git
synced 2026-04-06 00:32:02 +02:00
add range calendar component
This commit is contained in:
@@ -42,8 +42,8 @@
|
||||
"@kamiya4047/eslint-plugin-pretty-import": "^0.1.6",
|
||||
"@sentry/vite-plugin": "^4.9.1",
|
||||
"@sentry/vue": "^10.38.0",
|
||||
"@sigma/node-border": "^3.0.0",
|
||||
"@sigma/edge-curve": "^3.1.0",
|
||||
"@sigma/node-border": "^3.0.0",
|
||||
"@tailwindcss/vite": "^4.1.18",
|
||||
"@tanstack/vue-table": "^8.21.3",
|
||||
"@tanstack/vue-virtual": "^3.13.18",
|
||||
@@ -85,6 +85,7 @@
|
||||
"tw-animate-css": "^1.4.0",
|
||||
"vee-validate": "^4.15.1",
|
||||
"vite": "^7.3.1",
|
||||
"vitest": "^3.2.4",
|
||||
"vue": "^3.5.28",
|
||||
"vue-i18n": "^11.2.8",
|
||||
"vue-json-pretty": "^2.6.0",
|
||||
@@ -92,7 +93,6 @@
|
||||
"vue-router": "^4.6.4",
|
||||
"vue-showdown": "^4.2.0",
|
||||
"vue-sonner": "^2.0.9",
|
||||
"vitest": "^3.2.4",
|
||||
"worker-timers": "^8.0.30",
|
||||
"yargs": "^18.0.0",
|
||||
"zod": "^3.25.76"
|
||||
|
||||
102
src/components/ui/range-calendar/RangeCalendar.vue
Normal file
102
src/components/ui/range-calendar/RangeCalendar.vue
Normal file
@@ -0,0 +1,102 @@
|
||||
<script setup>
|
||||
import { RangeCalendarRoot, useForwardPropsEmits } from 'reka-ui';
|
||||
import { cn } from '@/lib/utils';
|
||||
import { reactiveOmit } from '@vueuse/core';
|
||||
|
||||
import {
|
||||
RangeCalendarCell,
|
||||
RangeCalendarCellTrigger,
|
||||
RangeCalendarGrid,
|
||||
RangeCalendarGridBody,
|
||||
RangeCalendarGridHead,
|
||||
RangeCalendarGridRow,
|
||||
RangeCalendarHeadCell,
|
||||
RangeCalendarHeader,
|
||||
RangeCalendarHeading,
|
||||
RangeCalendarNextButton,
|
||||
RangeCalendarPrevButton
|
||||
} from '.';
|
||||
|
||||
const props = defineProps({
|
||||
defaultPlaceholder: { type: null, required: false },
|
||||
defaultValue: { type: Object, required: false },
|
||||
modelValue: { type: [Object, null], required: false },
|
||||
placeholder: { type: null, required: false },
|
||||
allowNonContiguousRanges: { type: Boolean, required: false },
|
||||
pagedNavigation: { type: Boolean, required: false },
|
||||
preventDeselect: { type: Boolean, required: false },
|
||||
maximumDays: { type: Number, 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 },
|
||||
isDateHighlightable: { type: Function, required: false },
|
||||
dir: { type: String, required: false },
|
||||
nextPage: { type: Function, required: false },
|
||||
prevPage: { type: Function, required: false },
|
||||
disableDaysOutsideCurrentView: { type: Boolean, required: false },
|
||||
fixedDate: { type: String, required: false },
|
||||
asChild: { type: Boolean, required: false },
|
||||
as: { type: null, required: false },
|
||||
class: { type: null, required: false }
|
||||
});
|
||||
|
||||
const emits = defineEmits([
|
||||
'update:modelValue',
|
||||
'update:validModelValue',
|
||||
'update:placeholder',
|
||||
'update:startValue'
|
||||
]);
|
||||
|
||||
const delegatedProps = reactiveOmit(props, 'class');
|
||||
|
||||
const forwarded = useForwardPropsEmits(delegatedProps, emits);
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<RangeCalendarRoot
|
||||
v-slot="{ grid, weekDays }"
|
||||
data-slot="range-calendar"
|
||||
:class="cn('p-3', props.class)"
|
||||
v-bind="forwarded">
|
||||
<RangeCalendarHeader>
|
||||
<RangeCalendarHeading />
|
||||
|
||||
<div class="flex items-center gap-1">
|
||||
<RangeCalendarPrevButton />
|
||||
<RangeCalendarNextButton />
|
||||
</div>
|
||||
</RangeCalendarHeader>
|
||||
|
||||
<div class="flex flex-col gap-y-4 mt-4 sm:flex-row sm:gap-x-4 sm:gap-y-0">
|
||||
<RangeCalendarGrid v-for="month in grid" :key="month.value.toString()">
|
||||
<RangeCalendarGridHead>
|
||||
<RangeCalendarGridRow>
|
||||
<RangeCalendarHeadCell v-for="day in weekDays" :key="day">
|
||||
{{ day }}
|
||||
</RangeCalendarHeadCell>
|
||||
</RangeCalendarGridRow>
|
||||
</RangeCalendarGridHead>
|
||||
<RangeCalendarGridBody>
|
||||
<RangeCalendarGridRow
|
||||
v-for="(weekDates, index) in month.rows"
|
||||
:key="`weekDate-${index}`"
|
||||
class="mt-2 w-full">
|
||||
<RangeCalendarCell v-for="weekDate in weekDates" :key="weekDate.toString()" :date="weekDate">
|
||||
<RangeCalendarCellTrigger :day="weekDate" :month="month.value" />
|
||||
</RangeCalendarCell>
|
||||
</RangeCalendarGridRow>
|
||||
</RangeCalendarGridBody>
|
||||
</RangeCalendarGrid>
|
||||
</div>
|
||||
</RangeCalendarRoot>
|
||||
</template>
|
||||
30
src/components/ui/range-calendar/RangeCalendarCell.vue
Normal file
30
src/components/ui/range-calendar/RangeCalendarCell.vue
Normal file
@@ -0,0 +1,30 @@
|
||||
<script setup>
|
||||
import { RangeCalendarCell, useForwardProps } from 'reka-ui';
|
||||
import { cn } from '@/lib/utils';
|
||||
import { reactiveOmit } from '@vueuse/core';
|
||||
|
||||
const props = defineProps({
|
||||
date: { type: null, required: true },
|
||||
asChild: { type: Boolean, required: false },
|
||||
as: { type: null, required: false },
|
||||
class: { type: null, required: false }
|
||||
});
|
||||
|
||||
const delegatedProps = reactiveOmit(props, 'class');
|
||||
|
||||
const forwardedProps = useForwardProps(delegatedProps);
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<RangeCalendarCell
|
||||
data-slot="range-calendar-cell"
|
||||
:class="
|
||||
cn(
|
||||
'relative p-0 text-center text-sm focus-within:relative focus-within:z-20 [&:has([data-selected])]:bg-accent first:[&:has([data-selected])]:rounded-l-md last:[&:has([data-selected])]:rounded-r-md [&:has([data-selected][data-selection-end])]:rounded-r-md [&:has([data-selected][data-selection-start])]:rounded-l-md',
|
||||
props.class
|
||||
)
|
||||
"
|
||||
v-bind="forwardedProps">
|
||||
<slot />
|
||||
</RangeCalendarCell>
|
||||
</template>
|
||||
@@ -0,0 +1,44 @@
|
||||
<script setup>
|
||||
import { RangeCalendarCellTrigger, useForwardProps } from 'reka-ui';
|
||||
import { buttonVariants } from '@/components/ui/button';
|
||||
import { cn } from '@/lib/utils';
|
||||
import { reactiveOmit } from '@vueuse/core';
|
||||
|
||||
const props = defineProps({
|
||||
day: { type: null, required: true },
|
||||
month: { type: null, required: true },
|
||||
asChild: { type: Boolean, required: false },
|
||||
as: { type: null, required: false, default: 'button' },
|
||||
class: { type: null, required: false }
|
||||
});
|
||||
|
||||
const delegatedProps = reactiveOmit(props, 'class');
|
||||
|
||||
const forwardedProps = useForwardProps(delegatedProps);
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<RangeCalendarCellTrigger
|
||||
data-slot="range-calendar-trigger"
|
||||
:class="
|
||||
cn(
|
||||
buttonVariants({ variant: 'ghost' }),
|
||||
'h-8 w-8 p-0 font-normal data-[selected]:opacity-100',
|
||||
'[&[data-today]:not([data-selected])]:bg-accent [&[data-today]:not([data-selected])]:text-accent-foreground',
|
||||
// Selection Start
|
||||
'data-[selection-start]:bg-primary data-[selection-start]:text-primary-foreground data-[selection-start]:hover:bg-primary data-[selection-start]:hover:text-primary-foreground data-[selection-start]:focus:bg-primary data-[selection-start]:focus:text-primary-foreground',
|
||||
// Selection End
|
||||
'data-[selection-end]:bg-primary data-[selection-end]:text-primary-foreground data-[selection-end]:hover:bg-primary data-[selection-end]:hover:text-primary-foreground data-[selection-end]:focus:bg-primary data-[selection-end]:focus:text-primary-foreground',
|
||||
// Outside months
|
||||
'data-[outside-view]:text-muted-foreground',
|
||||
// Disabled
|
||||
'data-[disabled]:text-muted-foreground data-[disabled]:opacity-50',
|
||||
// Unavailable
|
||||
'data-[unavailable]:text-destructive-foreground data-[unavailable]:line-through',
|
||||
props.class
|
||||
)
|
||||
"
|
||||
v-bind="forwardedProps">
|
||||
<slot />
|
||||
</RangeCalendarCellTrigger>
|
||||
</template>
|
||||
24
src/components/ui/range-calendar/RangeCalendarGrid.vue
Normal file
24
src/components/ui/range-calendar/RangeCalendarGrid.vue
Normal file
@@ -0,0 +1,24 @@
|
||||
<script setup>
|
||||
import { RangeCalendarGrid, useForwardProps } from 'reka-ui';
|
||||
import { cn } from '@/lib/utils';
|
||||
import { reactiveOmit } from '@vueuse/core';
|
||||
|
||||
const props = defineProps({
|
||||
asChild: { type: Boolean, required: false },
|
||||
as: { type: null, required: false },
|
||||
class: { type: null, required: false }
|
||||
});
|
||||
|
||||
const delegatedProps = reactiveOmit(props, 'class');
|
||||
|
||||
const forwardedProps = useForwardProps(delegatedProps);
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<RangeCalendarGrid
|
||||
data-slot="range-calendar-grid"
|
||||
:class="cn('w-full border-collapse space-x-1', props.class)"
|
||||
v-bind="forwardedProps">
|
||||
<slot />
|
||||
</RangeCalendarGrid>
|
||||
</template>
|
||||
14
src/components/ui/range-calendar/RangeCalendarGridBody.vue
Normal file
14
src/components/ui/range-calendar/RangeCalendarGridBody.vue
Normal file
@@ -0,0 +1,14 @@
|
||||
<script setup>
|
||||
import { RangeCalendarGridBody } from 'reka-ui';
|
||||
|
||||
const props = defineProps({
|
||||
asChild: { type: Boolean, required: false },
|
||||
as: { type: null, required: false }
|
||||
});
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<RangeCalendarGridBody data-slot="range-calendar-grid-body" v-bind="props">
|
||||
<slot />
|
||||
</RangeCalendarGridBody>
|
||||
</template>
|
||||
14
src/components/ui/range-calendar/RangeCalendarGridHead.vue
Normal file
14
src/components/ui/range-calendar/RangeCalendarGridHead.vue
Normal file
@@ -0,0 +1,14 @@
|
||||
<script setup>
|
||||
import { RangeCalendarGridHead } from 'reka-ui';
|
||||
|
||||
const props = defineProps({
|
||||
asChild: { type: Boolean, required: false },
|
||||
as: { type: null, required: false }
|
||||
});
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<RangeCalendarGridHead data-slot="range-calendar-grid-head" v-bind="props">
|
||||
<slot />
|
||||
</RangeCalendarGridHead>
|
||||
</template>
|
||||
21
src/components/ui/range-calendar/RangeCalendarGridRow.vue
Normal file
21
src/components/ui/range-calendar/RangeCalendarGridRow.vue
Normal file
@@ -0,0 +1,21 @@
|
||||
<script setup>
|
||||
import { RangeCalendarGridRow, useForwardProps } from 'reka-ui';
|
||||
import { cn } from '@/lib/utils';
|
||||
import { reactiveOmit } from '@vueuse/core';
|
||||
|
||||
const props = defineProps({
|
||||
asChild: { type: Boolean, required: false },
|
||||
as: { type: null, required: false },
|
||||
class: { type: null, required: false }
|
||||
});
|
||||
|
||||
const delegatedProps = reactiveOmit(props, 'class');
|
||||
|
||||
const forwardedProps = useForwardProps(delegatedProps);
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<RangeCalendarGridRow data-slot="range-calendar-grid-row" :class="cn('flex', props.class)" v-bind="forwardedProps">
|
||||
<slot />
|
||||
</RangeCalendarGridRow>
|
||||
</template>
|
||||
24
src/components/ui/range-calendar/RangeCalendarHeadCell.vue
Normal file
24
src/components/ui/range-calendar/RangeCalendarHeadCell.vue
Normal file
@@ -0,0 +1,24 @@
|
||||
<script setup>
|
||||
import { RangeCalendarHeadCell, useForwardProps } from 'reka-ui';
|
||||
import { cn } from '@/lib/utils';
|
||||
import { reactiveOmit } from '@vueuse/core';
|
||||
|
||||
const props = defineProps({
|
||||
asChild: { type: Boolean, required: false },
|
||||
as: { type: null, required: false },
|
||||
class: { type: null, required: false }
|
||||
});
|
||||
|
||||
const delegatedProps = reactiveOmit(props, 'class');
|
||||
|
||||
const forwardedProps = useForwardProps(delegatedProps);
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<RangeCalendarHeadCell
|
||||
data-slot="range-calendar-head-cell"
|
||||
:class="cn('w-8 rounded-md text-[0.8rem] font-normal text-muted-foreground', props.class)"
|
||||
v-bind="forwardedProps">
|
||||
<slot />
|
||||
</RangeCalendarHeadCell>
|
||||
</template>
|
||||
24
src/components/ui/range-calendar/RangeCalendarHeader.vue
Normal file
24
src/components/ui/range-calendar/RangeCalendarHeader.vue
Normal file
@@ -0,0 +1,24 @@
|
||||
<script setup>
|
||||
import { RangeCalendarHeader, useForwardProps } from 'reka-ui';
|
||||
import { cn } from '@/lib/utils';
|
||||
import { reactiveOmit } from '@vueuse/core';
|
||||
|
||||
const props = defineProps({
|
||||
asChild: { type: Boolean, required: false },
|
||||
as: { type: null, required: false },
|
||||
class: { type: null, required: false }
|
||||
});
|
||||
|
||||
const delegatedProps = reactiveOmit(props, 'class');
|
||||
|
||||
const forwardedProps = useForwardProps(delegatedProps);
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<RangeCalendarHeader
|
||||
data-slot="range-calendar-header"
|
||||
:class="cn('flex justify-center pt-1 relative items-center w-full', props.class)"
|
||||
v-bind="forwardedProps">
|
||||
<slot />
|
||||
</RangeCalendarHeader>
|
||||
</template>
|
||||
29
src/components/ui/range-calendar/RangeCalendarHeading.vue
Normal file
29
src/components/ui/range-calendar/RangeCalendarHeading.vue
Normal file
@@ -0,0 +1,29 @@
|
||||
<script setup>
|
||||
import { RangeCalendarHeading, useForwardProps } from 'reka-ui';
|
||||
import { cn } from '@/lib/utils';
|
||||
import { reactiveOmit } from '@vueuse/core';
|
||||
|
||||
const props = defineProps({
|
||||
asChild: { type: Boolean, required: false },
|
||||
as: { type: null, required: false },
|
||||
class: { type: null, required: false }
|
||||
});
|
||||
|
||||
defineSlots();
|
||||
|
||||
const delegatedProps = reactiveOmit(props, 'class');
|
||||
|
||||
const forwardedProps = useForwardProps(delegatedProps);
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<RangeCalendarHeading
|
||||
v-slot="{ headingValue }"
|
||||
data-slot="range-calendar-heading"
|
||||
:class="cn('text-sm font-medium', props.class)"
|
||||
v-bind="forwardedProps">
|
||||
<slot :heading-value>
|
||||
{{ headingValue }}
|
||||
</slot>
|
||||
</RangeCalendarHeading>
|
||||
</template>
|
||||
36
src/components/ui/range-calendar/RangeCalendarNextButton.vue
Normal file
36
src/components/ui/range-calendar/RangeCalendarNextButton.vue
Normal file
@@ -0,0 +1,36 @@
|
||||
<script setup>
|
||||
import { RangeCalendarNext, useForwardProps } from 'reka-ui';
|
||||
import { ChevronRight } from 'lucide-vue-next';
|
||||
import { buttonVariants } from '@/components/ui/button';
|
||||
import { cn } from '@/lib/utils';
|
||||
import { reactiveOmit } from '@vueuse/core';
|
||||
|
||||
const props = defineProps({
|
||||
nextPage: { type: Function, required: false },
|
||||
asChild: { type: Boolean, required: false },
|
||||
as: { type: null, required: false },
|
||||
class: { type: null, required: false }
|
||||
});
|
||||
|
||||
const delegatedProps = reactiveOmit(props, 'class');
|
||||
|
||||
const forwardedProps = useForwardProps(delegatedProps);
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<RangeCalendarNext
|
||||
data-slot="range-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>
|
||||
</RangeCalendarNext>
|
||||
</template>
|
||||
36
src/components/ui/range-calendar/RangeCalendarPrevButton.vue
Normal file
36
src/components/ui/range-calendar/RangeCalendarPrevButton.vue
Normal file
@@ -0,0 +1,36 @@
|
||||
<script setup>
|
||||
import { RangeCalendarPrev, useForwardProps } from 'reka-ui';
|
||||
import { ChevronLeft } from 'lucide-vue-next';
|
||||
import { buttonVariants } from '@/components/ui/button';
|
||||
import { cn } from '@/lib/utils';
|
||||
import { reactiveOmit } from '@vueuse/core';
|
||||
|
||||
const props = defineProps({
|
||||
prevPage: { type: Function, required: false },
|
||||
asChild: { type: Boolean, required: false },
|
||||
as: { type: null, required: false },
|
||||
class: { type: null, required: false }
|
||||
});
|
||||
|
||||
const delegatedProps = reactiveOmit(props, 'class');
|
||||
|
||||
const forwardedProps = useForwardProps(delegatedProps);
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<RangeCalendarPrev
|
||||
data-slot="range-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>
|
||||
</RangeCalendarPrev>
|
||||
</template>
|
||||
12
src/components/ui/range-calendar/index.js
Normal file
12
src/components/ui/range-calendar/index.js
Normal file
@@ -0,0 +1,12 @@
|
||||
export { default as RangeCalendar } from './RangeCalendar.vue';
|
||||
export { default as RangeCalendarCell } from './RangeCalendarCell.vue';
|
||||
export { default as RangeCalendarCellTrigger } from './RangeCalendarCellTrigger.vue';
|
||||
export { default as RangeCalendarGrid } from './RangeCalendarGrid.vue';
|
||||
export { default as RangeCalendarGridBody } from './RangeCalendarGridBody.vue';
|
||||
export { default as RangeCalendarGridHead } from './RangeCalendarGridHead.vue';
|
||||
export { default as RangeCalendarGridRow } from './RangeCalendarGridRow.vue';
|
||||
export { default as RangeCalendarHeadCell } from './RangeCalendarHeadCell.vue';
|
||||
export { default as RangeCalendarHeader } from './RangeCalendarHeader.vue';
|
||||
export { default as RangeCalendarHeading } from './RangeCalendarHeading.vue';
|
||||
export { default as RangeCalendarNextButton } from './RangeCalendarNextButton.vue';
|
||||
export { default as RangeCalendarPrevButton } from './RangeCalendarPrevButton.vue';
|
||||
Reference in New Issue
Block a user