@hectorbliss/denik-calendar 0.0.1 → 0.0.2
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 +21 -7
- package/dist/index.cjs +128 -86
- package/dist/index.d.cts +6 -4
- package/dist/index.d.ts +6 -4
- package/dist/index.js +129 -88
- 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
|
|
|
@@ -121,6 +129,12 @@ interface CalendarConfig {
|
|
|
121
129
|
|
|
122
130
|
- React 18+ or 19+
|
|
123
131
|
|
|
132
|
+
## Author
|
|
133
|
+
|
|
134
|
+
Made by [@blissito](https://github.com/blissito)
|
|
135
|
+
|
|
136
|
+
Learn React & web development at [fixtergeek.com](https://fixtergeek.com)
|
|
137
|
+
|
|
124
138
|
## License
|
|
125
139
|
|
|
126
140
|
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,85 +213,6 @@ 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" }) });
|
|
@@ -201,7 +242,7 @@ function Calendar({
|
|
|
201
242
|
const { locale = "es-MX", icons = {} } = config;
|
|
202
243
|
const week = completeWeek(date);
|
|
203
244
|
const [activeId, setActiveId] = react.useState(null);
|
|
204
|
-
const { canMove } =
|
|
245
|
+
const { canMove } = useCalendarEvents(events);
|
|
205
246
|
const sensors = core.useSensors(
|
|
206
247
|
core.useSensor(core.PointerSensor, {
|
|
207
248
|
activationConstraint: { distance: 8 }
|
|
@@ -581,5 +622,6 @@ exports.generateHours = generateHours;
|
|
|
581
622
|
exports.getDaysInMonth = getDaysInMonth;
|
|
582
623
|
exports.getMonday = getMonday;
|
|
583
624
|
exports.isToday = isToday;
|
|
625
|
+
exports.useCalendarEvents = useCalendarEvents;
|
|
584
626
|
exports.useClickOutside = useClickOutside;
|
|
585
627
|
exports.useEventOverlap = useEventOverlap;
|
package/dist/index.d.cts
CHANGED
|
@@ -56,15 +56,17 @@ interface CalendarProps {
|
|
|
56
56
|
declare function Calendar({ date, events, onEventClick, onNewEvent, onEventMove, onAddBlock, onRemoveBlock, config, }: CalendarProps): react_jsx_runtime.JSX.Element;
|
|
57
57
|
|
|
58
58
|
/**
|
|
59
|
-
* Hook for
|
|
60
|
-
* Can be used standalone (headless) or with the Calendar component
|
|
59
|
+
* Hook for managing calendar events - overlap detection, filtering, and availability
|
|
61
60
|
*/
|
|
62
|
-
declare function
|
|
61
|
+
declare function useCalendarEvents(events: CalendarEvent[]): {
|
|
63
62
|
hasOverlap: (start: Date, duration: number, excludeId?: string) => boolean;
|
|
64
63
|
findConflicts: (start: Date, duration: number, excludeId?: string) => CalendarEvent[];
|
|
65
64
|
canMove: (eventId: string, newStart: Date) => boolean;
|
|
66
65
|
getEventsForDay: (date: Date) => CalendarEvent[];
|
|
66
|
+
getEventsForWeek: (date: Date) => CalendarEvent[];
|
|
67
|
+
findAvailableSlots: (date: Date, duration: number, startHour?: number, endHour?: number) => Date[];
|
|
67
68
|
};
|
|
69
|
+
declare const useEventOverlap: typeof useCalendarEvents;
|
|
68
70
|
|
|
69
71
|
/**
|
|
70
72
|
* Get the Monday of the week for a given date
|
|
@@ -126,4 +128,4 @@ declare function useClickOutside<T extends HTMLElement>({ isActive, onOutsideCli
|
|
|
126
128
|
*/
|
|
127
129
|
declare function formatDate(date: Date, locale?: string): string;
|
|
128
130
|
|
|
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 };
|
|
131
|
+
export { Calendar, type CalendarConfig, type CalendarEvent, type CalendarProps, Calendar as SimpleBigWeekView, addDaysToDate, addMinutesToDate, areSameDates, completeWeek, formatDate, fromDateToTimeString, fromMinsToLocaleTimeString, fromMinsToTimeString, generateHours, getDaysInMonth, getMonday, isToday, useCalendarEvents, useClickOutside, useEventOverlap };
|
package/dist/index.d.ts
CHANGED
|
@@ -56,15 +56,17 @@ interface CalendarProps {
|
|
|
56
56
|
declare function Calendar({ date, events, onEventClick, onNewEvent, onEventMove, onAddBlock, onRemoveBlock, config, }: CalendarProps): react_jsx_runtime.JSX.Element;
|
|
57
57
|
|
|
58
58
|
/**
|
|
59
|
-
* Hook for
|
|
60
|
-
* Can be used standalone (headless) or with the Calendar component
|
|
59
|
+
* Hook for managing calendar events - overlap detection, filtering, and availability
|
|
61
60
|
*/
|
|
62
|
-
declare function
|
|
61
|
+
declare function useCalendarEvents(events: CalendarEvent[]): {
|
|
63
62
|
hasOverlap: (start: Date, duration: number, excludeId?: string) => boolean;
|
|
64
63
|
findConflicts: (start: Date, duration: number, excludeId?: string) => CalendarEvent[];
|
|
65
64
|
canMove: (eventId: string, newStart: Date) => boolean;
|
|
66
65
|
getEventsForDay: (date: Date) => CalendarEvent[];
|
|
66
|
+
getEventsForWeek: (date: Date) => CalendarEvent[];
|
|
67
|
+
findAvailableSlots: (date: Date, duration: number, startHour?: number, endHour?: number) => Date[];
|
|
67
68
|
};
|
|
69
|
+
declare const useEventOverlap: typeof useCalendarEvents;
|
|
68
70
|
|
|
69
71
|
/**
|
|
70
72
|
* Get the Monday of the week for a given date
|
|
@@ -126,4 +128,4 @@ declare function useClickOutside<T extends HTMLElement>({ isActive, onOutsideCli
|
|
|
126
128
|
*/
|
|
127
129
|
declare function formatDate(date: Date, locale?: string): string;
|
|
128
130
|
|
|
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 };
|
|
131
|
+
export { Calendar, type CalendarConfig, type CalendarEvent, type CalendarProps, 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,85 +211,6 @@ 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" }) });
|
|
@@ -199,7 +240,7 @@ function Calendar({
|
|
|
199
240
|
const { locale = "es-MX", icons = {} } = config;
|
|
200
241
|
const week = completeWeek(date);
|
|
201
242
|
const [activeId, setActiveId] = useState(null);
|
|
202
|
-
const { canMove } =
|
|
243
|
+
const { canMove } = useCalendarEvents(events);
|
|
203
244
|
const sensors = useSensors(
|
|
204
245
|
useSensor(PointerSensor, {
|
|
205
246
|
activationConstraint: { distance: 8 }
|
|
@@ -565,4 +606,4 @@ var Options = ({
|
|
|
565
606
|
);
|
|
566
607
|
};
|
|
567
608
|
|
|
568
|
-
export { Calendar, Calendar as SimpleBigWeekView, addDaysToDate, addMinutesToDate, areSameDates, completeWeek, formatDate, fromDateToTimeString, fromMinsToLocaleTimeString, fromMinsToTimeString, generateHours, getDaysInMonth, getMonday, isToday, useClickOutside, useEventOverlap };
|
|
609
|
+
export { Calendar, Calendar as SimpleBigWeekView, addDaysToDate, addMinutesToDate, areSameDates, completeWeek, formatDate, fromDateToTimeString, fromMinsToLocaleTimeString, fromMinsToTimeString, generateHours, getDaysInMonth, getMonday, isToday, useCalendarEvents, useClickOutside, useEventOverlap };
|