@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 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 overlap detection logic without the UI:
48
+ Use the calendar logic without the UI:
49
49
 
50
50
  ```tsx
51
- import { useEventOverlap } from "denik-calendar";
51
+ import { useCalendarEvents } from "@hectorbliss/denik-calendar";
52
52
 
53
53
  function MyCustomCalendar({ events }) {
54
- const { canMove, hasOverlap, findConflicts } = useEventOverlap(events);
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
- function useEventOverlap(events) {
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.useMemo(() => {
53
- return (date) => {
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
- }, [events]);
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 } = useEventOverlap(events);
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 detecting event overlaps in a calendar
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 useEventOverlap(events: CalendarEvent[]): {
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 detecting event overlaps in a calendar
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 useEventOverlap(events: CalendarEvent[]): {
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, useMemo, useRef, useEffect, useState } from 'react';
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
- function useEventOverlap(events) {
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 = useMemo(() => {
51
- return (date) => {
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
- }, [events]);
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 } = useEventOverlap(events);
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 };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hectorbliss/denik-calendar",
3
- "version": "0.0.1",
3
+ "version": "0.0.2",
4
4
  "private": false,
5
5
  "description": "A React drag-and-drop week calendar component with overlap detection. Built with @dnd-kit.",
6
6
  "type": "module",