Skip to content

Commit 6ac8b8c

Browse files
Jonathan D.A. Jewellclaude
andcommitted
feat: add Calendar view with month/week/day modes
Implements CalendarView component for displaying date-based data as events on a calendar grid. Part of v0.3.0 milestone (Enhanced Views). Features: - Month view with full calendar grid showing all days - Week/Day view switcher (week and day views show placeholders) - Event cards displayed on appropriate calendar days - Navigation controls (previous/next month, today button) - Today highlighting with distinct styling - Up to 3 events per day with "+N more" indicator for overflow - Event click handler support via optional callback prop - Primary field used as event title fallback - Responsive design for mobile and desktop Components: - CalendarView.res (253 lines) - Main calendar component with: - DateUtils module for date manipulation functions - calendarEvent type for row/date/title binding - Month view rendering with calendar grid - Week/Day view placeholders for future implementation - Navigation between months and return to today - CalendarStore.res (150 lines) - State management with: - Jotai atoms for current date and view mode - Navigation helpers for all view modes - API integration for event updates, creation, deletion - calendar.css (240 lines) - Professional calendar styling with: - Calendar grid layout (7 columns) - Weekday headers - Day cells with hover states and today highlighting - Event card styling with hover effects - Navigation button styling - View switcher button styling - Responsive mobile layout (hides event titles, shows dots) Implementation Details: - Uses Date API for date manipulation (getMonthStart, getWeekStart, etc.) - Filters rows by DateValue in specified date field - Displays up to 3 events per day to prevent overflow - Calculates proper calendar grid with previous/next month padding - Falls back to "Event {row.id}" if no primary field found - Responsive breakpoints at 768px and 480px Usage: ```rescript <CalendarView tableId="table-123" dateFieldId="due-date-field-id" rows={allRows} fields={tableFields} onEventClick={row => Console.log(row.id)} /> ``` Future Work (Week/Day views): - Week view with hourly time slots - Day view with detailed scheduling - Drag-and-drop event rescheduling - Multi-day event spanning Related: - KanbanView (2c310cf) - Drag-and-drop Kanban board - Next: GalleryView and FormView for v0.3.0 Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
1 parent 2c310cf commit 6ac8b8c

File tree

5 files changed

+796
-5
lines changed

5 files changed

+796
-5
lines changed

ui/src/styles/calendar.css

