Skip to content

Commit bb4f42c

Browse files
feat(Calendar): add variant prop (#5138)
Co-authored-by: Benjamin Canac <[email protected]>
1 parent c8b01c9 commit bb4f42c

File tree

7 files changed

+4107
-2595
lines changed

7 files changed

+4107
-2595
lines changed

docs/content/docs/2.components/calendar.md

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -101,6 +101,28 @@ props:
101101
---
102102
::
103103

104+
### Variant
105+
106+
Use the `variant` prop to change the variant of the calendar.
107+
108+
::component-code
109+
---
110+
cast:
111+
defaultValue: DateRange
112+
hide:
113+
- range
114+
- defaultValue
115+
- defaultValue.start
116+
- defaultValue.end
117+
props:
118+
variant: subtle
119+
range: true
120+
defaultValue:
121+
start: [2022, 2, 3]
122+
end: [2022, 2, 20]
123+
---
124+
::
125+
104126
### Size
105127

106128
Use the `size` prop to change the size of the calendar.

playgrounds/nuxt/app/pages/components/calendar.vue

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,10 +10,12 @@ const rangeValue = shallowRef({
1010
})
1111
1212
const colors = Object.keys(theme.variants.color)
13+
const variants = Object.keys(theme.variants.variant)
1314
const sizes = Object.keys(theme.variants.size)
1415
1516
const attrs = reactive({
1617
color: [theme.defaultVariants.color],
18+
variant: [theme.defaultVariants.variant],
1719
size: [theme.defaultVariants.size]
1820
})
1921
@@ -24,6 +26,7 @@ const range = ref(false)
2426
<template>
2527
<Navbar>
2628
<USelect v-model="attrs.color" :items="colors" multiple />
29+
<USelect v-model="attrs.variant" :items="variants" multiple />
2730
<USelect v-model="attrs.size" :items="sizes" multiple />
2831
<USwitch v-model="multiple" label="Multiple" />
2932
<USwitch v-model="range" label="Range" />

src/runtime/components/Calendar.vue

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,10 @@ export interface CalendarProps<R extends boolean = false, M extends boolean = fa
7676
* @defaultValue 'primary'
7777
*/
7878
color?: Calendar['variants']['color']
79+
/**
80+
* @defaultValue 'solid'
81+
*/
82+
variant?: Calendar['variants']['variant']
7983
/**
8084
* @defaultValue 'md'
8185
*/
@@ -126,7 +130,7 @@ defineSlots<CalendarSlots>()
126130
const { code: locale, dir, t } = useLocale()
127131
const appConfig = useAppConfig() as Calendar['AppConfig']
128132
129-
const rootProps = useForwardPropsEmits(reactiveOmit(props, 'range', 'modelValue', 'defaultValue', 'color', 'size', 'monthControls', 'yearControls', 'class', 'ui'), emits)
133+
const rootProps = useForwardPropsEmits(reactiveOmit(props, 'range', 'modelValue', 'defaultValue', 'color', 'variant', 'size', 'monthControls', 'yearControls', 'class', 'ui'), emits)
130134
131135
const nextYearIcon = computed(() => props.nextYearIcon || (dir.value === 'rtl' ? appConfig.ui.icons.chevronDoubleLeft : appConfig.ui.icons.chevronDoubleRight))
132136
const nextMonthIcon = computed(() => props.nextMonthIcon || (dir.value === 'rtl' ? appConfig.ui.icons.chevronLeft : appConfig.ui.icons.chevronRight))
@@ -135,6 +139,7 @@ const prevMonthIcon = computed(() => props.prevMonthIcon || (dir.value === 'rtl'
135139
136140
const ui = computed(() => tv({ extend: tv(theme), ...(appConfig.ui?.calendar || {}) })({
137141
color: props.color,
142+
variant: props.variant,
138143
size: props.size
139144
}))
140145

src/theme/calendar.ts

Lines changed: 60 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -12,19 +12,25 @@ export default (options: Required<ModuleOptions>) => ({
1212
gridBody: 'grid',
1313
headCell: 'rounded-md',
1414
cell: 'relative text-center',
15-
cellTrigger: ['m-0.5 relative flex items-center justify-center rounded-full whitespace-nowrap focus-visible:ring-2 focus:outline-none data-disabled:text-muted data-unavailable:line-through data-unavailable:text-muted data-unavailable:pointer-events-none data-[selected]:text-inverted data-today:font-semibold data-[outside-view]:text-muted', options.theme.transitions && 'transition']
15+
cellTrigger: ['m-0.5 relative flex items-center justify-center rounded-full whitespace-nowrap focus-visible:ring-2 focus:outline-none data-disabled:text-muted data-unavailable:line-through data-unavailable:text-muted data-unavailable:pointer-events-none data-today:font-semibold data-[outside-view]:text-muted', options.theme.transitions && 'transition']
1616
},
1717
variants: {
1818
color: {
1919
...Object.fromEntries((options.theme.colors || []).map((color: string) => [color, {
2020
headCell: `text-${color}`,
21-
cellTrigger: `focus-visible:ring-${color} data-[selected]:bg-${color} data-today:not-data-[selected]:text-${color} data-[highlighted]:bg-${color}/20 hover:not-data-[selected]:bg-${color}/20`
21+
cellTrigger: `focus-visible:ring-${color}`
2222
}])),
2323
neutral: {
2424
headCell: 'text-highlighted',
25-
cellTrigger: 'focus-visible:ring-inverted data-[selected]:bg-inverted data-today:not-data-[selected]:text-highlighted data-[highlighted]:bg-inverted/20 hover:not-data-[selected]:bg-inverted/10'
25+
cellTrigger: 'focus-visible:ring-inverted'
2626
}
2727
},
28+
variant: {
29+
solid: '',
30+
outline: '',
31+
soft: '',
32+
subtle: ''
33+
},
2834
size: {
2935
xs: {
3036
heading: 'text-xs',
@@ -57,8 +63,58 @@ export default (options: Required<ModuleOptions>) => ({
5763
}
5864
}
5965
},
66+
compoundVariants: [...(options.theme.colors || []).map((color: string) => ({
67+
color,
68+
variant: 'solid',
69+
class: {
70+
cellTrigger: `data-[selected]:bg-${color} data-[selected]:text-inverted data-today:not-data-[selected]:text-${color} data-[highlighted]:bg-${color}/20 hover:not-data-[selected]:bg-${color}/20`
71+
}
72+
})), ...(options.theme.colors || []).map((color: string) => ({
73+
color,
74+
variant: 'outline',
75+
class: {
76+
cellTrigger: `data-[selected]:ring data-[selected]:ring-inset data-[selected]:ring-${color}/50 data-[selected]:text-${color} data-today:not-data-[selected]:text-${color} data-[highlighted]:bg-${color}/10 hover:not-data-[selected]:bg-${color}/10`
77+
}
78+
})), ...(options.theme.colors || []).map((color: string) => ({
79+
color,
80+
variant: 'soft',
81+
class: {
82+
cellTrigger: `data-[selected]:bg-${color}/10 data-[selected]:text-${color} data-today:not-data-[selected]:text-${color} data-[highlighted]:bg-${color}/20 hover:not-data-[selected]:bg-${color}/20`
83+
}
84+
})), ...(options.theme.colors || []).map((color: string) => ({
85+
color,
86+
variant: 'subtle',
87+
class: {
88+
cellTrigger: `data-[selected]:bg-${color}/10 data-[selected]:text-${color} data-[selected]:ring data-[selected]:ring-inset data-[selected]:ring-${color}/25 data-today:not-data-[selected]:text-${color} data-[highlighted]:bg-${color}/20 hover:not-data-[selected]:bg-${color}/20`
89+
}
90+
})), {
91+
color: 'neutral',
92+
variant: 'solid',
93+
class: {
94+
cellTrigger: 'data-[selected]:bg-inverted data-[selected]:text-inverted data-today:not-data-[selected]:text-highlighted data-[highlighted]:bg-inverted/20 hover:not-data-[selected]:bg-inverted/10'
95+
}
96+
}, {
97+
color: 'neutral',
98+
variant: 'outline',
99+
class: {
100+
cellTrigger: 'data-[selected]:ring data-[selected]:ring-inset data-[selected]:ring-accented data-[selected]:text-default data-[selected]:bg-default data-today:not-data-[selected]:text-highlighted data-[highlighted]:bg-inverted/10 hover:not-data-[selected]:bg-inverted/10'
101+
}
102+
}, {
103+
color: 'neutral',
104+
variant: 'soft',
105+
class: {
106+
cellTrigger: 'data-[selected]:bg-elevated data-[selected]:text-default data-today:not-data-[selected]:text-highlighted data-[highlighted]:bg-inverted/20 hover:not-data-[selected]:bg-inverted/10'
107+
}
108+
}, {
109+
color: 'neutral',
110+
variant: 'subtle',
111+
class: {
112+
cellTrigger: 'data-[selected]:bg-elevated data-[selected]:text-default data-[selected]:ring data-[selected]:ring-inset data-[selected]:ring-accented data-today:not-data-[selected]:text-highlighted data-[highlighted]:bg-inverted/20 hover:not-data-[selected]:bg-inverted/10'
113+
}
114+
}],
60115
defaultVariants: {
61116
size: 'md',
62-
color: 'primary'
117+
color: 'primary',
118+
variant: 'solid'
63119
}
64120
})

test/components/Calendar.spec.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import { CalendarDate } from '@internationalized/date'
88

99
describe('Calendar', () => {
1010
const sizes = Object.keys(theme.variants.size) as any
11+
const variants = Object.keys(theme.variants.variant) as any
1112
const date = new Date('2025-01-01')
1213

1314
vi.setSystemTime(date)
@@ -37,6 +38,7 @@ describe('Calendar', () => {
3738
['without monthControls', { props: { monthControls: false } }],
3839
['without yearControls', { props: { yearControls: false } }],
3940
...sizes.map((size: string) => [`with size ${size}`, { props: { size } }]),
41+
...variants.map((variant: string) => [`with variant ${variant}`, { props: { variant, defaultValue: new CalendarDate(2025, 1, 15) } }]),
4042
['with color neutral', { props: { color: 'neutral' } }],
4143
['with as', { props: { as: 'section' } }],
4244
['with class', { props: { class: 'max-w-sm' } }],

0 commit comments

Comments
 (0)