react-native-calendar-resource 1.0.0

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.
Files changed (36) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +403 -0
  3. package/dist/components/calendar/Calendar.d.ts +3 -0
  4. package/dist/components/calendar/Calendar.d.ts.map +1 -0
  5. package/dist/components/calendar/Calendar.js +69 -0
  6. package/dist/components/calendar/CalendarHeader.d.ts +14 -0
  7. package/dist/components/calendar/CalendarHeader.d.ts.map +1 -0
  8. package/dist/components/calendar/CalendarHeader.js +113 -0
  9. package/dist/components/calendar/EventsLayer.d.ts +13 -0
  10. package/dist/components/calendar/EventsLayer.d.ts.map +1 -0
  11. package/dist/components/calendar/EventsLayer.js +79 -0
  12. package/dist/components/calendar/GridBody.d.ts +14 -0
  13. package/dist/components/calendar/GridBody.d.ts.map +1 -0
  14. package/dist/components/calendar/GridBody.js +70 -0
  15. package/dist/components/calendar/ResourceHeaders.d.ts +13 -0
  16. package/dist/components/calendar/ResourceHeaders.d.ts.map +1 -0
  17. package/dist/components/calendar/ResourceHeaders.js +32 -0
  18. package/dist/components/calendar/TimeColumn.d.ts +14 -0
  19. package/dist/components/calendar/TimeColumn.d.ts.map +1 -0
  20. package/dist/components/calendar/TimeColumn.js +56 -0
  21. package/dist/components/calendar/UnavailableLayer.d.ts +12 -0
  22. package/dist/components/calendar/UnavailableLayer.d.ts.map +1 -0
  23. package/dist/components/calendar/UnavailableLayer.js +122 -0
  24. package/dist/components/calendar/dateUtils.d.ts +37 -0
  25. package/dist/components/calendar/dateUtils.d.ts.map +1 -0
  26. package/dist/components/calendar/dateUtils.js +128 -0
  27. package/dist/components/calendar/index.d.ts +9 -0
  28. package/dist/components/calendar/index.d.ts.map +1 -0
  29. package/dist/components/calendar/index.js +7 -0
  30. package/dist/index.d.ts +9 -0
  31. package/dist/index.d.ts.map +1 -0
  32. package/dist/index.js +9 -0
  33. package/dist/types/calendar.d.ts +118 -0
  34. package/dist/types/calendar.d.ts.map +1 -0
  35. package/dist/types/calendar.js +1 -0
  36. package/package.json +38 -0
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 [Your Name]
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,403 @@
1
+ # React Native Calendar Component
2
+
3
+ A fully customizable, type-safe resource-based calendar component for React Native. Built with TypeScript and pure React Native styling, this calendar displays events across multiple resources (courts, rooms, equipment, etc.) with flexible time slots and unavailable periods.
4
+
5
+ ## Features
6
+
7
+ - ✨ **Fully Customizable** - Configure everything from dimensions to colors to rendering
8
+ - 🎯 **Type-Safe** - Full TypeScript support with generics
9
+ - 📱 **Resource-Based** - Display multiple resources side-by-side
10
+ - ⏰ **Flexible Time Slots** - Configure any start/end hour range
11
+ - 🎨 **Custom Rendering** - Override default renderers for events, resources, and slots
12
+ - 🚫 **Unavailable Slots** - Support for reserved, off-hours, and disabled periods
13
+ - 📐 **Configurable Dimensions** - Adjust resource width and hour height
14
+ - 🎭 **Style Customization** - Full control over colors and appearance
15
+ - 👆 **Interactive** - Handle slot, event, and resource press events
16
+ - 🔄 **Synchronized Scrolling** - Header and time column scroll with the grid
17
+
18
+ ---
19
+
20
+ ## Installation
21
+
22
+ ```bash
23
+ npm install react-native-calendar-resource
24
+ # or
25
+ yarn add react-native-calendar-resource
26
+ ```
27
+
28
+ ### Dependencies
29
+
30
+ Ensure you have the required peer dependencies:
31
+
32
+ ```bash
33
+ npm install react react-native date-fns
34
+ ```
35
+
36
+ ---
37
+
38
+ ## Quick Start
39
+
40
+ ```tsx
41
+ import { Calendar } from "react-native-calendar-resource";
42
+
43
+ function MyCalendar() {
44
+ return (
45
+ <Calendar
46
+ date={new Date()}
47
+ resources={[
48
+ { id: "1", label: "event 1" },
49
+ { id: "2", label: "event 2" },
50
+ ]}
51
+ events={[
52
+ {
53
+ id: "evt1",
54
+ title: "Booked",
55
+ resourceId: "1",
56
+ startHour: 10,
57
+ endHour: 12,
58
+ color: "#3b82f6",
59
+ },
60
+ ]}
61
+ onSlotPress={(hour, resourceId, date) => {
62
+ console.log("Slot pressed:", { hour, resourceId, date });
63
+ }}
64
+ />
65
+ );
66
+ }
67
+ ```
68
+
69
+ ---
70
+
71
+ ## API Reference
72
+
73
+ ### Required Props
74
+
75
+ | Prop | Type | Description |
76
+ | ----------- | -------------------- | ---------------------------------------- |
77
+ | `date` | `Date` | The date to display in the calendar |
78
+ | `resources` | `Resource<T>[]` | Array of resources to display as columns |
79
+ | `events` | `CalendarEvent<T>[]` | Array of events to render |
80
+
81
+ ### Optional Props
82
+
83
+ | Prop | Type | Default | Description |
84
+ | ------------------------ | ---------------------------------- | ---------------------------------------------------- | -------------------------------------- |
85
+ | `unavailableSlots` | `UnavailableSlot<T>[]` | `[]` | Slots that are unavailable |
86
+ | `timeConfig` | `CalendarTimeConfig` | `{ startHour: 9, endHour: 24, timeFormat: "HH:mm" }` | Time configuration |
87
+ | `dimensions` | `Partial<CalendarDimensions>` | `{ resourceWidth: 64, hourHeight: 64 }` | Grid dimensions |
88
+ | `styles` | `CalendarStyles` | - | Overall calendar styling |
89
+ | `eventStyles` | `CalendarEventStyles` | - | Event-specific styling |
90
+ | `unavailableStyles` | `UnavailableSlotStyles` | - | Unavailable slot styling |
91
+ | `onSlotPress` | `(hour, resourceId, date) => void` | - | Called when empty slot is pressed |
92
+ | `onEventPress` | `(event) => void` | - | Called when event is pressed |
93
+ | `onResourcePress` | `(resource) => void` | - | Called when resource header is pressed |
94
+ | `renderEvent` | `(event, dimensions) => ReactNode` | - | Custom event renderer |
95
+ | `renderResourceHeader` | `(resource) => ReactNode` | - | Custom resource header renderer |
96
+ | `renderTimeSlot` | `(hour) => ReactNode` | - | Custom time slot renderer |
97
+ | `renderUnavailableSlot` | `(slot, dimensions) => ReactNode` | - | Custom unavailable slot renderer |
98
+ | `showHeader` | `boolean` | `true` | Show date header |
99
+ | `showTimeColumn` | `boolean` | `true` | Show time column |
100
+ | `showResourceHeaders` | `boolean` | `true` | Show resource headers |
101
+ | `enableHorizontalScroll` | `boolean` | `true` | Enable horizontal scrolling |
102
+ | `enableVerticalScroll` | `boolean` | `true` | Enable vertical scrolling |
103
+
104
+ ---
105
+
106
+ ## Type Definitions
107
+
108
+ ```typescript
109
+ type Resource<T = any> = {
110
+ id: string;
111
+ label: string;
112
+ data?: T; // Additional custom data
113
+ };
114
+
115
+ type CalendarEvent<T = any> = {
116
+ id: string;
117
+ title: string;
118
+ resourceId: string;
119
+ startHour: number;
120
+ endHour: number;
121
+ color?: string;
122
+ data?: T; // Additional custom data
123
+ };
124
+
125
+ type UnavailableSlot<T = any> = {
126
+ id: string;
127
+ resourceId: string;
128
+ startHour: number;
129
+ endHour: number;
130
+ type: "reserved" | "offHours" | "disabled";
131
+ data?: T; // Additional custom data
132
+ };
133
+
134
+ type CalendarTimeConfig = {
135
+ startHour: number; // 0-23
136
+ endHour: number; // 1-24
137
+ timeFormat?: string; // date-fns format string
138
+ };
139
+
140
+ type CalendarDimensions = {
141
+ resourceWidth: number; // px
142
+ hourHeight: number; // px
143
+ };
144
+ ```
145
+
146
+ ---
147
+
148
+ ## Examples
149
+
150
+ ### Custom Time Range
151
+
152
+ ```tsx
153
+ <Calendar
154
+ date={new Date()}
155
+ resources={resources}
156
+ events={events}
157
+ timeConfig={{
158
+ startHour: 6, // Start at 6 AM
159
+ endHour: 22, // End at 10 PM
160
+ timeFormat: "h:mm a", // 12-hour format
161
+ }}
162
+ />
163
+ ```
164
+
165
+ ### Custom Dimensions
166
+
167
+ ```tsx
168
+ <Calendar
169
+ date={new Date()}
170
+ resources={resources}
171
+ events={events}
172
+ dimensions={{
173
+ resourceWidth: 100,
174
+ hourHeight: 80,
175
+ }}
176
+ />
177
+ ```
178
+
179
+ ### Custom Event Rendering
180
+
181
+ ```tsx
182
+ <Calendar
183
+ date={new Date()}
184
+ resources={resources}
185
+ events={events}
186
+ renderEvent={(event, { width, height }) => (
187
+ <View style={{ width, height, backgroundColor: event.color, padding: 8 }}>
188
+ <Text style={{ color: "white", fontWeight: "bold" }}>{event.title}</Text>
189
+ <Text style={{ color: "white", fontSize: 12 }}>
190
+ {event.startHour}:00 - {event.endHour}:00
191
+ </Text>
192
+ </View>
193
+ )}
194
+ />
195
+ ```
196
+
197
+ ### Custom Resource Headers
198
+
199
+ ```tsx
200
+ <Calendar
201
+ date={new Date()}
202
+ resources={resources}
203
+ events={events}
204
+ renderResourceHeader={(resource) => (
205
+ <View style={{ padding: 8 }}>
206
+ <Text style={{ fontSize: 16, fontWeight: "bold", color: "#fff" }}>
207
+ {resource.label}
208
+ </Text>
209
+ <Text style={{ fontSize: 12, color: "#999" }}>Available</Text>
210
+ </View>
211
+ )}
212
+ />
213
+ ```
214
+
215
+ ### Custom Styling
216
+
217
+ ```tsx
218
+ <Calendar
219
+ date={new Date()}
220
+ resources={resources}
221
+ events={events}
222
+ styles={{
223
+ backgroundColor: "#000",
224
+ gridColor: "#333",
225
+ slotBackgroundColor: "#1a1a1a",
226
+ }}
227
+ eventStyles={{
228
+ defaultColor: "#3b82f6",
229
+ borderRadius: 12,
230
+ opacity: 0.9,
231
+ }}
232
+ unavailableStyles={{
233
+ reserved: {
234
+ backgroundColor: "#dc2626",
235
+ opacity: 0.4,
236
+ },
237
+ offHours: {
238
+ backgroundColor: "#404040",
239
+ opacity: 0.6,
240
+ },
241
+ }}
242
+ />
243
+ ```
244
+
245
+ ### With TypeScript Generics
246
+
247
+ ```tsx
248
+ interface eventData {
249
+ capacity: number;
250
+ amenities: string[];
251
+ }
252
+
253
+ interface BookingData {
254
+ userId: string;
255
+ notes: string;
256
+ }
257
+
258
+ <Calendar<BookingData, eventData>
259
+ date={new Date()}
260
+ resources={[
261
+ {
262
+ id: "1",
263
+ label: "event 1",
264
+ data: { capacity: 4, amenities: ["lights", "net"] },
265
+ },
266
+ ]}
267
+ events={[
268
+ {
269
+ id: "1",
270
+ title: "Booked",
271
+ resourceId: "1",
272
+ startHour: 10,
273
+ endHour: 12,
274
+ data: { userId: "user123", notes: "Bring equipment" },
275
+ },
276
+ ]}
277
+ onEventPress={(event) => {
278
+ console.log(event.data.userId); // Type-safe!
279
+ }}
280
+ />;
281
+ ```
282
+
283
+ ---
284
+
285
+ ## Technologies
286
+
287
+ - React Native
288
+ - Expo
289
+ - TypeScript
290
+ - date-fns
291
+ - NativeWind (optional, for default styling)
292
+
293
+ ---
294
+
295
+ ## License
296
+
297
+ MIT
298
+
299
+ - The tab bar remains visible while navigating deeper into Explore-related screens
300
+ - Navigation context is preserved
301
+ - Platform-native gestures and transitions are maintained
302
+
303
+ Headers are disabled at the stack level and selectively enabled per screen to match the Figma design and avoid default iOS -Liquid Glass- system back button styling.
304
+
305
+ ---
306
+
307
+ ### Calendar Component
308
+
309
+ The booking calendar was implemented from scratch instead of relying on third-party libraries.
310
+
311
+ Reasons for this decision:
312
+
313
+ 1. No third-party library was found that met the requirements of the project.
314
+ 2. Pixel-perfect alignment with the design
315
+ 3. Full control over a resource-based grid (events × time)
316
+ 4. Optimized performance for the specific use case
317
+ 5. Reduced dependency and bundle size overhead
318
+
319
+ #### Calendar Architecture
320
+
321
+ ```
322
+ Calendar/
323
+ ├── Calendar.tsx
324
+ ├── CalendarHeader.tsx
325
+ ├── TimeColumn.tsx
326
+ ├── ResourceHeaders.tsx
327
+ ├── GridBody.tsx
328
+ ├── EventsLayer.tsx
329
+ └── UnavailableLayer.tsx
330
+ ```
331
+
332
+ #### Key Features
333
+
334
+ - Synchronized horizontal and vertical scrolling
335
+ - Resource-based layout for multiple events
336
+ - Three slot states: available, booked, unavailable
337
+ - Configurable time range, slot height, and column width
338
+ - Cross-platform behavior parity (iOS & Android)
339
+
340
+ ---
341
+
342
+ ### Styling Approach
343
+
344
+ Styling is handled using NativeWind (TailwindCSS for React Native):
345
+
346
+ - Semantic color tokens (background, card, accent, muted)
347
+ - Consistent spacing and typography via utility classes
348
+ - Centralized theme configuration
349
+ - Custom fonts (Asap, Bebas Neue) loaded via `expo-font`
350
+ - A mix of @expo/vector-icons and SVG icon components was used. SVGs were introduced for icons where an exact visual match was not available in the Expo icon set.
351
+
352
+ ---
353
+
354
+ ### Date & Time Handling
355
+
356
+ All date and time operations use date-fns for:
357
+
358
+ - Clear and readable date manipulation
359
+ - Tree-shakeable imports
360
+ - Safer alternatives to native Date APIs
361
+
362
+ ---
363
+
364
+ ## Tech Stack
365
+
366
+ - Expo SDK 54
367
+ - TypeScript
368
+ - Expo Router + React Navigation Stack
369
+ - NativeWind (TailwindCSS)
370
+ - date-fns
371
+ - react-native-leaflet-view (Leaflet maps via WebView)
372
+ - expo-linear-gradient
373
+ - react-native-reanimated
374
+
375
+ ---
376
+
377
+ ## Implemented Features
378
+
379
+ - Venue details screen with image carousel
380
+ - Rating badges and amenity tags
381
+ - Coach cards with detail navigation
382
+ - Custom calendar grid
383
+ - Date shortcuts (Today / Tomorrow)
384
+ - Visual distinction between slot states
385
+ - Location card with map preview
386
+ - Persistent tab bar navigation
387
+ - Nested stack navigation within Explore
388
+ - Cross-platform support (iOS & Android)
389
+
390
+ ---
391
+
392
+ ## Additional Notes
393
+
394
+ - The Explore tab was used to display event details since no design was provided for the Home screen.
395
+ In a real-world application, the Explore tab would typically be used to search for or browse events, then navigate to the event details screen.
396
+ - Non-Explore tabs are intentionally minimal as they are outside the task scope
397
+ - Leaflet (via `react-native-leaflet-view`) was used for maps instead of `react-native-maps` or other Google Maps-based libraries, as those require a Google Cloud API key and billing subscription
398
+ - Mock data is used throughout the app
399
+ - Safe area insets are handled for iOS notch and home indicator
400
+
401
+ ---
402
+
403
+ **Author:** Abdelrahman Sobhy
@@ -0,0 +1,3 @@
1
+ import { CalendarProps } from "../../types/calendar";
2
+ export declare function Calendar<TEvent = any, TResource = any, TUnavailable = any>({ date, resources, events, unavailableSlots, timeConfig, dimensions, styles, eventStyles, unavailableStyles, onSlotPress, onEventPress, onResourcePress, onDateChange, minDate, maxDate, allowPastDates, renderEvent, renderResourceHeader, renderTimeSlot, renderUnavailableSlot, showHeader, showTimeColumn, showResourceHeaders, enableHorizontalScroll, enableVerticalScroll, showDateNavigation, dateFormat, accessibilityLabel, }: CalendarProps<TEvent, TResource, TUnavailable>): import("react").JSX.Element;
3
+ //# sourceMappingURL=Calendar.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Calendar.d.ts","sourceRoot":"","sources":["../../../components/calendar/Calendar.tsx"],"names":[],"mappings":"AAOA,OAAO,EAEL,aAAa,EAEd,MAAM,sBAAsB,CAAC;AAoB9B,wBAAgB,QAAQ,CAAC,MAAM,GAAG,GAAG,EAAE,SAAS,GAAG,GAAG,EAAE,YAAY,GAAG,GAAG,EAAE,EAC1E,IAAI,EACJ,SAAS,EACT,MAAM,EACN,gBAAqB,EACrB,UAAU,EACV,UAAU,EACV,MAAM,EACN,WAAW,EACX,iBAAiB,EACjB,WAAW,EACX,YAAY,EACZ,eAAe,EACf,YAAY,EACZ,OAAO,EACP,OAAO,EACP,cAAsB,EACtB,WAAW,EACX,oBAAoB,EACpB,cAAc,EACd,qBAAqB,EACrB,UAAiB,EACjB,cAAqB,EACrB,mBAA0B,EAC1B,sBAA6B,EAC7B,oBAA2B,EAC3B,kBAAyB,EACzB,UAA+B,EAC/B,kBAA+B,GAChC,EAAE,aAAa,CAAC,MAAM,EAAE,SAAS,EAAE,YAAY,CAAC,+BA0IhD"}
@@ -0,0 +1,69 @@
1
+ import { useMemo, useRef } from "react";
2
+ import { ScrollView, View, } from "react-native";
3
+ import { CalendarHeader } from "./CalendarHeader";
4
+ import { EventsLayer } from "./EventsLayer";
5
+ import { GridBody } from "./GridBody";
6
+ import { ResourceHeaders } from "./ResourceHeaders";
7
+ import { TimeColumn } from "./TimeColumn";
8
+ import { UnavailableLayer } from "./UnavailableLayer";
9
+ // Default configuration
10
+ const DEFAULT_DIMENSIONS = {
11
+ resourceWidth: 64,
12
+ hourHeight: 64,
13
+ };
14
+ const DEFAULT_TIME_CONFIG = {
15
+ startHour: 9,
16
+ endHour: 24,
17
+ timeFormat: "HH:mm",
18
+ };
19
+ export function Calendar({ date, resources, events, unavailableSlots = [], timeConfig, dimensions, styles, eventStyles, unavailableStyles, onSlotPress, onEventPress, onResourcePress, onDateChange, minDate, maxDate, allowPastDates = false, renderEvent, renderResourceHeader, renderTimeSlot, renderUnavailableSlot, showHeader = true, showTimeColumn = true, showResourceHeaders = true, enableHorizontalScroll = true, enableVerticalScroll = true, showDateNavigation = true, dateFormat = "EEE, MMM d, yyyy", accessibilityLabel = "Calendar", }) {
20
+ const headerScrollRef = useRef(null);
21
+ const timeColumnScrollRef = useRef(null);
22
+ // Merge configurations with defaults
23
+ const finalDimensions = useMemo(() => ({ ...DEFAULT_DIMENSIONS, ...dimensions }), [dimensions]);
24
+ const finalTimeConfig = useMemo(() => ({ ...DEFAULT_TIME_CONFIG, ...timeConfig }), [timeConfig]);
25
+ // Calculate grid dimensions
26
+ const gridTotalWidth = resources.length * finalDimensions.resourceWidth;
27
+ const totalHours = finalTimeConfig.endHour - finalTimeConfig.startHour;
28
+ const gridTotalHeight = totalHours * finalDimensions.hourHeight;
29
+ const handleHorizontalScroll = (scrollEvent) => {
30
+ const horizontalOffset = scrollEvent.nativeEvent.contentOffset.x;
31
+ headerScrollRef.current?.scrollTo({ x: horizontalOffset, animated: false });
32
+ };
33
+ const handleVerticalScroll = (scrollEvent) => {
34
+ const verticalOffset = scrollEvent.nativeEvent.contentOffset.y;
35
+ timeColumnScrollRef.current?.scrollTo({
36
+ y: verticalOffset,
37
+ animated: false,
38
+ });
39
+ };
40
+ const backgroundColor = styles?.backgroundColor || "#1a1a1a";
41
+ return (<View style={{ flex: 1, backgroundColor }} accessibilityLabel={accessibilityLabel}>
42
+ {showHeader && (<CalendarHeader date={date} timeConfig={finalTimeConfig} onDateChange={onDateChange} showDateNavigation={showDateNavigation} dateFormat={dateFormat} minDate={minDate} maxDate={maxDate} allowPastDates={allowPastDates}/>)}
43
+
44
+ {showResourceHeaders && (<ResourceHeaders resources={resources} scrollRef={headerScrollRef} dimensions={finalDimensions} renderResourceHeader={renderResourceHeader} onResourcePress={onResourcePress}/>)}
45
+
46
+ <View style={{ flex: 1, flexDirection: "row" }}>
47
+ {showTimeColumn && (<TimeColumn scrollRef={timeColumnScrollRef} gridTotalHeight={gridTotalHeight} timeConfig={finalTimeConfig} dimensions={finalDimensions} renderTimeSlot={renderTimeSlot} styles={styles}/>)}
48
+
49
+ <View style={{ flex: 1 }}>
50
+ <ScrollView horizontal={enableHorizontalScroll} showsHorizontalScrollIndicator={enableHorizontalScroll} bounces={false} onScroll={handleHorizontalScroll} scrollEventThrottle={16} nestedScrollEnabled={true} scrollEnabled={enableHorizontalScroll}>
51
+ <ScrollView showsVerticalScrollIndicator={enableVerticalScroll} bounces={false} onScroll={handleVerticalScroll} scrollEventThrottle={16} nestedScrollEnabled={true} scrollEnabled={enableVerticalScroll} contentContainerStyle={{
52
+ width: gridTotalWidth,
53
+ height: gridTotalHeight,
54
+ }}>
55
+ <View style={{
56
+ position: "relative",
57
+ width: gridTotalWidth,
58
+ height: gridTotalHeight,
59
+ }}>
60
+ <GridBody resources={resources} events={events} unavailableSlots={unavailableSlots} onSlotPress={onSlotPress} date={date} timeConfig={finalTimeConfig} dimensions={finalDimensions} styles={styles}/>
61
+ <UnavailableLayer unavailableSlots={unavailableSlots} resources={resources} timeConfig={finalTimeConfig} dimensions={finalDimensions} unavailableStyles={unavailableStyles} renderUnavailableSlot={renderUnavailableSlot}/>
62
+ <EventsLayer events={events} resources={resources} onEventPress={onEventPress} timeConfig={finalTimeConfig} dimensions={finalDimensions} eventStyles={eventStyles} renderEvent={renderEvent}/>
63
+ </View>
64
+ </ScrollView>
65
+ </ScrollView>
66
+ </View>
67
+ </View>
68
+ </View>);
69
+ }
@@ -0,0 +1,14 @@
1
+ import { CalendarTimeConfig } from "../../types/calendar";
2
+ type CalendarHeaderProps = {
3
+ date: Date;
4
+ timeConfig: CalendarTimeConfig;
5
+ onDateChange?: (date: Date) => void;
6
+ showDateNavigation?: boolean;
7
+ dateFormat?: string;
8
+ minDate?: Date;
9
+ maxDate?: Date;
10
+ allowPastDates?: boolean;
11
+ };
12
+ export declare function CalendarHeader({ date, timeConfig, onDateChange, showDateNavigation, dateFormat, minDate, maxDate, allowPastDates, }: CalendarHeaderProps): import("react").JSX.Element;
13
+ export {};
14
+ //# sourceMappingURL=CalendarHeader.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"CalendarHeader.d.ts","sourceRoot":"","sources":["../../../components/calendar/CalendarHeader.tsx"],"names":[],"mappings":"AAGA,OAAO,EAAE,kBAAkB,EAAE,MAAM,sBAAsB,CAAC;AAE1D,KAAK,mBAAmB,GAAG;IACzB,IAAI,EAAE,IAAI,CAAC;IACX,UAAU,EAAE,kBAAkB,CAAC;IAC/B,YAAY,CAAC,EAAE,CAAC,IAAI,EAAE,IAAI,KAAK,IAAI,CAAC;IACpC,kBAAkB,CAAC,EAAE,OAAO,CAAC;IAC7B,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,OAAO,CAAC,EAAE,IAAI,CAAC;IACf,OAAO,CAAC,EAAE,IAAI,CAAC;IACf,cAAc,CAAC,EAAE,OAAO,CAAC;CAC1B,CAAC;AAEF,wBAAgB,cAAc,CAAC,EAC7B,IAAI,EACJ,UAAU,EACV,YAAY,EACZ,kBAAyB,EACzB,UAA+B,EAC/B,OAAO,EACP,OAAO,EACP,cAAqB,GACtB,EAAE,mBAAmB,+BA4IrB"}
@@ -0,0 +1,113 @@
1
+ import { MaterialIcons } from "@expo/vector-icons";
2
+ import { addDays, format, subDays } from "date-fns";
3
+ import { Pressable, Text, View } from "react-native";
4
+ export function CalendarHeader({ date, timeConfig, onDateChange, showDateNavigation = true, dateFormat = "EEE, MMM d, yyyy", minDate, maxDate, allowPastDates = true, }) {
5
+ const handlePreviousDay = () => {
6
+ const newDate = subDays(date, 1);
7
+ // Check if going back is allowed
8
+ if (!allowPastDates) {
9
+ const today = new Date();
10
+ today.setHours(0, 0, 0, 0);
11
+ const checkDate = new Date(newDate);
12
+ checkDate.setHours(0, 0, 0, 0);
13
+ if (checkDate < today)
14
+ return;
15
+ }
16
+ // Check minDate constraint
17
+ if (minDate) {
18
+ const min = new Date(minDate);
19
+ min.setHours(0, 0, 0, 0);
20
+ const checkDate = new Date(newDate);
21
+ checkDate.setHours(0, 0, 0, 0);
22
+ if (checkDate < min)
23
+ return;
24
+ }
25
+ onDateChange?.(newDate);
26
+ };
27
+ const handleNextDay = () => {
28
+ const newDate = addDays(date, 1);
29
+ // Check maxDate constraint
30
+ if (maxDate) {
31
+ const max = new Date(maxDate);
32
+ max.setHours(0, 0, 0, 0);
33
+ const checkDate = new Date(newDate);
34
+ checkDate.setHours(0, 0, 0, 0);
35
+ if (checkDate > max)
36
+ return;
37
+ }
38
+ onDateChange?.(newDate);
39
+ };
40
+ // Check if previous button should be disabled
41
+ const isPreviousDisabled = () => {
42
+ const prevDate = subDays(date, 1);
43
+ if (!allowPastDates) {
44
+ const today = new Date();
45
+ today.setHours(0, 0, 0, 0);
46
+ const checkDate = new Date(prevDate);
47
+ checkDate.setHours(0, 0, 0, 0);
48
+ if (checkDate < today)
49
+ return true;
50
+ }
51
+ if (minDate) {
52
+ const min = new Date(minDate);
53
+ min.setHours(0, 0, 0, 0);
54
+ const checkDate = new Date(prevDate);
55
+ checkDate.setHours(0, 0, 0, 0);
56
+ if (checkDate < min)
57
+ return true;
58
+ }
59
+ return false;
60
+ };
61
+ // Check if next button should be disabled
62
+ const isNextDisabled = () => {
63
+ if (!maxDate)
64
+ return false;
65
+ const nextDate = addDays(date, 1);
66
+ const max = new Date(maxDate);
67
+ max.setHours(0, 0, 0, 0);
68
+ const checkDate = new Date(nextDate);
69
+ checkDate.setHours(0, 0, 0, 0);
70
+ return checkDate > max;
71
+ };
72
+ const prevDisabled = isPreviousDisabled();
73
+ const nextDisabled = isNextDisabled();
74
+ return (<View style={{
75
+ backgroundColor: "#1a1a1a",
76
+ paddingHorizontal: 20,
77
+ paddingVertical: 16,
78
+ }}>
79
+ <View style={{
80
+ flexDirection: "row",
81
+ alignItems: "center",
82
+ justifyContent: "space-between",
83
+ }}>
84
+ {showDateNavigation ? (<>
85
+ <Pressable onPress={handlePreviousDay} disabled={prevDisabled} style={{
86
+ width: 40,
87
+ height: 40,
88
+ borderRadius: 20,
89
+ alignItems: "center",
90
+ justifyContent: "center",
91
+ backgroundColor: "rgba(38, 38, 38, 0.75)",
92
+ }}>
93
+ <MaterialIcons name="chevron-left" size={20} color={prevDisabled ? "#626262" : "#fefefe"}/>
94
+ </Pressable>
95
+ <Text style={{ color: "#fefefe", fontSize: 16, fontWeight: "600" }}>
96
+ {format(date, dateFormat)}
97
+ </Text>
98
+ <Pressable onPress={handleNextDay} disabled={nextDisabled} style={{
99
+ width: 40,
100
+ height: 40,
101
+ borderRadius: 20,
102
+ backgroundColor: "rgba(38, 38, 38, 0.75)",
103
+ alignItems: "center",
104
+ justifyContent: "center",
105
+ }}>
106
+ <MaterialIcons name="chevron-right" size={20} color={nextDisabled ? "#626262" : "#fefefe"}/>
107
+ </Pressable>
108
+ </>) : (<Text style={{ color: '#fefefe', fontSize: 16, fontWeight: '600' }}>
109
+ {format(date, dateFormat)}
110
+ </Text>)}
111
+ </View>
112
+ </View>);
113
+ }
@@ -0,0 +1,13 @@
1
+ import { CalendarDimensions, CalendarEvent, CalendarEventStyles, CalendarTimeConfig, RenderEventFunction, Resource } from "../../types/calendar";
2
+ type EventsLayerProps<TEvent = any, TResource = any> = {
3
+ events: CalendarEvent<TEvent>[];
4
+ resources: Resource<TResource>[];
5
+ onEventPress?: (event: CalendarEvent<TEvent>) => void;
6
+ timeConfig: CalendarTimeConfig;
7
+ dimensions: CalendarDimensions;
8
+ eventStyles?: CalendarEventStyles;
9
+ renderEvent?: RenderEventFunction<TEvent>;
10
+ };
11
+ export declare function EventsLayer<TEvent = any, TResource = any>({ events, resources, onEventPress, timeConfig, dimensions, eventStyles, renderEvent, }: EventsLayerProps<TEvent, TResource>): import("react").JSX.Element;
12
+ export {};
13
+ //# sourceMappingURL=EventsLayer.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"EventsLayer.d.ts","sourceRoot":"","sources":["../../../components/calendar/EventsLayer.tsx"],"names":[],"mappings":"AAEA,OAAO,EACL,kBAAkB,EAClB,aAAa,EACb,mBAAmB,EACnB,kBAAkB,EAClB,mBAAmB,EACnB,QAAQ,EACT,MAAM,sBAAsB,CAAC;AAE9B,KAAK,gBAAgB,CAAC,MAAM,GAAG,GAAG,EAAE,SAAS,GAAG,GAAG,IAAI;IACrD,MAAM,EAAE,aAAa,CAAC,MAAM,CAAC,EAAE,CAAC;IAChC,SAAS,EAAE,QAAQ,CAAC,SAAS,CAAC,EAAE,CAAC;IACjC,YAAY,CAAC,EAAE,CAAC,KAAK,EAAE,aAAa,CAAC,MAAM,CAAC,KAAK,IAAI,CAAC;IACtD,UAAU,EAAE,kBAAkB,CAAC;IAC/B,UAAU,EAAE,kBAAkB,CAAC;IAC/B,WAAW,CAAC,EAAE,mBAAmB,CAAC;IAClC,WAAW,CAAC,EAAE,mBAAmB,CAAC,MAAM,CAAC,CAAC;CAC3C,CAAC;AAEF,wBAAgB,WAAW,CAAC,MAAM,GAAG,GAAG,EAAE,SAAS,GAAG,GAAG,EAAE,EACzD,MAAM,EACN,SAAS,EACT,YAAY,EACZ,UAAU,EACV,UAAU,EACV,WAAW,EACX,WAAW,GACZ,EAAE,gBAAgB,CAAC,MAAM,EAAE,SAAS,CAAC,+BA+GrC"}