@hectorbliss/denik-calendar 0.0.1 → 0.0.3
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 +75 -7
- package/dist/index.cjs +162 -100
- package/dist/index.d.cts +37 -4
- package/dist/index.d.ts +37 -4
- package/dist/index.js +163 -102
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -7,13 +7,13 @@ Built with [@dnd-kit](https://dndkit.com/) for smooth drag interactions.
|
|
|
7
7
|
## Installation
|
|
8
8
|
|
|
9
9
|
```bash
|
|
10
|
-
npm install denik-calendar
|
|
10
|
+
npm install @hectorbliss/denik-calendar
|
|
11
11
|
```
|
|
12
12
|
|
|
13
13
|
## Usage
|
|
14
14
|
|
|
15
15
|
```tsx
|
|
16
|
-
import { Calendar } from "denik-calendar";
|
|
16
|
+
import { Calendar } from "@hectorbliss/denik-calendar";
|
|
17
17
|
|
|
18
18
|
function App() {
|
|
19
19
|
const [events, setEvents] = useState([
|
|
@@ -45,23 +45,31 @@ function App() {
|
|
|
45
45
|
|
|
46
46
|
## Headless Hook
|
|
47
47
|
|
|
48
|
-
Use the
|
|
48
|
+
Use the calendar logic without the UI:
|
|
49
49
|
|
|
50
50
|
```tsx
|
|
51
|
-
import {
|
|
51
|
+
import { useCalendarEvents } from "@hectorbliss/denik-calendar";
|
|
52
52
|
|
|
53
53
|
function MyCustomCalendar({ events }) {
|
|
54
|
-
const {
|
|
54
|
+
const {
|
|
55
|
+
canMove,
|
|
56
|
+
hasOverlap,
|
|
57
|
+
findConflicts,
|
|
58
|
+
getEventsForDay,
|
|
59
|
+
getEventsForWeek,
|
|
60
|
+
findAvailableSlots
|
|
61
|
+
} = useCalendarEvents(events);
|
|
55
62
|
|
|
56
63
|
const handleDrop = (eventId, newStart) => {
|
|
57
64
|
if (canMove(eventId, newStart)) {
|
|
58
|
-
// Safe to move
|
|
59
65
|
updateEvent(eventId, newStart);
|
|
60
66
|
} else {
|
|
61
|
-
// Conflict detected
|
|
62
67
|
toast.error("Time slot occupied");
|
|
63
68
|
}
|
|
64
69
|
};
|
|
70
|
+
|
|
71
|
+
// Get available 60-min slots for today (8am-6pm)
|
|
72
|
+
const slots = findAvailableSlots(new Date(), 60);
|
|
65
73
|
}
|
|
66
74
|
```
|
|
67
75
|
|
|
@@ -103,9 +111,62 @@ interface CalendarConfig {
|
|
|
103
111
|
edit?: ReactNode;
|
|
104
112
|
close?: ReactNode;
|
|
105
113
|
};
|
|
114
|
+
renderColumnHeader?: (props: ColumnHeaderProps) => ReactNode;
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
interface ColumnHeaderProps {
|
|
118
|
+
date: Date;
|
|
119
|
+
index: number;
|
|
120
|
+
isToday: boolean;
|
|
121
|
+
locale: string;
|
|
106
122
|
}
|
|
107
123
|
```
|
|
108
124
|
|
|
125
|
+
## Custom Column Headers
|
|
126
|
+
|
|
127
|
+
Transform the calendar from weekdays to any resource type:
|
|
128
|
+
|
|
129
|
+
```tsx
|
|
130
|
+
// Padel courts booking
|
|
131
|
+
<Calendar
|
|
132
|
+
events={courtEvents}
|
|
133
|
+
config={{
|
|
134
|
+
renderColumnHeader: ({ index }) => (
|
|
135
|
+
<div className="text-center font-semibold">
|
|
136
|
+
Court {index + 1}
|
|
137
|
+
</div>
|
|
138
|
+
)
|
|
139
|
+
}}
|
|
140
|
+
/>
|
|
141
|
+
|
|
142
|
+
// Meeting rooms
|
|
143
|
+
<Calendar
|
|
144
|
+
events={roomEvents}
|
|
145
|
+
config={{
|
|
146
|
+
renderColumnHeader: ({ index }) => {
|
|
147
|
+
const rooms = ["Sala A", "Sala B", "Sala C", "Sala D", "Sala E", "Sala F", "Sala G"];
|
|
148
|
+
return <span>{rooms[index]}</span>;
|
|
149
|
+
}
|
|
150
|
+
}}
|
|
151
|
+
/>
|
|
152
|
+
|
|
153
|
+
// Employees schedule
|
|
154
|
+
<Calendar
|
|
155
|
+
events={shifts}
|
|
156
|
+
config={{
|
|
157
|
+
renderColumnHeader: ({ index }) => {
|
|
158
|
+
const team = ["Ana", "Carlos", "María", "Pedro", "Laura", "Diego", "Sofia"];
|
|
159
|
+
return (
|
|
160
|
+
<div className="flex flex-col items-center">
|
|
161
|
+
<img src={`/avatars/${index}.jpg`} className="w-8 h-8 rounded-full" />
|
|
162
|
+
<span className="text-sm">{team[index]}</span>
|
|
163
|
+
</div>
|
|
164
|
+
);
|
|
165
|
+
}
|
|
166
|
+
}}
|
|
167
|
+
/>
|
|
168
|
+
```
|
|
169
|
+
|
|
109
170
|
## Features
|
|
110
171
|
|
|
111
172
|
- Drag & drop events between time slots
|
|
@@ -113,6 +174,7 @@ interface CalendarConfig {
|
|
|
113
174
|
- Block time slots
|
|
114
175
|
- Week navigation
|
|
115
176
|
- Auto-scroll to current hour
|
|
177
|
+
- Custom column headers (resources, courts, rooms, employees)
|
|
116
178
|
- Customizable icons
|
|
117
179
|
- Locale support
|
|
118
180
|
- TypeScript support
|
|
@@ -121,6 +183,12 @@ interface CalendarConfig {
|
|
|
121
183
|
|
|
122
184
|
- React 18+ or 19+
|
|
123
185
|
|
|
186
|
+
## Author
|
|
187
|
+
|
|
188
|
+
Made by [@blissito](https://github.com/blissito)
|
|
189
|
+
|
|
190
|
+
Learn React & web development at [fixtergeek.com](https://fixtergeek.com)
|
|
191
|
+
|
|
124
192
|
## License
|
|
125
193
|
|
|
126
194
|
MIT
|
package/dist/index.cjs
CHANGED
|
@@ -6,7 +6,88 @@ var utilities = require('@dnd-kit/utilities');
|
|
|
6
6
|
var jsxRuntime = require('react/jsx-runtime');
|
|
7
7
|
|
|
8
8
|
// src/Calendar.tsx
|
|
9
|
-
|
|
9
|
+
|
|
10
|
+
// src/utils.ts
|
|
11
|
+
var getMonday = (today = /* @__PURE__ */ new Date()) => {
|
|
12
|
+
const d = new Date(today);
|
|
13
|
+
const day = d.getDay();
|
|
14
|
+
const diff = d.getDate() - day + (day === 0 ? -6 : 1);
|
|
15
|
+
return new Date(d.setDate(diff));
|
|
16
|
+
};
|
|
17
|
+
var completeWeek = (date) => {
|
|
18
|
+
const startDate = new Date(date);
|
|
19
|
+
const day = startDate.getDay();
|
|
20
|
+
const offset = day === 0 ? -6 : 1 - day;
|
|
21
|
+
startDate.setDate(startDate.getDate() + offset);
|
|
22
|
+
return Array.from({ length: 7 }, (_, i) => {
|
|
23
|
+
const d = new Date(startDate);
|
|
24
|
+
d.setDate(d.getDate() + i);
|
|
25
|
+
return d;
|
|
26
|
+
});
|
|
27
|
+
};
|
|
28
|
+
var generateHours = ({
|
|
29
|
+
fromHour,
|
|
30
|
+
toHour
|
|
31
|
+
}) => {
|
|
32
|
+
return Array.from({ length: toHour - fromHour }, (_, index) => {
|
|
33
|
+
const hour = fromHour + index;
|
|
34
|
+
return hour < 10 ? `0${hour}:00` : `${hour}:00`;
|
|
35
|
+
});
|
|
36
|
+
};
|
|
37
|
+
var isToday = (date) => {
|
|
38
|
+
const today = /* @__PURE__ */ new Date();
|
|
39
|
+
return date.getDate() === today.getDate() && date.getMonth() === today.getMonth() && date.getFullYear() === today.getFullYear();
|
|
40
|
+
};
|
|
41
|
+
var areSameDates = (d1, d2) => {
|
|
42
|
+
if (!d1 || !d2) return false;
|
|
43
|
+
return d1.getDate() === d2.getDate() && d1.getMonth() === d2.getMonth() && d1.getFullYear() === d2.getFullYear();
|
|
44
|
+
};
|
|
45
|
+
var addDaysToDate = (date, days) => {
|
|
46
|
+
const result = new Date(date);
|
|
47
|
+
result.setDate(result.getDate() + days);
|
|
48
|
+
return result;
|
|
49
|
+
};
|
|
50
|
+
var addMinutesToDate = (date, mins) => {
|
|
51
|
+
const result = new Date(date);
|
|
52
|
+
result.setMinutes(result.getMinutes() + mins);
|
|
53
|
+
return result;
|
|
54
|
+
};
|
|
55
|
+
var fromMinsToTimeString = (mins) => {
|
|
56
|
+
const h = Math.floor(mins / 60);
|
|
57
|
+
const m = mins % 60;
|
|
58
|
+
return `${h < 10 ? "0" + h : h}:${m < 10 ? "0" + m : m}`;
|
|
59
|
+
};
|
|
60
|
+
var fromMinsToLocaleTimeString = (mins, locale = "es-MX") => {
|
|
61
|
+
const h = Math.floor(mins / 60);
|
|
62
|
+
const m = mins % 60;
|
|
63
|
+
const today = /* @__PURE__ */ new Date();
|
|
64
|
+
today.setHours(h, m, 0, 0);
|
|
65
|
+
return today.toLocaleTimeString(locale);
|
|
66
|
+
};
|
|
67
|
+
var fromDateToTimeString = (date, locale = "es-MX") => {
|
|
68
|
+
return new Date(date).toLocaleTimeString(locale);
|
|
69
|
+
};
|
|
70
|
+
var getDaysInMonth = (date) => {
|
|
71
|
+
const firstDay = new Date(date.getFullYear(), date.getMonth(), 1);
|
|
72
|
+
const lastDay = new Date(date.getFullYear(), date.getMonth() + 1, 0);
|
|
73
|
+
const numberOfMissing = 6 - lastDay.getDay();
|
|
74
|
+
const leftOffset = firstDay.getDay();
|
|
75
|
+
firstDay.setDate(firstDay.getDate() - leftOffset);
|
|
76
|
+
const days = [];
|
|
77
|
+
days.push(new Date(firstDay));
|
|
78
|
+
while (firstDay < lastDay) {
|
|
79
|
+
firstDay.setDate(firstDay.getDate() + 1);
|
|
80
|
+
days.push(new Date(firstDay));
|
|
81
|
+
}
|
|
82
|
+
for (let i = 0; i < numberOfMissing; i++) {
|
|
83
|
+
firstDay.setDate(firstDay.getDate() + 1);
|
|
84
|
+
days.push(new Date(firstDay));
|
|
85
|
+
}
|
|
86
|
+
return days;
|
|
87
|
+
};
|
|
88
|
+
|
|
89
|
+
// src/useCalendarEvents.ts
|
|
90
|
+
function useCalendarEvents(events) {
|
|
10
91
|
const hasOverlap = react.useCallback(
|
|
11
92
|
(start, duration, excludeId) => {
|
|
12
93
|
const hour = start.getHours() + start.getMinutes() / 60;
|
|
@@ -49,21 +130,60 @@ function useEventOverlap(events) {
|
|
|
49
130
|
},
|
|
50
131
|
[events, hasOverlap]
|
|
51
132
|
);
|
|
52
|
-
const getEventsForDay = react.
|
|
53
|
-
|
|
133
|
+
const getEventsForDay = react.useCallback(
|
|
134
|
+
(date) => {
|
|
54
135
|
return events.filter((event) => {
|
|
55
136
|
const eventDate = new Date(event.start);
|
|
56
137
|
return eventDate.getDate() === date.getDate() && eventDate.getMonth() === date.getMonth() && eventDate.getFullYear() === date.getFullYear();
|
|
57
138
|
});
|
|
58
|
-
}
|
|
59
|
-
|
|
139
|
+
},
|
|
140
|
+
[events]
|
|
141
|
+
);
|
|
142
|
+
const getEventsForWeek = react.useCallback(
|
|
143
|
+
(date) => {
|
|
144
|
+
const week = completeWeek(date);
|
|
145
|
+
const start = week[0];
|
|
146
|
+
const end = new Date(week[6]);
|
|
147
|
+
end.setHours(23, 59, 59, 999);
|
|
148
|
+
return events.filter((event) => {
|
|
149
|
+
const eventDate = new Date(event.start);
|
|
150
|
+
return eventDate >= start && eventDate <= end;
|
|
151
|
+
});
|
|
152
|
+
},
|
|
153
|
+
[events]
|
|
154
|
+
);
|
|
155
|
+
const findAvailableSlots = react.useCallback(
|
|
156
|
+
(date, duration, startHour = 8, endHour = 18) => {
|
|
157
|
+
const slots = [];
|
|
158
|
+
const dayEvents = getEventsForDay(date);
|
|
159
|
+
for (let hour = startHour; hour <= endHour - duration / 60; hour++) {
|
|
160
|
+
const slotStart = new Date(date);
|
|
161
|
+
slotStart.setHours(hour, 0, 0, 0);
|
|
162
|
+
const hasConflict = dayEvents.some((event) => {
|
|
163
|
+
const eventStart = new Date(event.start);
|
|
164
|
+
const eventHour = eventStart.getHours();
|
|
165
|
+
const eventEnd = eventHour + event.duration / 60;
|
|
166
|
+
const slotEnd = hour + duration / 60;
|
|
167
|
+
return hour >= eventHour && hour < eventEnd || slotEnd > eventHour && slotEnd <= eventEnd || hour <= eventHour && slotEnd >= eventEnd;
|
|
168
|
+
});
|
|
169
|
+
if (!hasConflict) {
|
|
170
|
+
slots.push(slotStart);
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
return slots;
|
|
174
|
+
},
|
|
175
|
+
[getEventsForDay]
|
|
176
|
+
);
|
|
60
177
|
return {
|
|
61
178
|
hasOverlap,
|
|
62
179
|
findConflicts,
|
|
63
180
|
canMove,
|
|
64
|
-
getEventsForDay
|
|
181
|
+
getEventsForDay,
|
|
182
|
+
getEventsForWeek,
|
|
183
|
+
findAvailableSlots
|
|
65
184
|
};
|
|
66
185
|
}
|
|
186
|
+
var useEventOverlap = useCalendarEvents;
|
|
67
187
|
function useClickOutside({
|
|
68
188
|
isActive,
|
|
69
189
|
onOutsideClick
|
|
@@ -93,101 +213,33 @@ function formatDate(date, locale = "es-MX") {
|
|
|
93
213
|
minute: "2-digit"
|
|
94
214
|
});
|
|
95
215
|
}
|
|
96
|
-
|
|
97
|
-
// src/utils.ts
|
|
98
|
-
var getMonday = (today = /* @__PURE__ */ new Date()) => {
|
|
99
|
-
const d = new Date(today);
|
|
100
|
-
const day = d.getDay();
|
|
101
|
-
const diff = d.getDate() - day + (day === 0 ? -6 : 1);
|
|
102
|
-
return new Date(d.setDate(diff));
|
|
103
|
-
};
|
|
104
|
-
var completeWeek = (date) => {
|
|
105
|
-
const startDate = new Date(date);
|
|
106
|
-
const day = startDate.getDay();
|
|
107
|
-
const offset = day === 0 ? -6 : 1 - day;
|
|
108
|
-
startDate.setDate(startDate.getDate() + offset);
|
|
109
|
-
return Array.from({ length: 7 }, (_, i) => {
|
|
110
|
-
const d = new Date(startDate);
|
|
111
|
-
d.setDate(d.getDate() + i);
|
|
112
|
-
return d;
|
|
113
|
-
});
|
|
114
|
-
};
|
|
115
|
-
var generateHours = ({
|
|
116
|
-
fromHour,
|
|
117
|
-
toHour
|
|
118
|
-
}) => {
|
|
119
|
-
return Array.from({ length: toHour - fromHour }, (_, index) => {
|
|
120
|
-
const hour = fromHour + index;
|
|
121
|
-
return hour < 10 ? `0${hour}:00` : `${hour}:00`;
|
|
122
|
-
});
|
|
123
|
-
};
|
|
124
|
-
var isToday = (date) => {
|
|
125
|
-
const today = /* @__PURE__ */ new Date();
|
|
126
|
-
return date.getDate() === today.getDate() && date.getMonth() === today.getMonth() && date.getFullYear() === today.getFullYear();
|
|
127
|
-
};
|
|
128
|
-
var areSameDates = (d1, d2) => {
|
|
129
|
-
if (!d1 || !d2) return false;
|
|
130
|
-
return d1.getDate() === d2.getDate() && d1.getMonth() === d2.getMonth() && d1.getFullYear() === d2.getFullYear();
|
|
131
|
-
};
|
|
132
|
-
var addDaysToDate = (date, days) => {
|
|
133
|
-
const result = new Date(date);
|
|
134
|
-
result.setDate(result.getDate() + days);
|
|
135
|
-
return result;
|
|
136
|
-
};
|
|
137
|
-
var addMinutesToDate = (date, mins) => {
|
|
138
|
-
const result = new Date(date);
|
|
139
|
-
result.setMinutes(result.getMinutes() + mins);
|
|
140
|
-
return result;
|
|
141
|
-
};
|
|
142
|
-
var fromMinsToTimeString = (mins) => {
|
|
143
|
-
const h = Math.floor(mins / 60);
|
|
144
|
-
const m = mins % 60;
|
|
145
|
-
return `${h < 10 ? "0" + h : h}:${m < 10 ? "0" + m : m}`;
|
|
146
|
-
};
|
|
147
|
-
var fromMinsToLocaleTimeString = (mins, locale = "es-MX") => {
|
|
148
|
-
const h = Math.floor(mins / 60);
|
|
149
|
-
const m = mins % 60;
|
|
150
|
-
const today = /* @__PURE__ */ new Date();
|
|
151
|
-
today.setHours(h, m, 0, 0);
|
|
152
|
-
return today.toLocaleTimeString(locale);
|
|
153
|
-
};
|
|
154
|
-
var fromDateToTimeString = (date, locale = "es-MX") => {
|
|
155
|
-
return new Date(date).toLocaleTimeString(locale);
|
|
156
|
-
};
|
|
157
|
-
var getDaysInMonth = (date) => {
|
|
158
|
-
const firstDay = new Date(date.getFullYear(), date.getMonth(), 1);
|
|
159
|
-
const lastDay = new Date(date.getFullYear(), date.getMonth() + 1, 0);
|
|
160
|
-
const numberOfMissing = 6 - lastDay.getDay();
|
|
161
|
-
const leftOffset = firstDay.getDay();
|
|
162
|
-
firstDay.setDate(firstDay.getDate() - leftOffset);
|
|
163
|
-
const days = [];
|
|
164
|
-
days.push(new Date(firstDay));
|
|
165
|
-
while (firstDay < lastDay) {
|
|
166
|
-
firstDay.setDate(firstDay.getDate() + 1);
|
|
167
|
-
days.push(new Date(firstDay));
|
|
168
|
-
}
|
|
169
|
-
for (let i = 0; i < numberOfMissing; i++) {
|
|
170
|
-
firstDay.setDate(firstDay.getDate() + 1);
|
|
171
|
-
days.push(new Date(firstDay));
|
|
172
|
-
}
|
|
173
|
-
return days;
|
|
174
|
-
};
|
|
175
216
|
var cn = (...classes) => classes.filter(Boolean).join(" ");
|
|
176
217
|
var DefaultTrashIcon = () => /* @__PURE__ */ jsxRuntime.jsx("svg", { viewBox: "0 0 24 24", className: "w-4 h-4", fill: "currentColor", children: /* @__PURE__ */ jsxRuntime.jsx("path", { d: "M6 19c0 1.1.9 2 2 2h8c1.1 0 2-.9 2-2V7H6v12zM19 4h-3.5l-1-1h-5l-1 1H5v2h14V4z" }) });
|
|
177
218
|
var DefaultEditIcon = () => /* @__PURE__ */ jsxRuntime.jsx("svg", { viewBox: "0 0 24 24", className: "w-4 h-4", fill: "currentColor", children: /* @__PURE__ */ jsxRuntime.jsx("path", { d: "M3 17.25V21h3.75L17.81 9.94l-3.75-3.75L3 17.25zM20.71 7.04c.39-.39.39-1.02 0-1.41l-2.34-2.34c-.39-.39-1.02-.39-1.41 0l-1.83 1.83 3.75 3.75 1.83-1.83z" }) });
|
|
178
219
|
var DefaultCloseIcon = () => /* @__PURE__ */ jsxRuntime.jsx("svg", { viewBox: "0 0 24 24", className: "w-4 h-4", fill: "currentColor", children: /* @__PURE__ */ jsxRuntime.jsx("path", { d: "M19 6.41L17.59 5 12 10.59 6.41 5 5 6.41 10.59 12 5 17.59 6.41 19 12 13.41 17.59 19 19 17.59 13.41 12z" }) });
|
|
179
|
-
var DayHeader = ({
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
220
|
+
var DayHeader = ({
|
|
221
|
+
date,
|
|
222
|
+
locale,
|
|
223
|
+
index,
|
|
224
|
+
renderColumnHeader
|
|
225
|
+
}) => {
|
|
226
|
+
const isToday2 = isToday(date);
|
|
227
|
+
if (renderColumnHeader) {
|
|
228
|
+
return /* @__PURE__ */ jsxRuntime.jsx("div", { className: "grid place-items-center", children: renderColumnHeader({ date, index, isToday: isToday2, locale }) });
|
|
229
|
+
}
|
|
230
|
+
return /* @__PURE__ */ jsxRuntime.jsxs("p", { className: "grid place-items-center", children: [
|
|
231
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "capitalize", children: date.toLocaleDateString(locale, { weekday: "short" }) }),
|
|
232
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
233
|
+
"span",
|
|
234
|
+
{
|
|
235
|
+
className: cn(
|
|
236
|
+
isToday2 && "bg-blue-500 rounded-full p-1 text-white"
|
|
237
|
+
),
|
|
238
|
+
children: date.getDate()
|
|
239
|
+
}
|
|
240
|
+
)
|
|
241
|
+
] });
|
|
242
|
+
};
|
|
191
243
|
function Calendar({
|
|
192
244
|
date = /* @__PURE__ */ new Date(),
|
|
193
245
|
events = [],
|
|
@@ -198,10 +250,10 @@ function Calendar({
|
|
|
198
250
|
onRemoveBlock,
|
|
199
251
|
config = {}
|
|
200
252
|
}) {
|
|
201
|
-
const { locale = "es-MX", icons = {} } = config;
|
|
253
|
+
const { locale = "es-MX", icons = {}, renderColumnHeader } = config;
|
|
202
254
|
const week = completeWeek(date);
|
|
203
255
|
const [activeId, setActiveId] = react.useState(null);
|
|
204
|
-
const { canMove } =
|
|
256
|
+
const { canMove } = useCalendarEvents(events);
|
|
205
257
|
const sensors = core.useSensors(
|
|
206
258
|
core.useSensor(core.PointerSensor, {
|
|
207
259
|
activationConstraint: { distance: 8 }
|
|
@@ -250,7 +302,16 @@ function Calendar({
|
|
|
250
302
|
/* @__PURE__ */ jsxRuntime.jsxs("article", { className: "w-full bg-white shadow rounded-xl", children: [
|
|
251
303
|
/* @__PURE__ */ jsxRuntime.jsxs("section", { className: "grid grid-cols-8 place-items-center py-4", children: [
|
|
252
304
|
/* @__PURE__ */ jsxRuntime.jsx("p", { children: /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-sm text-gray-500", children: Intl.DateTimeFormat().resolvedOptions().timeZone }) }),
|
|
253
|
-
week.map((day) => /* @__PURE__ */ jsxRuntime.jsx(
|
|
305
|
+
week.map((day, index) => /* @__PURE__ */ jsxRuntime.jsx(
|
|
306
|
+
DayHeader,
|
|
307
|
+
{
|
|
308
|
+
date: day,
|
|
309
|
+
locale,
|
|
310
|
+
index,
|
|
311
|
+
renderColumnHeader
|
|
312
|
+
},
|
|
313
|
+
day.toISOString()
|
|
314
|
+
))
|
|
254
315
|
] }),
|
|
255
316
|
/* @__PURE__ */ jsxRuntime.jsxs("section", { className: "grid grid-cols-8 max-h-[80vh] overflow-y-auto", children: [
|
|
256
317
|
/* @__PURE__ */ jsxRuntime.jsx(TimeColumn, {}),
|
|
@@ -581,5 +642,6 @@ exports.generateHours = generateHours;
|
|
|
581
642
|
exports.getDaysInMonth = getDaysInMonth;
|
|
582
643
|
exports.getMonday = getMonday;
|
|
583
644
|
exports.isToday = isToday;
|
|
645
|
+
exports.useCalendarEvents = useCalendarEvents;
|
|
584
646
|
exports.useClickOutside = useClickOutside;
|
|
585
647
|
exports.useEventOverlap = useEventOverlap;
|
package/dist/index.d.cts
CHANGED
|
@@ -1,6 +1,20 @@
|
|
|
1
1
|
import * as react_jsx_runtime from 'react/jsx-runtime';
|
|
2
2
|
import { ReactNode, RefObject } from 'react';
|
|
3
3
|
|
|
4
|
+
/**
|
|
5
|
+
* Props passed to custom column header renderer
|
|
6
|
+
* Use this to build custom headers for resources (courts, rooms, employees, etc.)
|
|
7
|
+
*/
|
|
8
|
+
interface ColumnHeaderProps {
|
|
9
|
+
/** The date for this column */
|
|
10
|
+
date: Date;
|
|
11
|
+
/** Column index (0-6) */
|
|
12
|
+
index: number;
|
|
13
|
+
/** Whether this column represents today */
|
|
14
|
+
isToday: boolean;
|
|
15
|
+
/** The configured locale */
|
|
16
|
+
locale: string;
|
|
17
|
+
}
|
|
4
18
|
/**
|
|
5
19
|
* Generic calendar event - decoupled from any ORM
|
|
6
20
|
*/
|
|
@@ -30,6 +44,23 @@ interface CalendarConfig {
|
|
|
30
44
|
edit?: ReactNode;
|
|
31
45
|
close?: ReactNode;
|
|
32
46
|
};
|
|
47
|
+
/**
|
|
48
|
+
* Custom renderer for column headers.
|
|
49
|
+
* Use this to display resources (courts, rooms, employees) instead of weekdays.
|
|
50
|
+
*
|
|
51
|
+
* @example
|
|
52
|
+
* // Padel courts
|
|
53
|
+
* renderColumnHeader: ({ index }) => <span>Court {index + 1}</span>
|
|
54
|
+
*
|
|
55
|
+
* @example
|
|
56
|
+
* // With custom styling
|
|
57
|
+
* renderColumnHeader: ({ date, isToday }) => (
|
|
58
|
+
* <div className={isToday ? "font-bold" : ""}>
|
|
59
|
+
* {date.toLocaleDateString("en", { weekday: "short" })}
|
|
60
|
+
* </div>
|
|
61
|
+
* )
|
|
62
|
+
*/
|
|
63
|
+
renderColumnHeader?: (props: ColumnHeaderProps) => ReactNode;
|
|
33
64
|
}
|
|
34
65
|
/**
|
|
35
66
|
* Calendar component props
|
|
@@ -56,15 +87,17 @@ interface CalendarProps {
|
|
|
56
87
|
declare function Calendar({ date, events, onEventClick, onNewEvent, onEventMove, onAddBlock, onRemoveBlock, config, }: CalendarProps): react_jsx_runtime.JSX.Element;
|
|
57
88
|
|
|
58
89
|
/**
|
|
59
|
-
* Hook for
|
|
60
|
-
* Can be used standalone (headless) or with the Calendar component
|
|
90
|
+
* Hook for managing calendar events - overlap detection, filtering, and availability
|
|
61
91
|
*/
|
|
62
|
-
declare function
|
|
92
|
+
declare function useCalendarEvents(events: CalendarEvent[]): {
|
|
63
93
|
hasOverlap: (start: Date, duration: number, excludeId?: string) => boolean;
|
|
64
94
|
findConflicts: (start: Date, duration: number, excludeId?: string) => CalendarEvent[];
|
|
65
95
|
canMove: (eventId: string, newStart: Date) => boolean;
|
|
66
96
|
getEventsForDay: (date: Date) => CalendarEvent[];
|
|
97
|
+
getEventsForWeek: (date: Date) => CalendarEvent[];
|
|
98
|
+
findAvailableSlots: (date: Date, duration: number, startHour?: number, endHour?: number) => Date[];
|
|
67
99
|
};
|
|
100
|
+
declare const useEventOverlap: typeof useCalendarEvents;
|
|
68
101
|
|
|
69
102
|
/**
|
|
70
103
|
* Get the Monday of the week for a given date
|
|
@@ -126,4 +159,4 @@ declare function useClickOutside<T extends HTMLElement>({ isActive, onOutsideCli
|
|
|
126
159
|
*/
|
|
127
160
|
declare function formatDate(date: Date, locale?: string): string;
|
|
128
161
|
|
|
129
|
-
export { Calendar, type CalendarConfig, type CalendarEvent, type CalendarProps, Calendar as SimpleBigWeekView, addDaysToDate, addMinutesToDate, areSameDates, completeWeek, formatDate, fromDateToTimeString, fromMinsToLocaleTimeString, fromMinsToTimeString, generateHours, getDaysInMonth, getMonday, isToday, useClickOutside, useEventOverlap };
|
|
162
|
+
export { Calendar, type CalendarConfig, type CalendarEvent, type CalendarProps, type ColumnHeaderProps, Calendar as SimpleBigWeekView, addDaysToDate, addMinutesToDate, areSameDates, completeWeek, formatDate, fromDateToTimeString, fromMinsToLocaleTimeString, fromMinsToTimeString, generateHours, getDaysInMonth, getMonday, isToday, useCalendarEvents, useClickOutside, useEventOverlap };
|
package/dist/index.d.ts
CHANGED
|
@@ -1,6 +1,20 @@
|
|
|
1
1
|
import * as react_jsx_runtime from 'react/jsx-runtime';
|
|
2
2
|
import { ReactNode, RefObject } from 'react';
|
|
3
3
|
|
|
4
|
+
/**
|
|
5
|
+
* Props passed to custom column header renderer
|
|
6
|
+
* Use this to build custom headers for resources (courts, rooms, employees, etc.)
|
|
7
|
+
*/
|
|
8
|
+
interface ColumnHeaderProps {
|
|
9
|
+
/** The date for this column */
|
|
10
|
+
date: Date;
|
|
11
|
+
/** Column index (0-6) */
|
|
12
|
+
index: number;
|
|
13
|
+
/** Whether this column represents today */
|
|
14
|
+
isToday: boolean;
|
|
15
|
+
/** The configured locale */
|
|
16
|
+
locale: string;
|
|
17
|
+
}
|
|
4
18
|
/**
|
|
5
19
|
* Generic calendar event - decoupled from any ORM
|
|
6
20
|
*/
|
|
@@ -30,6 +44,23 @@ interface CalendarConfig {
|
|
|
30
44
|
edit?: ReactNode;
|
|
31
45
|
close?: ReactNode;
|
|
32
46
|
};
|
|
47
|
+
/**
|
|
48
|
+
* Custom renderer for column headers.
|
|
49
|
+
* Use this to display resources (courts, rooms, employees) instead of weekdays.
|
|
50
|
+
*
|
|
51
|
+
* @example
|
|
52
|
+
* // Padel courts
|
|
53
|
+
* renderColumnHeader: ({ index }) => <span>Court {index + 1}</span>
|
|
54
|
+
*
|
|
55
|
+
* @example
|
|
56
|
+
* // With custom styling
|
|
57
|
+
* renderColumnHeader: ({ date, isToday }) => (
|
|
58
|
+
* <div className={isToday ? "font-bold" : ""}>
|
|
59
|
+
* {date.toLocaleDateString("en", { weekday: "short" })}
|
|
60
|
+
* </div>
|
|
61
|
+
* )
|
|
62
|
+
*/
|
|
63
|
+
renderColumnHeader?: (props: ColumnHeaderProps) => ReactNode;
|
|
33
64
|
}
|
|
34
65
|
/**
|
|
35
66
|
* Calendar component props
|
|
@@ -56,15 +87,17 @@ interface CalendarProps {
|
|
|
56
87
|
declare function Calendar({ date, events, onEventClick, onNewEvent, onEventMove, onAddBlock, onRemoveBlock, config, }: CalendarProps): react_jsx_runtime.JSX.Element;
|
|
57
88
|
|
|
58
89
|
/**
|
|
59
|
-
* Hook for
|
|
60
|
-
* Can be used standalone (headless) or with the Calendar component
|
|
90
|
+
* Hook for managing calendar events - overlap detection, filtering, and availability
|
|
61
91
|
*/
|
|
62
|
-
declare function
|
|
92
|
+
declare function useCalendarEvents(events: CalendarEvent[]): {
|
|
63
93
|
hasOverlap: (start: Date, duration: number, excludeId?: string) => boolean;
|
|
64
94
|
findConflicts: (start: Date, duration: number, excludeId?: string) => CalendarEvent[];
|
|
65
95
|
canMove: (eventId: string, newStart: Date) => boolean;
|
|
66
96
|
getEventsForDay: (date: Date) => CalendarEvent[];
|
|
97
|
+
getEventsForWeek: (date: Date) => CalendarEvent[];
|
|
98
|
+
findAvailableSlots: (date: Date, duration: number, startHour?: number, endHour?: number) => Date[];
|
|
67
99
|
};
|
|
100
|
+
declare const useEventOverlap: typeof useCalendarEvents;
|
|
68
101
|
|
|
69
102
|
/**
|
|
70
103
|
* Get the Monday of the week for a given date
|
|
@@ -126,4 +159,4 @@ declare function useClickOutside<T extends HTMLElement>({ isActive, onOutsideCli
|
|
|
126
159
|
*/
|
|
127
160
|
declare function formatDate(date: Date, locale?: string): string;
|
|
128
161
|
|
|
129
|
-
export { Calendar, type CalendarConfig, type CalendarEvent, type CalendarProps, Calendar as SimpleBigWeekView, addDaysToDate, addMinutesToDate, areSameDates, completeWeek, formatDate, fromDateToTimeString, fromMinsToLocaleTimeString, fromMinsToTimeString, generateHours, getDaysInMonth, getMonday, isToday, useClickOutside, useEventOverlap };
|
|
162
|
+
export { Calendar, type CalendarConfig, type CalendarEvent, type CalendarProps, type ColumnHeaderProps, Calendar as SimpleBigWeekView, addDaysToDate, addMinutesToDate, areSameDates, completeWeek, formatDate, fromDateToTimeString, fromMinsToLocaleTimeString, fromMinsToTimeString, generateHours, getDaysInMonth, getMonday, isToday, useCalendarEvents, useClickOutside, useEventOverlap };
|
package/dist/index.js
CHANGED
|
@@ -1,10 +1,91 @@
|
|
|
1
|
-
import { useCallback,
|
|
1
|
+
import { useCallback, useRef, useEffect, useState } from 'react';
|
|
2
2
|
import { useSensors, useSensor, PointerSensor, KeyboardSensor, DndContext, closestCenter, DragOverlay, useDroppable, useDraggable } from '@dnd-kit/core';
|
|
3
3
|
import { CSS } from '@dnd-kit/utilities';
|
|
4
4
|
import { jsxs, jsx, Fragment } from 'react/jsx-runtime';
|
|
5
5
|
|
|
6
6
|
// src/Calendar.tsx
|
|
7
|
-
|
|
7
|
+
|
|
8
|
+
// src/utils.ts
|
|
9
|
+
var getMonday = (today = /* @__PURE__ */ new Date()) => {
|
|
10
|
+
const d = new Date(today);
|
|
11
|
+
const day = d.getDay();
|
|
12
|
+
const diff = d.getDate() - day + (day === 0 ? -6 : 1);
|
|
13
|
+
return new Date(d.setDate(diff));
|
|
14
|
+
};
|
|
15
|
+
var completeWeek = (date) => {
|
|
16
|
+
const startDate = new Date(date);
|
|
17
|
+
const day = startDate.getDay();
|
|
18
|
+
const offset = day === 0 ? -6 : 1 - day;
|
|
19
|
+
startDate.setDate(startDate.getDate() + offset);
|
|
20
|
+
return Array.from({ length: 7 }, (_, i) => {
|
|
21
|
+
const d = new Date(startDate);
|
|
22
|
+
d.setDate(d.getDate() + i);
|
|
23
|
+
return d;
|
|
24
|
+
});
|
|
25
|
+
};
|
|
26
|
+
var generateHours = ({
|
|
27
|
+
fromHour,
|
|
28
|
+
toHour
|
|
29
|
+
}) => {
|
|
30
|
+
return Array.from({ length: toHour - fromHour }, (_, index) => {
|
|
31
|
+
const hour = fromHour + index;
|
|
32
|
+
return hour < 10 ? `0${hour}:00` : `${hour}:00`;
|
|
33
|
+
});
|
|
34
|
+
};
|
|
35
|
+
var isToday = (date) => {
|
|
36
|
+
const today = /* @__PURE__ */ new Date();
|
|
37
|
+
return date.getDate() === today.getDate() && date.getMonth() === today.getMonth() && date.getFullYear() === today.getFullYear();
|
|
38
|
+
};
|
|
39
|
+
var areSameDates = (d1, d2) => {
|
|
40
|
+
if (!d1 || !d2) return false;
|
|
41
|
+
return d1.getDate() === d2.getDate() && d1.getMonth() === d2.getMonth() && d1.getFullYear() === d2.getFullYear();
|
|
42
|
+
};
|
|
43
|
+
var addDaysToDate = (date, days) => {
|
|
44
|
+
const result = new Date(date);
|
|
45
|
+
result.setDate(result.getDate() + days);
|
|
46
|
+
return result;
|
|
47
|
+
};
|
|
48
|
+
var addMinutesToDate = (date, mins) => {
|
|
49
|
+
const result = new Date(date);
|
|
50
|
+
result.setMinutes(result.getMinutes() + mins);
|
|
51
|
+
return result;
|
|
52
|
+
};
|
|
53
|
+
var fromMinsToTimeString = (mins) => {
|
|
54
|
+
const h = Math.floor(mins / 60);
|
|
55
|
+
const m = mins % 60;
|
|
56
|
+
return `${h < 10 ? "0" + h : h}:${m < 10 ? "0" + m : m}`;
|
|
57
|
+
};
|
|
58
|
+
var fromMinsToLocaleTimeString = (mins, locale = "es-MX") => {
|
|
59
|
+
const h = Math.floor(mins / 60);
|
|
60
|
+
const m = mins % 60;
|
|
61
|
+
const today = /* @__PURE__ */ new Date();
|
|
62
|
+
today.setHours(h, m, 0, 0);
|
|
63
|
+
return today.toLocaleTimeString(locale);
|
|
64
|
+
};
|
|
65
|
+
var fromDateToTimeString = (date, locale = "es-MX") => {
|
|
66
|
+
return new Date(date).toLocaleTimeString(locale);
|
|
67
|
+
};
|
|
68
|
+
var getDaysInMonth = (date) => {
|
|
69
|
+
const firstDay = new Date(date.getFullYear(), date.getMonth(), 1);
|
|
70
|
+
const lastDay = new Date(date.getFullYear(), date.getMonth() + 1, 0);
|
|
71
|
+
const numberOfMissing = 6 - lastDay.getDay();
|
|
72
|
+
const leftOffset = firstDay.getDay();
|
|
73
|
+
firstDay.setDate(firstDay.getDate() - leftOffset);
|
|
74
|
+
const days = [];
|
|
75
|
+
days.push(new Date(firstDay));
|
|
76
|
+
while (firstDay < lastDay) {
|
|
77
|
+
firstDay.setDate(firstDay.getDate() + 1);
|
|
78
|
+
days.push(new Date(firstDay));
|
|
79
|
+
}
|
|
80
|
+
for (let i = 0; i < numberOfMissing; i++) {
|
|
81
|
+
firstDay.setDate(firstDay.getDate() + 1);
|
|
82
|
+
days.push(new Date(firstDay));
|
|
83
|
+
}
|
|
84
|
+
return days;
|
|
85
|
+
};
|
|
86
|
+
|
|
87
|
+
// src/useCalendarEvents.ts
|
|
88
|
+
function useCalendarEvents(events) {
|
|
8
89
|
const hasOverlap = useCallback(
|
|
9
90
|
(start, duration, excludeId) => {
|
|
10
91
|
const hour = start.getHours() + start.getMinutes() / 60;
|
|
@@ -47,21 +128,60 @@ function useEventOverlap(events) {
|
|
|
47
128
|
},
|
|
48
129
|
[events, hasOverlap]
|
|
49
130
|
);
|
|
50
|
-
const getEventsForDay =
|
|
51
|
-
|
|
131
|
+
const getEventsForDay = useCallback(
|
|
132
|
+
(date) => {
|
|
52
133
|
return events.filter((event) => {
|
|
53
134
|
const eventDate = new Date(event.start);
|
|
54
135
|
return eventDate.getDate() === date.getDate() && eventDate.getMonth() === date.getMonth() && eventDate.getFullYear() === date.getFullYear();
|
|
55
136
|
});
|
|
56
|
-
}
|
|
57
|
-
|
|
137
|
+
},
|
|
138
|
+
[events]
|
|
139
|
+
);
|
|
140
|
+
const getEventsForWeek = useCallback(
|
|
141
|
+
(date) => {
|
|
142
|
+
const week = completeWeek(date);
|
|
143
|
+
const start = week[0];
|
|
144
|
+
const end = new Date(week[6]);
|
|
145
|
+
end.setHours(23, 59, 59, 999);
|
|
146
|
+
return events.filter((event) => {
|
|
147
|
+
const eventDate = new Date(event.start);
|
|
148
|
+
return eventDate >= start && eventDate <= end;
|
|
149
|
+
});
|
|
150
|
+
},
|
|
151
|
+
[events]
|
|
152
|
+
);
|
|
153
|
+
const findAvailableSlots = useCallback(
|
|
154
|
+
(date, duration, startHour = 8, endHour = 18) => {
|
|
155
|
+
const slots = [];
|
|
156
|
+
const dayEvents = getEventsForDay(date);
|
|
157
|
+
for (let hour = startHour; hour <= endHour - duration / 60; hour++) {
|
|
158
|
+
const slotStart = new Date(date);
|
|
159
|
+
slotStart.setHours(hour, 0, 0, 0);
|
|
160
|
+
const hasConflict = dayEvents.some((event) => {
|
|
161
|
+
const eventStart = new Date(event.start);
|
|
162
|
+
const eventHour = eventStart.getHours();
|
|
163
|
+
const eventEnd = eventHour + event.duration / 60;
|
|
164
|
+
const slotEnd = hour + duration / 60;
|
|
165
|
+
return hour >= eventHour && hour < eventEnd || slotEnd > eventHour && slotEnd <= eventEnd || hour <= eventHour && slotEnd >= eventEnd;
|
|
166
|
+
});
|
|
167
|
+
if (!hasConflict) {
|
|
168
|
+
slots.push(slotStart);
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
return slots;
|
|
172
|
+
},
|
|
173
|
+
[getEventsForDay]
|
|
174
|
+
);
|
|
58
175
|
return {
|
|
59
176
|
hasOverlap,
|
|
60
177
|
findConflicts,
|
|
61
178
|
canMove,
|
|
62
|
-
getEventsForDay
|
|
179
|
+
getEventsForDay,
|
|
180
|
+
getEventsForWeek,
|
|
181
|
+
findAvailableSlots
|
|
63
182
|
};
|
|
64
183
|
}
|
|
184
|
+
var useEventOverlap = useCalendarEvents;
|
|
65
185
|
function useClickOutside({
|
|
66
186
|
isActive,
|
|
67
187
|
onOutsideClick
|
|
@@ -91,101 +211,33 @@ function formatDate(date, locale = "es-MX") {
|
|
|
91
211
|
minute: "2-digit"
|
|
92
212
|
});
|
|
93
213
|
}
|
|
94
|
-
|
|
95
|
-
// src/utils.ts
|
|
96
|
-
var getMonday = (today = /* @__PURE__ */ new Date()) => {
|
|
97
|
-
const d = new Date(today);
|
|
98
|
-
const day = d.getDay();
|
|
99
|
-
const diff = d.getDate() - day + (day === 0 ? -6 : 1);
|
|
100
|
-
return new Date(d.setDate(diff));
|
|
101
|
-
};
|
|
102
|
-
var completeWeek = (date) => {
|
|
103
|
-
const startDate = new Date(date);
|
|
104
|
-
const day = startDate.getDay();
|
|
105
|
-
const offset = day === 0 ? -6 : 1 - day;
|
|
106
|
-
startDate.setDate(startDate.getDate() + offset);
|
|
107
|
-
return Array.from({ length: 7 }, (_, i) => {
|
|
108
|
-
const d = new Date(startDate);
|
|
109
|
-
d.setDate(d.getDate() + i);
|
|
110
|
-
return d;
|
|
111
|
-
});
|
|
112
|
-
};
|
|
113
|
-
var generateHours = ({
|
|
114
|
-
fromHour,
|
|
115
|
-
toHour
|
|
116
|
-
}) => {
|
|
117
|
-
return Array.from({ length: toHour - fromHour }, (_, index) => {
|
|
118
|
-
const hour = fromHour + index;
|
|
119
|
-
return hour < 10 ? `0${hour}:00` : `${hour}:00`;
|
|
120
|
-
});
|
|
121
|
-
};
|
|
122
|
-
var isToday = (date) => {
|
|
123
|
-
const today = /* @__PURE__ */ new Date();
|
|
124
|
-
return date.getDate() === today.getDate() && date.getMonth() === today.getMonth() && date.getFullYear() === today.getFullYear();
|
|
125
|
-
};
|
|
126
|
-
var areSameDates = (d1, d2) => {
|
|
127
|
-
if (!d1 || !d2) return false;
|
|
128
|
-
return d1.getDate() === d2.getDate() && d1.getMonth() === d2.getMonth() && d1.getFullYear() === d2.getFullYear();
|
|
129
|
-
};
|
|
130
|
-
var addDaysToDate = (date, days) => {
|
|
131
|
-
const result = new Date(date);
|
|
132
|
-
result.setDate(result.getDate() + days);
|
|
133
|
-
return result;
|
|
134
|
-
};
|
|
135
|
-
var addMinutesToDate = (date, mins) => {
|
|
136
|
-
const result = new Date(date);
|
|
137
|
-
result.setMinutes(result.getMinutes() + mins);
|
|
138
|
-
return result;
|
|
139
|
-
};
|
|
140
|
-
var fromMinsToTimeString = (mins) => {
|
|
141
|
-
const h = Math.floor(mins / 60);
|
|
142
|
-
const m = mins % 60;
|
|
143
|
-
return `${h < 10 ? "0" + h : h}:${m < 10 ? "0" + m : m}`;
|
|
144
|
-
};
|
|
145
|
-
var fromMinsToLocaleTimeString = (mins, locale = "es-MX") => {
|
|
146
|
-
const h = Math.floor(mins / 60);
|
|
147
|
-
const m = mins % 60;
|
|
148
|
-
const today = /* @__PURE__ */ new Date();
|
|
149
|
-
today.setHours(h, m, 0, 0);
|
|
150
|
-
return today.toLocaleTimeString(locale);
|
|
151
|
-
};
|
|
152
|
-
var fromDateToTimeString = (date, locale = "es-MX") => {
|
|
153
|
-
return new Date(date).toLocaleTimeString(locale);
|
|
154
|
-
};
|
|
155
|
-
var getDaysInMonth = (date) => {
|
|
156
|
-
const firstDay = new Date(date.getFullYear(), date.getMonth(), 1);
|
|
157
|
-
const lastDay = new Date(date.getFullYear(), date.getMonth() + 1, 0);
|
|
158
|
-
const numberOfMissing = 6 - lastDay.getDay();
|
|
159
|
-
const leftOffset = firstDay.getDay();
|
|
160
|
-
firstDay.setDate(firstDay.getDate() - leftOffset);
|
|
161
|
-
const days = [];
|
|
162
|
-
days.push(new Date(firstDay));
|
|
163
|
-
while (firstDay < lastDay) {
|
|
164
|
-
firstDay.setDate(firstDay.getDate() + 1);
|
|
165
|
-
days.push(new Date(firstDay));
|
|
166
|
-
}
|
|
167
|
-
for (let i = 0; i < numberOfMissing; i++) {
|
|
168
|
-
firstDay.setDate(firstDay.getDate() + 1);
|
|
169
|
-
days.push(new Date(firstDay));
|
|
170
|
-
}
|
|
171
|
-
return days;
|
|
172
|
-
};
|
|
173
214
|
var cn = (...classes) => classes.filter(Boolean).join(" ");
|
|
174
215
|
var DefaultTrashIcon = () => /* @__PURE__ */ jsx("svg", { viewBox: "0 0 24 24", className: "w-4 h-4", fill: "currentColor", children: /* @__PURE__ */ jsx("path", { d: "M6 19c0 1.1.9 2 2 2h8c1.1 0 2-.9 2-2V7H6v12zM19 4h-3.5l-1-1h-5l-1 1H5v2h14V4z" }) });
|
|
175
216
|
var DefaultEditIcon = () => /* @__PURE__ */ jsx("svg", { viewBox: "0 0 24 24", className: "w-4 h-4", fill: "currentColor", children: /* @__PURE__ */ jsx("path", { d: "M3 17.25V21h3.75L17.81 9.94l-3.75-3.75L3 17.25zM20.71 7.04c.39-.39.39-1.02 0-1.41l-2.34-2.34c-.39-.39-1.02-.39-1.41 0l-1.83 1.83 3.75 3.75 1.83-1.83z" }) });
|
|
176
217
|
var DefaultCloseIcon = () => /* @__PURE__ */ jsx("svg", { viewBox: "0 0 24 24", className: "w-4 h-4", fill: "currentColor", children: /* @__PURE__ */ jsx("path", { d: "M19 6.41L17.59 5 12 10.59 6.41 5 5 6.41 10.59 12 5 17.59 6.41 19 12 13.41 17.59 19 19 17.59 13.41 12z" }) });
|
|
177
|
-
var DayHeader = ({
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
218
|
+
var DayHeader = ({
|
|
219
|
+
date,
|
|
220
|
+
locale,
|
|
221
|
+
index,
|
|
222
|
+
renderColumnHeader
|
|
223
|
+
}) => {
|
|
224
|
+
const isToday2 = isToday(date);
|
|
225
|
+
if (renderColumnHeader) {
|
|
226
|
+
return /* @__PURE__ */ jsx("div", { className: "grid place-items-center", children: renderColumnHeader({ date, index, isToday: isToday2, locale }) });
|
|
227
|
+
}
|
|
228
|
+
return /* @__PURE__ */ jsxs("p", { className: "grid place-items-center", children: [
|
|
229
|
+
/* @__PURE__ */ jsx("span", { className: "capitalize", children: date.toLocaleDateString(locale, { weekday: "short" }) }),
|
|
230
|
+
/* @__PURE__ */ jsx(
|
|
231
|
+
"span",
|
|
232
|
+
{
|
|
233
|
+
className: cn(
|
|
234
|
+
isToday2 && "bg-blue-500 rounded-full p-1 text-white"
|
|
235
|
+
),
|
|
236
|
+
children: date.getDate()
|
|
237
|
+
}
|
|
238
|
+
)
|
|
239
|
+
] });
|
|
240
|
+
};
|
|
189
241
|
function Calendar({
|
|
190
242
|
date = /* @__PURE__ */ new Date(),
|
|
191
243
|
events = [],
|
|
@@ -196,10 +248,10 @@ function Calendar({
|
|
|
196
248
|
onRemoveBlock,
|
|
197
249
|
config = {}
|
|
198
250
|
}) {
|
|
199
|
-
const { locale = "es-MX", icons = {} } = config;
|
|
251
|
+
const { locale = "es-MX", icons = {}, renderColumnHeader } = config;
|
|
200
252
|
const week = completeWeek(date);
|
|
201
253
|
const [activeId, setActiveId] = useState(null);
|
|
202
|
-
const { canMove } =
|
|
254
|
+
const { canMove } = useCalendarEvents(events);
|
|
203
255
|
const sensors = useSensors(
|
|
204
256
|
useSensor(PointerSensor, {
|
|
205
257
|
activationConstraint: { distance: 8 }
|
|
@@ -248,7 +300,16 @@ function Calendar({
|
|
|
248
300
|
/* @__PURE__ */ jsxs("article", { className: "w-full bg-white shadow rounded-xl", children: [
|
|
249
301
|
/* @__PURE__ */ jsxs("section", { className: "grid grid-cols-8 place-items-center py-4", children: [
|
|
250
302
|
/* @__PURE__ */ jsx("p", { children: /* @__PURE__ */ jsx("span", { className: "text-sm text-gray-500", children: Intl.DateTimeFormat().resolvedOptions().timeZone }) }),
|
|
251
|
-
week.map((day) => /* @__PURE__ */ jsx(
|
|
303
|
+
week.map((day, index) => /* @__PURE__ */ jsx(
|
|
304
|
+
DayHeader,
|
|
305
|
+
{
|
|
306
|
+
date: day,
|
|
307
|
+
locale,
|
|
308
|
+
index,
|
|
309
|
+
renderColumnHeader
|
|
310
|
+
},
|
|
311
|
+
day.toISOString()
|
|
312
|
+
))
|
|
252
313
|
] }),
|
|
253
314
|
/* @__PURE__ */ jsxs("section", { className: "grid grid-cols-8 max-h-[80vh] overflow-y-auto", children: [
|
|
254
315
|
/* @__PURE__ */ jsx(TimeColumn, {}),
|
|
@@ -565,4 +626,4 @@ var Options = ({
|
|
|
565
626
|
);
|
|
566
627
|
};
|
|
567
628
|
|
|
568
|
-
export { Calendar, Calendar as SimpleBigWeekView, addDaysToDate, addMinutesToDate, areSameDates, completeWeek, formatDate, fromDateToTimeString, fromMinsToLocaleTimeString, fromMinsToTimeString, generateHours, getDaysInMonth, getMonday, isToday, useClickOutside, useEventOverlap };
|
|
629
|
+
export { Calendar, Calendar as SimpleBigWeekView, addDaysToDate, addMinutesToDate, areSameDates, completeWeek, formatDate, fromDateToTimeString, fromMinsToLocaleTimeString, fromMinsToTimeString, generateHours, getDaysInMonth, getMonday, isToday, useCalendarEvents, useClickOutside, useEventOverlap };
|