Skip to content

Commit 8f4d686

Browse files
authored
feat(MonthPicker,YearPicker): add month and year pickers (#2434)
* feat(MonthPicker,YearPicker): add month and year pickers * docs: update date picker example * fix(RangePickers): handle null modelValue * fix(pickers): address review feedback * fix: range highlight sticks + height jumps * fix: address remaining review feedback * fix(range-picker): resync controlled range * fix(core): resolve picker review regressions * refactor: extract calendar fixes + clean up comments * fix(pickers): resolve coderabbit findings * fix(calendar): forward props safely
1 parent 78e762e commit 8f4d686

File tree

132 files changed

+11004
-22
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

132 files changed

+11004
-22
lines changed

docs/.vitepress/config.ts

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -193,6 +193,22 @@ export default defineConfig({
193193
text: `Time Field ${BadgeHTML('Alpha', true)}`,
194194
link: '/docs/components/time-field',
195195
},
196+
{
197+
text: `Month Picker ${BadgeHTML('Alpha', true)}`,
198+
link: '/docs/components/month-picker',
199+
},
200+
{
201+
text: `Month Range Picker ${BadgeHTML('Alpha', true)}`,
202+
link: '/docs/components/month-range-picker',
203+
},
204+
{
205+
text: `Year Picker ${BadgeHTML('Alpha', true)}`,
206+
link: '/docs/components/year-picker',
207+
},
208+
{
209+
text: `Year Range Picker ${BadgeHTML('Alpha', true)}`,
210+
link: '/docs/components/year-range-picker',
211+
},
196212
],
197213
},
198214
{
@@ -318,6 +334,10 @@ export default defineConfig({
318334
text: 'Date Picker Selection',
319335
link: '/examples/date-picker-selection',
320336
},
337+
{
338+
text: 'Date Picker View Switching',
339+
link: '/examples/date-picker-view-switching',
340+
},
321341
],
322342
},
323343
{
Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
<script setup lang="ts">
2+
import { Icon } from '@iconify/vue'
3+
import { CalendarDate } from '@internationalized/date'
4+
import { MonthPickerCell, MonthPickerCellTrigger, MonthPickerGrid, MonthPickerGridBody, MonthPickerGridRow, MonthPickerHeader, MonthPickerHeading, MonthPickerNext, MonthPickerPrev, MonthPickerRoot } from 'reka-ui'
5+
import './styles.css'
6+
7+
const date = new CalendarDate(2024, 10, 1)
8+
</script>
9+
10+
<template>
11+
<MonthPickerRoot
12+
v-slot="{ grid }"
13+
:default-value="date"
14+
class="MonthPicker"
15+
>
16+
<MonthPickerHeader class="MonthPickerHeader">
17+
<MonthPickerPrev class="MonthPickerNavButton">
18+
<Icon
19+
icon="radix-icons:chevron-left"
20+
class="Icon"
21+
/>
22+
</MonthPickerPrev>
23+
<MonthPickerHeading class="MonthPickerHeading" />
24+
<MonthPickerNext class="MonthPickerNavButton">
25+
<Icon
26+
icon="radix-icons:chevron-right"
27+
class="Icon"
28+
/>
29+
</MonthPickerNext>
30+
</MonthPickerHeader>
31+
<div class="MonthPickerWrapper">
32+
<MonthPickerGrid class="MonthPickerGrid">
33+
<MonthPickerGridBody class="MonthPickerGridWrapper">
34+
<MonthPickerGridRow
35+
v-for="(months, index) in grid.rows"
36+
:key="`month-${index}`"
37+
class="MonthPickerGridRow"
38+
>
39+
<MonthPickerCell
40+
v-for="month in months"
41+
:key="month.toString()"
42+
:date="month"
43+
class="MonthPickerCell"
44+
>
45+
<MonthPickerCellTrigger
46+
:month="month"
47+
class="MonthPickerCellTrigger"
48+
/>
49+
</MonthPickerCell>
50+
</MonthPickerGridRow>
51+
</MonthPickerGridBody>
52+
</MonthPickerGrid>
53+
</div>
54+
</MonthPickerRoot>
55+
</template>
Lines changed: 134 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,134 @@
1+
@import '@radix-ui/colors/black-alpha.css';
2+
@import '@radix-ui/colors/grass.css';
3+
4+
.Icon {
5+
width: 1rem;
6+
height: 1rem;
7+
}
8+
9+
.MonthPicker {
10+
margin-top: 1.5rem;
11+
border-radius: 0.75rem;
12+
border: 1px solid #e5e5e5;
13+
background-color: #ffffff;
14+
box-shadow: 0 1px 2px 0 rgba(0, 0, 0, 0.05);
15+
padding: 1rem;
16+
}
17+
18+
.MonthPickerHeader {
19+
display: flex;
20+
justify-content: space-between;
21+
align-items: center;
22+
}
23+
24+
.MonthPickerNavButton {
25+
display: inline-flex;
26+
justify-content: center;
27+
align-items: center;
28+
width: 2rem;
29+
height: 2rem;
30+
color: #000000;
31+
background-color: transparent;
32+
cursor: pointer;
33+
border-radius: 0.375rem;
34+
}
35+
36+
.MonthPickerNavButton:hover {
37+
background-color: #fafaf9;
38+
}
39+
40+
.MonthPickerNavButton:focus {
41+
box-shadow: 0 0 0 2px #000000;
42+
}
43+
44+
.MonthPickerHeading {
45+
font-size: 0.875rem;
46+
font-weight: 500;
47+
color: #000000;
48+
}
49+
50+
.MonthPickerWrapper {
51+
padding-top: 1rem;
52+
}
53+
54+
.MonthPickerGrid {
55+
width: 100%;
56+
user-select: none;
57+
}
58+
59+
.MonthPickerGridWrapper {
60+
display: grid;
61+
gap: 0.25rem 0;
62+
}
63+
64+
.MonthPickerGridRow {
65+
display: grid;
66+
grid-template-columns: repeat(4, minmax(0, 1fr));
67+
gap: 0.25rem;
68+
}
69+
70+
.MonthPickerCell {
71+
position: relative;
72+
text-align: center;
73+
}
74+
75+
.MonthPickerCellTrigger {
76+
position: relative;
77+
display: flex;
78+
justify-content: center;
79+
align-items: center;
80+
width: 3rem;
81+
height: 3rem;
82+
border-radius: 0.5rem;
83+
font-size: 0.875rem;
84+
font-weight: 400;
85+
color: #000000;
86+
background-color: transparent;
87+
outline: none;
88+
cursor: pointer;
89+
border: none;
90+
}
91+
92+
.MonthPickerCellTrigger:hover {
93+
background-color: var(--grass-5);
94+
}
95+
96+
.MonthPickerCellTrigger:focus {
97+
box-shadow: 0 0 0 2px #000000;
98+
}
99+
100+
.MonthPickerCellTrigger[data-disabled] {
101+
color: rgba(0, 0, 0, 0.3);
102+
pointer-events: none;
103+
}
104+
105+
.MonthPickerCellTrigger[data-selected] {
106+
background-color: var(--grass-10);
107+
color: #ffffff;
108+
font-weight: 500;
109+
}
110+
111+
.MonthPickerCellTrigger[data-unavailable] {
112+
color: rgba(0, 0, 0, 0.3);
113+
text-decoration: line-through;
114+
pointer-events: none;
115+
}
116+
117+
.MonthPickerCellTrigger::before {
118+
content: '';
119+
position: absolute;
120+
top: 0.25rem;
121+
width: 0.25rem;
122+
height: 0.25rem;
123+
border-radius: 9999px;
124+
display: none;
125+
}
126+
127+
.MonthPickerCellTrigger[data-today]::before {
128+
display: block;
129+
background-color: var(--grass-9);
130+
}
131+
132+
.MonthPickerCellTrigger[data-selected]::before {
133+
background-color: #ffffff;
134+
}
Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
<script setup lang="ts">
2+
import { Icon } from '@iconify/vue'
3+
import { CalendarDate } from '@internationalized/date'
4+
import { MonthPickerCell, MonthPickerCellTrigger, MonthPickerGrid, MonthPickerGridBody, MonthPickerGridRow, MonthPickerHeader, MonthPickerHeading, MonthPickerNext, MonthPickerPrev, MonthPickerRoot } from 'reka-ui'
5+
6+
const date = new CalendarDate(2024, 10, 1)
7+
</script>
8+
9+
<template>
10+
<MonthPickerRoot
11+
v-slot="{ grid }"
12+
:default-value="date"
13+
class="mt-6 rounded-xl bg-white p-4 shadow-sm border"
14+
>
15+
<MonthPickerHeader class="flex items-center justify-between">
16+
<MonthPickerPrev
17+
class="inline-flex items-center cursor-pointer text-black justify-center rounded-md bg-transparent size-8 hover:bg-stone-50 active:scale-98 active:transition-all focus:shadow-[0_0_0_2px] focus:shadow-green10"
18+
>
19+
<Icon
20+
icon="radix-icons:chevron-left"
21+
class="size-4"
22+
/>
23+
</MonthPickerPrev>
24+
<MonthPickerHeading class="text-sm text-black font-medium" />
25+
<MonthPickerNext
26+
class="inline-flex items-center cursor-pointer justify-center text-black rounded-md bg-transparent size-8 hover:bg-stone-50 active:scale-98 active:transition-all focus:shadow-[0_0_0_2px] focus:shadow-green10"
27+
>
28+
<Icon
29+
icon="radix-icons:chevron-right"
30+
class="size-4"
31+
/>
32+
</MonthPickerNext>
33+
</MonthPickerHeader>
34+
<div class="pt-4">
35+
<MonthPickerGrid class="w-full border-collapse select-none">
36+
<MonthPickerGridBody class="grid gap-y-1">
37+
<MonthPickerGridRow
38+
v-for="(months, index) in grid.rows"
39+
:key="`month-${index}`"
40+
class="grid grid-cols-4 gap-x-1"
41+
>
42+
<MonthPickerCell
43+
v-for="month in months"
44+
:key="month.toString()"
45+
:date="month"
46+
class="relative text-center text-sm"
47+
>
48+
<MonthPickerCellTrigger
49+
:month="month"
50+
class="relative flex items-center justify-center whitespace-nowrap text-sm font-normal text-black size-12 rounded-lg outline-none focus:shadow-[0_0_0_2px] focus:shadow-green10 data-[disabled]:text-black/30 data-[selected]:!bg-green10 data-[selected]:text-white hover:bg-green5 data-[unavailable]:pointer-events-none data-[unavailable]:text-black/30 data-[unavailable]:line-through before:absolute before:top-1 before:hidden before:rounded-full before:size-1 before:bg-white data-[today]:before:block data-[today]:before:bg-green9"
51+
/>
52+
</MonthPickerCell>
53+
</MonthPickerGridRow>
54+
</MonthPickerGridBody>
55+
</MonthPickerGrid>
56+
</div>
57+
</MonthPickerRoot>
58+
</template>
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
const { blackA, grass, green } = require('@radix-ui/colors')
2+
3+
/** @type {import('tailwindcss').Config} */
4+
module.exports = {
5+
content: ['./**/*.vue'],
6+
theme: {
7+
extend: {
8+
colors: {
9+
...blackA,
10+
...grass,
11+
...green,
12+
},
13+
},
14+
},
15+
plugins: [],
16+
}
Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
<script setup lang="ts">
2+
import { Icon } from '@iconify/vue'
3+
import { CalendarDate } from '@internationalized/date'
4+
import { MonthRangePickerCell, MonthRangePickerCellTrigger, MonthRangePickerGrid, MonthRangePickerGridBody, MonthRangePickerGridRow, MonthRangePickerHeader, MonthRangePickerHeading, MonthRangePickerNext, MonthRangePickerPrev, MonthRangePickerRoot } from 'reka-ui'
5+
import './styles.css'
6+
7+
const defaultValue = {
8+
start: new CalendarDate(2024, 3, 1),
9+
end: new CalendarDate(2024, 6, 1),
10+
}
11+
</script>
12+
13+
<template>
14+
<MonthRangePickerRoot
15+
v-slot="{ grid }"
16+
:default-value="defaultValue"
17+
class="MonthRangePicker"
18+
>
19+
<MonthRangePickerHeader class="MonthRangePickerHeader">
20+
<MonthRangePickerPrev class="MonthRangePickerNavButton">
21+
<Icon
22+
icon="radix-icons:chevron-left"
23+
class="Icon"
24+
/>
25+
</MonthRangePickerPrev>
26+
<MonthRangePickerHeading class="MonthRangePickerHeading" />
27+
<MonthRangePickerNext class="MonthRangePickerNavButton">
28+
<Icon
29+
icon="radix-icons:chevron-right"
30+
class="Icon"
31+
/>
32+
</MonthRangePickerNext>
33+
</MonthRangePickerHeader>
34+
<div class="MonthRangePickerWrapper">
35+
<MonthRangePickerGrid class="MonthRangePickerGrid">
36+
<MonthRangePickerGridBody class="MonthRangePickerGridWrapper">
37+
<MonthRangePickerGridRow
38+
v-for="(months, index) in grid.rows"
39+
:key="`month-${index}`"
40+
class="MonthRangePickerGridRow"
41+
>
42+
<MonthRangePickerCell
43+
v-for="month in months"
44+
:key="month.toString()"
45+
:date="month"
46+
class="MonthRangePickerCell"
47+
>
48+
<MonthRangePickerCellTrigger
49+
:month="month"
50+
class="MonthRangePickerCellTrigger"
51+
/>
52+
</MonthRangePickerCell>
53+
</MonthRangePickerGridRow>
54+
</MonthRangePickerGridBody>
55+
</MonthRangePickerGrid>
56+
</div>
57+
</MonthRangePickerRoot>
58+
</template>

0 commit comments

Comments
 (0)