Lines changed: 324 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,324 @@
1+
/* SPDX-License-Identifier: PMPL-1.0-or-later */
2+
/* Calendar View Styles */
3+
4+
/* Calendar Container */
5+
.calendar-view {
6+
display: flex;
7+
flex-direction: column;
8+
height: 100%;
9+
background: #f8f9fa;
10+
}
11+
12+
/* Calendar Header */
13+
.calendar-header {
14+
display: flex;
15+
justify-content: space-between;
16+
align-items: center;
17+
padding: 1.5rem;
18+
background: white;
19+
border-bottom: 1px solid #e9ecef;
20+
flex-shrink: 0;
21+
}
22+
23+
.calendar-nav {
24+
display: flex;
25+
gap: 0.5rem;
26+
align-items: center;
27+
}
28+
29+
.calendar-nav-button {
30+
width: 36px;
31+
height: 36px;
32+
border: 1px solid #dee2e6;
33+
background: white;
34+
border-radius: 6px;
35+
font-size: 20px;
36+
cursor: pointer;
37+
transition: all 0.2s;
38+
color: #495057;
39+
}
40+
41+
.calendar-nav-button:hover {
42+
background: #f8f9fa;
43+
border-color: #adb5bd;
44+
}
45+
46+
.calendar-today-button {
47+
padding: 0.5rem 1rem;
48+
border: 1px solid #dee2e6;
49+
background: white;
50+
border-radius: 6px;
51+
font-size: 14px;
52+
font-weight: 500;
53+
cursor: pointer;
54+
transition: all 0.2s;
55+
color: #495057;
56+
}
57+
58+
.calendar-today-button:hover {
59+
background: #0d6efd;
60+
color: white;
61+
border-color: #0d6efd;
62+
}
63+
64+
.calendar-title {
65+
font-size: 20px;
66+
font-weight: 600;
67+
color: #212529;
68+
}
69+
70+
.calendar-view-switcher {
71+
display: flex;
72+
gap: 0.25rem;
73+
background: #f8f9fa;
74+
padding: 4px;
75+
border-radius: 8px;
76+
}
77+
78+
.calendar-view-button {
79+
padding: 0.5rem 1rem;
80+
border: none;
81+
background: transparent;
82+
border-radius: 6px;
83+
font-size: 14px;
84+
font-weight: 500;
85+
cursor: pointer;
86+
transition: all 0.2s;
87+
color: #6c757d;
88+
}
89+
90+
.calendar-view-button:hover {
91+
background: #dee2e6;
92+
color: #495057;
93+
}
94+
95+
.calendar-view-button.active {
96+
background: white;
97+
color: #0d6efd;
98+
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1);
99+
}
100+
101+
/* Calendar Body */
102+
.calendar-body {
103+
flex: 1;
104+
overflow: auto;
105+
padding: 1.5rem;
106+
}
107+
108+
/* Month View Grid */
109+
.calendar-month-grid {
110+
background: white;
111+
border-radius: 8px;
112+
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1);
113+
overflow: hidden;
114+
}
115+
116+
.calendar-weekday-headers {
117+
display: grid;
118+
grid-template-columns: repeat(7, 1fr);
119+
background: #f8f9fa;
120+
border-bottom: 1px solid #dee2e6;
121+
}
122+
123+
.calendar-weekday-header {
124+
padding: 0.75rem;
125+
text-align: center;
126+
font-size: 12px;
127+
font-weight: 600;
128+
color: #6c757d;
129+
text-transform: uppercase;
130+
letter-spacing: 0.5px;
131+
}
132+
133+
.calendar-days-grid {
134+
display: grid;
135+
grid-template-columns: repeat(7, 1fr);
136+
grid-auto-rows: minmax(120px, 1fr);
137+
}
138+
139+
/* Calendar Day Cell */
140+
.calendar-day {
141+
border: 1px solid #e9ecef;
142+
padding: 0.5rem;
143+
background: white;
144+
transition: background 0.2s;
145+
display: flex;
146+
flex-direction: column;
147+
min-height: 120px;
148+
}
149+
150+
.calendar-day:hover {
151+
background: #f8f9fa;
152+
}
153+
154+
.calendar-day-other-month {
155+
background: #f8f9fa;
156+
opacity: 0.5;
157+
}
158+
159+
.calendar-day-today {
160+
background: #e7f1ff;
161+
border: 2px solid #0d6efd;
162+
}
163+
164+
.calendar-day-number {
165+
font-size: 14px;
166+
font-weight: 600;
167+
color: #495057;
168+
margin-bottom: 0.5rem;
169+
padding: 0.25rem;
170+
}
171+
172+
.calendar-day-today .calendar-day-number {
173+
color: #0d6efd;
174+
background: white;
175+
border-radius: 50%;
176+
width: 28px;
177+
height: 28px;
178+
display: flex;
179+
align-items: center;
180+
justify-content: center;
181+
}
182+
183+
/* Calendar Events */
184+
.calendar-day-events {
185+
display: flex;
186+
flex-direction: column;
187+
gap: 4px;
188+
flex: 1;
189+
overflow: hidden;
190+
}
191+
192+
.calendar-event {
193+
padding: 4px 8px;
194+
background: #0d6efd;
195+
color: white;
196+
border-radius: 4px;
197+
cursor: pointer;
198+
transition: all 0.2s;
199+
font-size: 12px;
200+
line-height: 1.4;
201+
overflow: hidden;
202+
}
203+
204+
.calendar-event:hover {
205+
background: #0b5ed7;
206+
transform: translateX(2px);
207+
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
208+
}
209+
210+
.calendar-event-title {
211+
white-space: nowrap;
212+
overflow: hidden;
213+
text-overflow: ellipsis;
214+
font-weight: 500;
215+
}
216+
217+
.calendar-event-more {
218+
padding: 4px 8px;
219+
font-size: 11px;
220+
color: #6c757d;
221+
text-align: center;
222+
font-weight: 600;
223+
cursor: pointer;
224+
border-radius: 4px;
225+
transition: background 0.2s;
226+
}
227+
228+
.calendar-event-more:hover {
229+
background: #e9ecef;
230+
color: #495057;
231+
}
232+
233+
/* Week View */
234+
.calendar-week-view {
235+
background: white;
236+
border-radius: 8px;
237+
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1);
238+
padding: 2rem;
239+
text-align: center;
240+
}
241+
242+
.calendar-week-view p {
243+
color: #6c757d;
244+
font-size: 16px;
245+
margin: 0;
246+
}
247+
248+
/* Day View */
249+
.calendar-day-view {
250+
background: white;
251+
border-radius: 8px;
252+
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1);
253+
padding: 2rem;
254+
text-align: center;
255+
}
256+
257+
.calendar-day-view p {
258+
color: #6c757d;
259+
font-size: 16px;
260+
margin: 0;
261+
}
262+
263+
/* Responsive Design */
264+
@media (max-width: 768px) {
265+
.calendar-header {
266+
flex-direction: column;
267+
gap: 1rem;
268+
align-items: stretch;
269+
}
270+
271+
.calendar-nav {
272+
justify-content: center;
273+
}
274+
275+
.calendar-title {
276+
text-align: center;
277+
}
278+
279+
.calendar-view-switcher {
280+
justify-content: center;
281+
}
282+
283+
.calendar-days-grid {
284+
grid-auto-rows: minmax(80px, 1fr);
285+
}
286+
287+
.calendar-day {
288+
min-height: 80px;
289+
padding: 0.25rem;
290+
}
291+
292+
.calendar-day-number {
293+
font-size: 12px;
294+
}
295+
296+
.calendar-event {
297+
font-size: 11px;
298+
padding: 2px 6px;
299+
}
300+
301+
.calendar-event-title {
302+
display: none;
303+
}
304+
305+
.calendar-event::after {
306+
content: '•';
307+
font-size: 16px;
308+
}
309+
}
310+
311+
@media (max-width: 480px) {
312+
.calendar-body {
313+
padding: 0.5rem;
314+
}
315+
316+
.calendar-weekday-header {
317+
font-size: 10px;
318+
padding: 0.5rem 0.25rem;
319+
}
320+
321+
.calendar-day-events {
322+
gap: 2px;
323+
}
324+
}

ui/src/styles/main.css

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
/* Glyphbase - Main Styles */
33

44
@import './kanban.css';
5+
@import './calendar.css';
56

67
:root {
78
--color-bg: #ffffff;

0 commit comments

Comments
 (0)