react-attendance-calendar 2.4.3 → 2.4.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +240 -292
- package/dist/attendance-calendar.d.ts +17 -0
- package/dist/attendance-calendar.d.ts.map +1 -0
- package/dist/calendar/calendar-grid.d.ts +11 -0
- package/dist/calendar/calendar-grid.d.ts.map +1 -0
- package/dist/calendar/calendar-header.d.ts +6 -0
- package/dist/calendar/calendar-header.d.ts.map +1 -0
- package/dist/calendar/calendar-nav.d.ts +5 -0
- package/dist/calendar/calendar-nav.d.ts.map +1 -0
- package/dist/calendar/calendar-root.d.ts +11 -0
- package/dist/calendar/calendar-root.d.ts.map +1 -0
- package/dist/calendar/calendar-title.d.ts +6 -0
- package/dist/calendar/calendar-title.d.ts.map +1 -0
- package/dist/calendar/calendar-weekdays.d.ts +7 -0
- package/dist/calendar/calendar-weekdays.d.ts.map +1 -0
- package/dist/calendar/context.d.ts +23 -0
- package/dist/calendar/context.d.ts.map +1 -0
- package/dist/calendar/index.d.ts +25 -0
- package/dist/calendar/index.d.ts.map +1 -0
- package/dist/calendar/types.d.ts +28 -0
- package/dist/calendar/types.d.ts.map +1 -0
- package/dist/calendar/utils.d.ts +17 -0
- package/dist/calendar/utils.d.ts.map +1 -0
- package/dist/index.css +1 -1
- package/dist/index.d.ts +4 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +973 -791
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
- package/dist/AttendanceCalendar.d.ts +0 -38
- package/dist/AttendanceCalendar.d.ts.map +0 -1
package/README.md
CHANGED
|
@@ -1,44 +1,33 @@
|
|
|
1
1
|
# React Attendance Calendar
|
|
2
2
|
|
|
3
|
-
A modern, responsive
|
|
3
|
+
A modern, responsive React attendance calendar with a **compound component** architecture. Style it with classes, swap out navigation buttons, render custom cells — or just drop in the pre-composed version and go.
|
|
4
4
|
|
|
5
|
-
##
|
|
5
|
+
## Features
|
|
6
6
|
|
|
7
|
-
-
|
|
8
|
-
-
|
|
9
|
-
-
|
|
10
|
-
-
|
|
11
|
-
-
|
|
12
|
-
-
|
|
13
|
-
-
|
|
14
|
-
-
|
|
15
|
-
- ⚡ **Auto-initialization** - Defaults to current month if no view provided
|
|
16
|
-
- 🎯 **Smart navigation** - Seamless month/year transitions
|
|
17
|
-
- 🗓️ **Today highlight** - Current date is highlighted; style it with `todayCellClassName` (merged last so it wins over present/absent/cell classes)
|
|
18
|
-
- 📐 **Flexible header** - Position month title left, center, or right
|
|
7
|
+
- **Compound components** — compose `Calendar.Root`, `Header`, `Title`, `PrevTrigger`, `NextTrigger`, `WeekDays`, and `Grid` any way you like
|
|
8
|
+
- **Pre-composed default** — `AttendanceCalendar` works out of the box with zero config
|
|
9
|
+
- **`renderCell`** — full control over every cell's markup and style
|
|
10
|
+
- **`useCalendar()` hook** — headless access to all calendar state for completely custom UIs
|
|
11
|
+
- **`classNames` object** — one clean prop for `cell`, `present`, `absent`, `today`, `outside` styles
|
|
12
|
+
- **Responsive** — auto-switches between 7 and 14 columns based on container width
|
|
13
|
+
- **TypeScript** — full type safety and IntelliSense
|
|
14
|
+
- **Multi-month data** — provide attendance for multiple months at once
|
|
19
15
|
|
|
20
|
-
##
|
|
16
|
+
## Installation
|
|
21
17
|
|
|
22
18
|
```bash
|
|
23
19
|
npm install react-attendance-calendar
|
|
24
20
|
```
|
|
25
21
|
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
**Simply import the CSS in your app:**
|
|
22
|
+
Import the CSS once in your app entry:
|
|
29
23
|
|
|
30
24
|
```tsx
|
|
31
|
-
// In your main app file (e.g., App.tsx, main.tsx, or _app.tsx)
|
|
32
25
|
import "react-attendance-calendar/styles.css";
|
|
33
26
|
```
|
|
34
27
|
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
> **Note:** No additional setup required. The package includes pre-compiled CSS. If you're using Tailwind CSS v4 in your project, you can customize the component further using the `className` props.
|
|
38
|
-
|
|
39
|
-
## 🚀 Usage
|
|
28
|
+
> The package ships pre-compiled CSS. If you use Tailwind CSS v4, you can further customize via `className` and `classNames` props.
|
|
40
29
|
|
|
41
|
-
|
|
30
|
+
## Quick Start
|
|
42
31
|
|
|
43
32
|
```tsx
|
|
44
33
|
import { useState } from "react";
|
|
@@ -48,351 +37,310 @@ import "react-attendance-calendar/styles.css";
|
|
|
48
37
|
function App() {
|
|
49
38
|
const [view, setView] = useState({ year: 2024, monthIndex: 0 });
|
|
50
39
|
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
```tsx
|
|
58
|
-
import { AttendanceCalendar } from "react-attendance-calendar";
|
|
59
|
-
import "react-attendance-calendar/styles.css";
|
|
60
|
-
|
|
61
|
-
function App() {
|
|
62
|
-
const [view, setView] = useState();
|
|
40
|
+
const data = {
|
|
41
|
+
year: 2024,
|
|
42
|
+
monthIndex: 0,
|
|
43
|
+
presentDays: new Set([1, 2, 3, 5, 8, 9, 10]),
|
|
44
|
+
absentDays: new Set([4, 11]),
|
|
45
|
+
};
|
|
63
46
|
|
|
64
|
-
return
|
|
47
|
+
return (
|
|
48
|
+
<AttendanceCalendar
|
|
49
|
+
view={view}
|
|
50
|
+
onChangeView={setView}
|
|
51
|
+
attendanceData={data}
|
|
52
|
+
onDateClick={(day, month, year) => console.log(day, month, year)}
|
|
53
|
+
/>
|
|
54
|
+
);
|
|
65
55
|
}
|
|
66
56
|
```
|
|
67
57
|
|
|
68
|
-
|
|
58
|
+
## Usage
|
|
69
59
|
|
|
70
|
-
|
|
71
|
-
import { AttendanceCalendar } from "react-attendance-calendar";
|
|
72
|
-
import "react-attendance-calendar/styles.css";
|
|
60
|
+
### Pre-composed (simple)
|
|
73
61
|
|
|
74
|
-
|
|
75
|
-
year: 2024,
|
|
76
|
-
monthIndex: 0,
|
|
77
|
-
presentDays: new Set([1, 2, 3, 5, 8, 9, 10]),
|
|
78
|
-
absentDays: new Set([4, 11]),
|
|
79
|
-
};
|
|
62
|
+
The `AttendanceCalendar` component is a ready-to-use calendar with sensible defaults. Pass a `classNames` object to customize cell styles without touching the layout.
|
|
80
63
|
|
|
64
|
+
```tsx
|
|
81
65
|
<AttendanceCalendar
|
|
82
66
|
view={view}
|
|
83
67
|
onChangeView={setView}
|
|
84
|
-
attendanceData={
|
|
85
|
-
|
|
86
|
-
|
|
68
|
+
attendanceData={data}
|
|
69
|
+
classNames={{
|
|
70
|
+
cell: "rounded-full",
|
|
71
|
+
present: "bg-teal-500 shadow-md",
|
|
72
|
+
absent: "bg-rose-500",
|
|
73
|
+
today: "ring-2 ring-blue-400",
|
|
74
|
+
outside: "opacity-20",
|
|
75
|
+
}}
|
|
76
|
+
/>
|
|
87
77
|
```
|
|
88
78
|
|
|
89
|
-
###
|
|
79
|
+
### Compound components (full control)
|
|
80
|
+
|
|
81
|
+
Build your own layout with `Calendar.*` components. Place the title, navigation buttons, weekday headers, and grid wherever you want.
|
|
90
82
|
|
|
91
83
|
```tsx
|
|
92
|
-
import {
|
|
84
|
+
import { Calendar } from "react-attendance-calendar";
|
|
93
85
|
import "react-attendance-calendar/styles.css";
|
|
94
86
|
|
|
95
|
-
|
|
96
|
-
const
|
|
97
|
-
// January 2024
|
|
98
|
-
{
|
|
99
|
-
year: 2024,
|
|
100
|
-
monthIndex: 0,
|
|
101
|
-
presentDays: new Set([
|
|
102
|
-
1, 2, 3, 5, 8, 9, 10, 12, 15, 16, 17, 19, 22, 23, 24, 26, 29, 30, 31,
|
|
103
|
-
]),
|
|
104
|
-
absentDays: new Set([4, 11, 18, 25]),
|
|
105
|
-
},
|
|
106
|
-
// February 2025
|
|
107
|
-
{
|
|
108
|
-
year: 2025,
|
|
109
|
-
monthIndex: 1,
|
|
110
|
-
presentDays: new Set([
|
|
111
|
-
1, 2, 5, 6, 7, 8, 9, 12, 13, 14, 15, 16, 19, 20, 21, 22, 23, 26, 27, 28,
|
|
112
|
-
]),
|
|
113
|
-
absentDays: new Set([3, 10, 17, 24]),
|
|
114
|
-
},
|
|
115
|
-
];
|
|
87
|
+
function MyCalendar() {
|
|
88
|
+
const [view, setView] = useState({ year: 2024, monthIndex: 0 });
|
|
116
89
|
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
90
|
+
return (
|
|
91
|
+
<Calendar.Root
|
|
92
|
+
view={view}
|
|
93
|
+
onChangeView={setView}
|
|
94
|
+
attendanceData={data}
|
|
95
|
+
onDateClick={(day, month, year) => console.log(day, month, year)}
|
|
96
|
+
>
|
|
97
|
+
<Calendar.Header>
|
|
98
|
+
<Calendar.Title className="text-indigo-700" />
|
|
99
|
+
<div className="flex gap-2">
|
|
100
|
+
<Calendar.PrevTrigger className="rounded-full bg-indigo-500 text-white">
|
|
101
|
+
←
|
|
102
|
+
</Calendar.PrevTrigger>
|
|
103
|
+
<Calendar.NextTrigger className="rounded-full bg-indigo-500 text-white">
|
|
104
|
+
→
|
|
105
|
+
</Calendar.NextTrigger>
|
|
106
|
+
</div>
|
|
107
|
+
</Calendar.Header>
|
|
108
|
+
<Calendar.WeekDays dayClassName="text-indigo-400" />
|
|
109
|
+
<Calendar.Grid
|
|
110
|
+
classNames={{
|
|
111
|
+
present: "bg-indigo-500",
|
|
112
|
+
absent: "bg-pink-500",
|
|
113
|
+
}}
|
|
114
|
+
/>
|
|
115
|
+
</Calendar.Root>
|
|
116
|
+
);
|
|
117
|
+
}
|
|
123
118
|
```
|
|
124
119
|
|
|
125
|
-
### Custom
|
|
120
|
+
### Custom title format
|
|
126
121
|
|
|
127
122
|
```tsx
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
<AttendanceCalendar view={view} onChangeView={setView} monthTitlePosition="right" />
|
|
123
|
+
<Calendar.Title
|
|
124
|
+
format={(monthName, year) => (
|
|
125
|
+
<span className="text-lg font-medium">
|
|
126
|
+
{monthName} / {year}
|
|
127
|
+
</span>
|
|
128
|
+
)}
|
|
129
|
+
/>
|
|
136
130
|
```
|
|
137
131
|
|
|
138
|
-
### Custom
|
|
132
|
+
### Custom cell rendering
|
|
133
|
+
|
|
134
|
+
Use `renderCell` on `Calendar.Grid` (or the pre-composed `AttendanceCalendar`) for complete control over each cell's markup. The callback receives a `CellData` object with all the state you need.
|
|
139
135
|
|
|
140
136
|
```tsx
|
|
141
|
-
<
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
137
|
+
<Calendar.Grid
|
|
138
|
+
renderCell={(cell) => (
|
|
139
|
+
<div
|
|
140
|
+
className={`w-full aspect-square grid place-items-center rounded-xl text-sm ${
|
|
141
|
+
cell.isPresent
|
|
142
|
+
? "bg-green-100 text-green-700"
|
|
143
|
+
: cell.isAbsent
|
|
144
|
+
? "bg-red-100 text-red-700"
|
|
145
|
+
: cell.isToday
|
|
146
|
+
? "bg-blue-600 text-white"
|
|
147
|
+
: "text-slate-600"
|
|
148
|
+
}`}
|
|
149
|
+
>
|
|
150
|
+
{cell.day}
|
|
151
|
+
{cell.isPresent && <span className="text-[10px]">✓</span>}
|
|
152
|
+
{cell.isAbsent && <span className="text-[10px]">✗</span>}
|
|
153
|
+
</div>
|
|
154
|
+
)}
|
|
147
155
|
/>
|
|
148
156
|
```
|
|
149
157
|
|
|
150
|
-
###
|
|
158
|
+
### Headless with `useCalendar()`
|
|
151
159
|
|
|
152
|
-
|
|
160
|
+
For a completely custom UI, use the hook inside a `Calendar.Root`:
|
|
153
161
|
|
|
154
162
|
```tsx
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
163
|
+
import { Calendar, useCalendar } from "react-attendance-calendar";
|
|
164
|
+
|
|
165
|
+
function HeadlessCalendar() {
|
|
166
|
+
const { view, monthName, goPrev, goNext, cells, currentMonthData, today, columns } =
|
|
167
|
+
useCalendar();
|
|
168
|
+
|
|
169
|
+
// Build any UI you want with full access to calendar state
|
|
170
|
+
return <div>{/* your custom markup */}</div>;
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
function App() {
|
|
174
|
+
const [view, setView] = useState({ year: 2024, monthIndex: 0 });
|
|
175
|
+
|
|
176
|
+
return (
|
|
177
|
+
<Calendar.Root view={view} onChangeView={setView} attendanceData={data}>
|
|
178
|
+
<HeadlessCalendar />
|
|
179
|
+
</Calendar.Root>
|
|
180
|
+
);
|
|
181
|
+
}
|
|
162
182
|
```
|
|
163
183
|
|
|
164
|
-
### Custom
|
|
184
|
+
### Custom cell sizes
|
|
165
185
|
|
|
166
186
|
```tsx
|
|
167
|
-
// Square cells
|
|
168
|
-
<AttendanceCalendar
|
|
169
|
-
view={view}
|
|
170
|
-
onChangeView={setView}
|
|
171
|
-
cellSize={60} // 60px x 60px cells
|
|
172
|
-
/>
|
|
187
|
+
// Square cells
|
|
188
|
+
<AttendanceCalendar view={view} onChangeView={setView} cellSize={50} />
|
|
173
189
|
|
|
174
190
|
// Custom width and height
|
|
175
|
-
<AttendanceCalendar
|
|
176
|
-
view={view}
|
|
177
|
-
onChangeView={setView}
|
|
178
|
-
cellWidth={80} // 80px wide
|
|
179
|
-
cellHeight={50} // 50px tall
|
|
180
|
-
/>
|
|
191
|
+
<AttendanceCalendar view={view} onChangeView={setView} cellWidth={80} cellHeight={50} />
|
|
181
192
|
|
|
182
|
-
//
|
|
193
|
+
// Compact
|
|
183
194
|
<AttendanceCalendar
|
|
184
195
|
view={view}
|
|
185
196
|
onChangeView={setView}
|
|
186
|
-
cellSize={32}
|
|
187
|
-
|
|
197
|
+
cellSize={32}
|
|
198
|
+
classNames={{ cell: "rounded-full text-xs" }}
|
|
188
199
|
/>
|
|
189
200
|
```
|
|
190
201
|
|
|
191
|
-
###
|
|
202
|
+
### Multi-month data
|
|
192
203
|
|
|
193
|
-
|
|
194
|
-
import { useState } from "react";
|
|
195
|
-
import {
|
|
196
|
-
AttendanceCalendar,
|
|
197
|
-
type MonthView,
|
|
198
|
-
type AttendanceData,
|
|
199
|
-
} from "react-attendance-calendar";
|
|
200
|
-
import "react-attendance-calendar/styles.css";
|
|
204
|
+
Pass an array to `attendanceData` to pre-load multiple months. Navigation between loaded months is instant.
|
|
201
205
|
|
|
202
|
-
|
|
203
|
-
|
|
206
|
+
```tsx
|
|
207
|
+
const data: AttendanceData = [
|
|
208
|
+
{
|
|
204
209
|
year: 2024,
|
|
205
|
-
monthIndex: 0,
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
]),
|
|
217
|
-
absentDays: new Set([4, 11, 18, 25]),
|
|
218
|
-
},
|
|
219
|
-
// February 2025
|
|
220
|
-
{
|
|
221
|
-
year: 2025,
|
|
222
|
-
monthIndex: 1,
|
|
223
|
-
presentDays: new Set([
|
|
224
|
-
1, 2, 5, 6, 7, 8, 9, 12, 13, 14, 15, 16, 19, 20, 21, 22, 23, 26, 27, 28,
|
|
225
|
-
]),
|
|
226
|
-
absentDays: new Set([3, 10, 17, 24]),
|
|
227
|
-
},
|
|
228
|
-
];
|
|
229
|
-
|
|
230
|
-
const handleDateClick = (day: number, month: number, year: number) => {
|
|
231
|
-
console.log(`Clicked on ${day}/${month + 1}/${year}`);
|
|
232
|
-
// Your custom logic here
|
|
233
|
-
};
|
|
234
|
-
|
|
235
|
-
return (
|
|
236
|
-
<div className="max-w-4xl mx-auto p-4">
|
|
237
|
-
<AttendanceCalendar
|
|
238
|
-
view={view}
|
|
239
|
-
onChangeView={setView}
|
|
240
|
-
attendanceData={attendanceData}
|
|
241
|
-
onDateClick={handleDateClick}
|
|
242
|
-
showNavigation={true}
|
|
243
|
-
showWeekdayHeaders={true}
|
|
244
|
-
// Custom styling with className props
|
|
245
|
-
cellClassName="rounded-full"
|
|
246
|
-
presentCellClassName="bg-green-500 text-white shadow-lg"
|
|
247
|
-
absentCellClassName="bg-red-500 text-white border-2 border-red-300"
|
|
248
|
-
navigationButtonClassName="bg-blue-500 text-white hover:bg-blue-600"
|
|
249
|
-
monthTitleClassName="text-3xl text-purple-600"
|
|
250
|
-
containerClassName="border border-gray-200 rounded-lg p-4"
|
|
251
|
-
/>
|
|
252
|
-
</div>
|
|
253
|
-
);
|
|
254
|
-
}
|
|
210
|
+
monthIndex: 0,
|
|
211
|
+
presentDays: new Set([1, 2, 3, 5, 8, 9, 10]),
|
|
212
|
+
absentDays: new Set([4, 11]),
|
|
213
|
+
},
|
|
214
|
+
{
|
|
215
|
+
year: 2024,
|
|
216
|
+
monthIndex: 1,
|
|
217
|
+
presentDays: new Set([1, 2, 5, 6, 7]),
|
|
218
|
+
absentDays: new Set([3, 4, 8]),
|
|
219
|
+
},
|
|
220
|
+
];
|
|
255
221
|
|
|
256
|
-
|
|
222
|
+
<AttendanceCalendar view={view} onChangeView={setView} attendanceData={data} />;
|
|
257
223
|
```
|
|
258
224
|
|
|
259
|
-
##
|
|
260
|
-
|
|
261
|
-
| Prop | Type | Required | Description |
|
|
262
|
-
| --------------------------- | ---------------------------- | -------- | -------------------------------------------------- |
|
|
263
|
-
| `view` | `MonthView` | ❌ | Current month and year (defaults to current month) |
|
|
264
|
-
| `onChangeView` | `(view: MonthView) => void` | ✅ | Month navigation callback |
|
|
265
|
-
| `attendanceData` | `AttendanceData` | ❌ | Present/absent days data (single month or array) |
|
|
266
|
-
| `onDateClick` | `(day, month, year) => void` | ❌ | Date click callback |
|
|
267
|
-
| `monthTitlePosition` | `'left' \| 'center' \| 'right'` | ❌ | Position of the month title (default: `'center'`) |
|
|
268
|
-
| `showNavigation` | `boolean` | ❌ | Show nav arrows (default: `true`) |
|
|
269
|
-
| `showWeekdayHeaders` | `boolean` | ❌ | Show weekday headers (default: `true`) |
|
|
270
|
-
| `cellSize` | `number` | ❌ | Size in pixels for all calendar cells (square) |
|
|
271
|
-
| `cellHeight` | `number` | ❌ | Height in pixels for calendar cells |
|
|
272
|
-
| `cellWidth` | `number` | ❌ | Width in pixels for calendar cells |
|
|
273
|
-
| `className` | `string` | ❌ | Additional classes for root element |
|
|
274
|
-
| `cellClassName` | `string` | ❌ | Custom classes for all cells |
|
|
275
|
-
| `presentCellClassName` | `string` | ❌ | Custom classes for present days |
|
|
276
|
-
| `absentCellClassName` | `string` | ❌ | Custom classes for absent days |
|
|
277
|
-
| `navigationButtonClassName` | `string` | ❌ | Custom classes for nav buttons |
|
|
278
|
-
| `monthTitleClassName` | `string` | ❌ | Custom classes for month title |
|
|
279
|
-
| `weekdayHeaderClassName` | `string` | ❌ | Custom classes for weekday headers |
|
|
280
|
-
| `containerClassName` | `string` | ❌ | Custom classes for main container |
|
|
281
|
-
| `todayCellClassName` | `string` | ❌ | Classes for **today’s** cell only; merged **last** so they override other cell props |
|
|
282
|
-
|
|
283
|
-
### TypeScript
|
|
225
|
+
## API Reference
|
|
284
226
|
|
|
285
|
-
|
|
286
|
-
type MonthView = {
|
|
287
|
-
year: number; // e.g., 2024
|
|
288
|
-
monthIndex: number; // 0-11 (0 = January)
|
|
289
|
-
};
|
|
227
|
+
### `AttendanceCalendar` (pre-composed)
|
|
290
228
|
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
229
|
+
| Prop | Type | Default | Description |
|
|
230
|
+
| --- | --- | --- | --- |
|
|
231
|
+
| `view` | `MonthView` | current month | Current month and year |
|
|
232
|
+
| `onChangeView` | `(view: MonthView) => void` | **required** | Month navigation callback |
|
|
233
|
+
| `attendanceData` | `AttendanceData` | — | Present/absent days (single or array) |
|
|
234
|
+
| `onDateClick` | `(day, month, year) => void` | — | Date click callback |
|
|
235
|
+
| `showNavigation` | `boolean` | `true` | Show header with nav buttons |
|
|
236
|
+
| `showWeekDays` | `boolean` | `true` | Show weekday labels |
|
|
237
|
+
| `className` | `string` | — | Root container class |
|
|
238
|
+
| `classNames` | `GridClassNames` | — | Cell style overrides (see below) |
|
|
239
|
+
| `cellSize` | `number` | — | Square cell size in px |
|
|
240
|
+
| `cellWidth` | `number` | — | Cell width in px |
|
|
241
|
+
| `cellHeight` | `number` | — | Cell height in px |
|
|
242
|
+
| `renderCell` | `(state: CellData) => ReactNode` | — | Custom cell renderer |
|
|
297
243
|
|
|
298
|
-
|
|
299
|
-
type AttendanceData = MonthAttendanceData | MonthAttendanceData[];
|
|
244
|
+
### `GridClassNames`
|
|
300
245
|
|
|
301
|
-
|
|
246
|
+
```typescript
|
|
247
|
+
type GridClassNames = {
|
|
248
|
+
cell?: string; // Base class for all cells
|
|
249
|
+
present?: string; // Present-day cells
|
|
250
|
+
absent?: string; // Absent-day cells
|
|
251
|
+
today?: string; // Today's cell (merged last, wins over present/absent)
|
|
252
|
+
outside?: string; // Days outside current month
|
|
253
|
+
};
|
|
302
254
|
```
|
|
303
255
|
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
The component uses Tailwind CSS classes with a modern design system:
|
|
307
|
-
|
|
308
|
-
### Default Theme
|
|
309
|
-
|
|
310
|
-
- **Present days**: Emerald green (`emerald-500`) with white text
|
|
311
|
-
- **Absent days**: Amber orange (`amber-500`) with white text
|
|
312
|
-
- **Regular days**: Slate gray (`slate-700`) with light border
|
|
313
|
-
- **Today** (not present/absent): Dark slate background (`slate-900`) with white text; use `todayCellClassName` to replace or extend
|
|
314
|
-
- **Today** (present/absent): Same as that status, plus a subtle ring; override with `todayCellClassName`
|
|
315
|
-
- **Navigation**: Rounded buttons with hover effects
|
|
316
|
-
- **Typography**: Clean, readable fonts with proper hierarchy
|
|
317
|
-
|
|
318
|
-
### Responsive Behavior
|
|
256
|
+
### `CellData`
|
|
319
257
|
|
|
320
|
-
|
|
321
|
-
- **Desktop (≥700px container)**: Switches to 14-column layout
|
|
322
|
-
- **Narrow containers / mobile**: 7-column layout with touch-friendly sizing
|
|
323
|
-
- **Fixed size mode**: Use `cellSize`, `cellWidth`, or `cellHeight` for explicit pixel dimensions
|
|
258
|
+
Passed to `renderCell`:
|
|
324
259
|
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
260
|
+
```typescript
|
|
261
|
+
type CellData = {
|
|
262
|
+
day: number;
|
|
263
|
+
date: Date;
|
|
264
|
+
inCurrentMonth: boolean;
|
|
265
|
+
isPresent: boolean;
|
|
266
|
+
isAbsent: boolean;
|
|
267
|
+
isToday: boolean;
|
|
268
|
+
isClickable: boolean;
|
|
269
|
+
};
|
|
270
|
+
```
|
|
335
271
|
|
|
336
|
-
|
|
272
|
+
### Compound Components
|
|
337
273
|
|
|
338
|
-
|
|
274
|
+
| Component | Props | Description |
|
|
275
|
+
| --- | --- | --- |
|
|
276
|
+
| `Calendar.Root` | `view`, `onChangeView`, `attendanceData`, `onDateClick`, `className`, `children` | Context provider and container |
|
|
277
|
+
| `Calendar.Header` | `className`, `children` | Flex wrapper for title + nav |
|
|
278
|
+
| `Calendar.Title` | `className`, `format?` | Month/year heading |
|
|
279
|
+
| `Calendar.PrevTrigger` | all `<button>` props | Previous month button |
|
|
280
|
+
| `Calendar.NextTrigger` | all `<button>` props | Next month button |
|
|
281
|
+
| `Calendar.WeekDays` | `className`, `dayClassName`, `labels?` | Weekday labels row |
|
|
282
|
+
| `Calendar.Grid` | `className`, `classNames`, `cellSize`, `cellHeight`, `cellWidth`, `renderCell` | Date grid |
|
|
339
283
|
|
|
340
|
-
|
|
341
|
-
- Hover effects, transitions, animations
|
|
342
|
-
- Responsive breakpoints and utilities
|
|
343
|
-
- **`todayCellClassName`** — prefer this for today’s look so your utilities reliably override shared props like `presentCellClassName`
|
|
284
|
+
### `useCalendar()` Hook
|
|
344
285
|
|
|
345
|
-
|
|
286
|
+
Returns all calendar state when used inside `Calendar.Root`:
|
|
346
287
|
|
|
347
|
-
|
|
288
|
+
```typescript
|
|
289
|
+
const {
|
|
290
|
+
view, // MonthView — current month/year
|
|
291
|
+
monthName, // string — e.g. "January"
|
|
292
|
+
today, // { day, month, year }
|
|
293
|
+
currentMonthData,// MonthAttendanceData | undefined
|
|
294
|
+
columns, // number — 7 or 14 (responsive)
|
|
295
|
+
cells, // { day, inCurrentMonth }[]
|
|
296
|
+
weekdayLabels, // string[]
|
|
297
|
+
goPrev, // () => void
|
|
298
|
+
goNext, // () => void
|
|
299
|
+
onDateClick, // ((day, month, year) => void) | undefined
|
|
300
|
+
} = useCalendar();
|
|
301
|
+
```
|
|
348
302
|
|
|
349
|
-
###
|
|
303
|
+
### Types
|
|
350
304
|
|
|
351
305
|
```typescript
|
|
352
|
-
|
|
353
|
-
year:
|
|
354
|
-
monthIndex: 0
|
|
355
|
-
presentDays: new Set([1, 2, 3, 5, 8]),
|
|
356
|
-
absentDays: new Set([4, 6, 7]),
|
|
306
|
+
type MonthView = {
|
|
307
|
+
year: number;
|
|
308
|
+
monthIndex: number; // 0-11
|
|
357
309
|
};
|
|
358
|
-
```
|
|
359
310
|
|
|
360
|
-
|
|
311
|
+
type MonthAttendanceData = {
|
|
312
|
+
year: number;
|
|
313
|
+
monthIndex: number;
|
|
314
|
+
presentDays: Set<number>;
|
|
315
|
+
absentDays: Set<number>;
|
|
316
|
+
};
|
|
361
317
|
|
|
362
|
-
|
|
363
|
-
const attendanceData: AttendanceData = [
|
|
364
|
-
{
|
|
365
|
-
year: 2024,
|
|
366
|
-
monthIndex: 0, // January
|
|
367
|
-
presentDays: new Set([1, 2, 3, 5, 8]),
|
|
368
|
-
absentDays: new Set([4, 6, 7]),
|
|
369
|
-
},
|
|
370
|
-
{
|
|
371
|
-
year: 2024,
|
|
372
|
-
monthIndex: 1, // February
|
|
373
|
-
presentDays: new Set([1, 2, 5, 6, 7]),
|
|
374
|
-
absentDays: new Set([3, 4, 8]),
|
|
375
|
-
},
|
|
376
|
-
// ... add more months as needed
|
|
377
|
-
];
|
|
318
|
+
type AttendanceData = MonthAttendanceData | MonthAttendanceData[];
|
|
378
319
|
```
|
|
379
320
|
|
|
380
|
-
|
|
321
|
+
## Default Theme
|
|
322
|
+
|
|
323
|
+
- **Present** — emerald green (`emerald-500`) with white text
|
|
324
|
+
- **Absent** — amber orange (`amber-500`) with white text
|
|
325
|
+
- **Today** (no data) — dark slate (`slate-900`) with white text
|
|
326
|
+
- **Today** (present/absent) — status color + ring highlight
|
|
327
|
+
- **Outside month** — faded with light border
|
|
328
|
+
- **Navigation** — rounded buttons with hover/active effects
|
|
329
|
+
|
|
330
|
+
## Responsive Behavior
|
|
381
331
|
|
|
382
|
-
-
|
|
383
|
-
-
|
|
384
|
-
-
|
|
385
|
-
-
|
|
386
|
-
- ✅ **Memory efficient** - Store data in memory for instant access
|
|
387
|
-
- ✅ **Offline ready** - Pre-load data for offline calendar browsing
|
|
332
|
+
- Cells use `w-full aspect-square` by default — fluid with no overflow
|
|
333
|
+
- Container >= 700px: switches to 14-column layout
|
|
334
|
+
- Narrow / mobile: 7-column layout with touch-friendly sizing
|
|
335
|
+
- Use `cellSize`, `cellWidth`, or `cellHeight` for fixed pixel dimensions
|
|
388
336
|
|
|
389
|
-
##
|
|
337
|
+
## License
|
|
390
338
|
|
|
391
339
|
MIT © [Alamin](https://github.com/alamincodes)
|
|
392
340
|
|
|
393
341
|
---
|
|
394
342
|
|
|
395
343
|
<div align="center">
|
|
396
|
-
<a href="https://github.com/alamincodes/attendance-calendar">GitHub</a> •
|
|
344
|
+
<a href="https://github.com/alamincodes/attendance-calendar">GitHub</a> •
|
|
397
345
|
<a href="https://www.npmjs.com/package/react-attendance-calendar">npm</a>
|
|
398
346
|
</div>
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import type { AttendanceData, CellData, GridClassNames, MonthView } from "./calendar/types";
|
|
2
|
+
export type AttendanceCalendarProps = {
|
|
3
|
+
view?: MonthView;
|
|
4
|
+
onChangeView: (next: MonthView) => void;
|
|
5
|
+
attendanceData?: AttendanceData;
|
|
6
|
+
onDateClick?: (day: number, month: number, year: number) => void;
|
|
7
|
+
showNavigation?: boolean;
|
|
8
|
+
showWeekDays?: boolean;
|
|
9
|
+
className?: string;
|
|
10
|
+
classNames?: GridClassNames;
|
|
11
|
+
cellSize?: number;
|
|
12
|
+
cellHeight?: number;
|
|
13
|
+
cellWidth?: number;
|
|
14
|
+
renderCell?: (state: CellData) => React.ReactNode;
|
|
15
|
+
};
|
|
16
|
+
export default function AttendanceCalendar({ view, onChangeView, attendanceData, onDateClick, showNavigation, showWeekDays, className, classNames, cellSize, cellHeight, cellWidth, renderCell, }: AttendanceCalendarProps): import("react/jsx-runtime").JSX.Element;
|
|
17
|
+
//# sourceMappingURL=attendance-calendar.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"attendance-calendar.d.ts","sourceRoot":"","sources":["../src/attendance-calendar.tsx"],"names":[],"mappings":"AACA,OAAO,KAAK,EACV,cAAc,EACd,QAAQ,EACR,cAAc,EACd,SAAS,EACV,MAAM,kBAAkB,CAAC;AAE1B,MAAM,MAAM,uBAAuB,GAAG;IACpC,IAAI,CAAC,EAAE,SAAS,CAAC;IACjB,YAAY,EAAE,CAAC,IAAI,EAAE,SAAS,KAAK,IAAI,CAAC;IACxC,cAAc,CAAC,EAAE,cAAc,CAAC;IAChC,WAAW,CAAC,EAAE,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,KAAK,IAAI,CAAC;IACjE,cAAc,CAAC,EAAE,OAAO,CAAC;IACzB,YAAY,CAAC,EAAE,OAAO,CAAC;IACvB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,UAAU,CAAC,EAAE,cAAc,CAAC;IAC5B,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,UAAU,CAAC,EAAE,CAAC,KAAK,EAAE,QAAQ,KAAK,KAAK,CAAC,SAAS,CAAC;CACnD,CAAC;AAEF,MAAM,CAAC,OAAO,UAAU,kBAAkB,CAAC,EACzC,IAAI,EACJ,YAAY,EACZ,cAAc,EACd,WAAW,EACX,cAAqB,EACrB,YAAmB,EACnB,SAAS,EACT,UAAU,EACV,QAAQ,EACR,UAAU,EACV,SAAS,EACT,UAAU,GACX,EAAE,uBAAuB,2CA0BzB"}
|