react-native-resource-calendar 1.0.7 → 1.0.9
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 +112 -168
- package/dist/index.d.mts +14 -4
- package/dist/index.d.ts +14 -4
- package/dist/index.js +386 -213
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +338 -165
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -18,7 +18,7 @@ Expo compatibility.
|
|
|
18
18
|
---
|
|
19
19
|
|
|
20
20
|
## 🎬 Demo
|
|
21
|
-
https://github.com/user-attachments/assets/
|
|
21
|
+
https://github.com/user-attachments/assets/68fe0283-73ce-4689-8241-6587b817ecbd
|
|
22
22
|
|
|
23
23
|
---
|
|
24
24
|
|
|
@@ -71,141 +71,14 @@ Follow these steps to get started quickly with **React Native Resource Calendar*
|
|
|
71
71
|
|
|
72
72
|
```tsx
|
|
73
73
|
import React from 'react';
|
|
74
|
-
import {
|
|
75
|
-
import {Calendar, DraggedEventDraft, useCalendarBinding} from "react-native-resource-calendar";
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
events: [
|
|
83
|
-
{
|
|
84
|
-
id: 101,
|
|
85
|
-
resourceId: 1,
|
|
86
|
-
from: 8 * 60, // 8:00 AM
|
|
87
|
-
to: 9 * 60, // 9:00 AM
|
|
88
|
-
title: "Physical Therapy",
|
|
89
|
-
description: "Post-surgery recovery session",
|
|
90
|
-
meta: {client: "John Doe"},
|
|
91
|
-
},
|
|
92
|
-
{
|
|
93
|
-
id: 102,
|
|
94
|
-
resourceId: 1,
|
|
95
|
-
from: 10 * 60,
|
|
96
|
-
to: 11 * 60,
|
|
97
|
-
title: "Mobility Assessment",
|
|
98
|
-
description: "Initial consultation",
|
|
99
|
-
},
|
|
100
|
-
],
|
|
101
|
-
disabledBlocks: [
|
|
102
|
-
{
|
|
103
|
-
id: 1001,
|
|
104
|
-
resourceId: 1,
|
|
105
|
-
from: 12 * 60, // 12:00 PM
|
|
106
|
-
to: 13 * 60, // 1:00 PM
|
|
107
|
-
title: "Lunch Break",
|
|
108
|
-
},
|
|
109
|
-
],
|
|
110
|
-
disableIntervals: [
|
|
111
|
-
{
|
|
112
|
-
resourceId: 1,
|
|
113
|
-
from: 17 * 60, // 5:00 PM
|
|
114
|
-
to: 24 * 60, // 12:00 AM
|
|
115
|
-
},
|
|
116
|
-
],
|
|
117
|
-
},
|
|
118
|
-
{
|
|
119
|
-
id: 2,
|
|
120
|
-
name: "Bob Martinez",
|
|
121
|
-
avatar: "https://randomuser.me/api/portraits/men/22.jpg",
|
|
122
|
-
events: [
|
|
123
|
-
{
|
|
124
|
-
id: 201,
|
|
125
|
-
resourceId: 2,
|
|
126
|
-
from: 9 * 60 + 30, // 9:30 AM
|
|
127
|
-
to: 10 * 60 + 30, // 10:30 AM
|
|
128
|
-
title: "Personal Training",
|
|
129
|
-
meta: {client: "Alex Kim"},
|
|
130
|
-
},
|
|
131
|
-
{
|
|
132
|
-
id: 202,
|
|
133
|
-
resourceId: 2,
|
|
134
|
-
from: 15 * 60,
|
|
135
|
-
to: 16 * 60,
|
|
136
|
-
title: "Endurance Coaching",
|
|
137
|
-
},
|
|
138
|
-
],
|
|
139
|
-
disabledBlocks: [
|
|
140
|
-
{
|
|
141
|
-
id: 2001,
|
|
142
|
-
resourceId: 2,
|
|
143
|
-
from: 13 * 60,
|
|
144
|
-
to: 14 * 60,
|
|
145
|
-
title: "Staff Meeting",
|
|
146
|
-
},
|
|
147
|
-
],
|
|
148
|
-
disableIntervals: [
|
|
149
|
-
{
|
|
150
|
-
resourceId: 2,
|
|
151
|
-
from: 7 * 60,
|
|
152
|
-
to: 8 * 60,
|
|
153
|
-
},
|
|
154
|
-
],
|
|
155
|
-
},
|
|
156
|
-
{
|
|
157
|
-
id: 3,
|
|
158
|
-
name: "Charlie Kim",
|
|
159
|
-
avatar: "https://randomuser.me/api/portraits/men/33.jpg",
|
|
160
|
-
events: [
|
|
161
|
-
{
|
|
162
|
-
id: 301,
|
|
163
|
-
resourceId: 3,
|
|
164
|
-
from: 11 * 60,
|
|
165
|
-
to: 12 * 60,
|
|
166
|
-
title: "Sports Massage",
|
|
167
|
-
},
|
|
168
|
-
{
|
|
169
|
-
id: 302,
|
|
170
|
-
resourceId: 3,
|
|
171
|
-
from: 14 * 60 + 15,
|
|
172
|
-
to: 15 * 60,
|
|
173
|
-
title: "Deep Tissue Massage",
|
|
174
|
-
},
|
|
175
|
-
],
|
|
176
|
-
disabledBlocks: [
|
|
177
|
-
{
|
|
178
|
-
id: 3001,
|
|
179
|
-
resourceId: 3,
|
|
180
|
-
from: 12 * 60,
|
|
181
|
-
to: 13 * 60,
|
|
182
|
-
title: "Lunch",
|
|
183
|
-
},
|
|
184
|
-
],
|
|
185
|
-
},
|
|
186
|
-
{
|
|
187
|
-
id: 4,
|
|
188
|
-
name: "Diana Ross",
|
|
189
|
-
avatar: "https://randomuser.me/api/portraits/women/44.jpg",
|
|
190
|
-
events: [
|
|
191
|
-
{
|
|
192
|
-
id: 401,
|
|
193
|
-
resourceId: 4,
|
|
194
|
-
from: 13 * 60,
|
|
195
|
-
to: 14 * 60,
|
|
196
|
-
title: "Nutrition Plan Review",
|
|
197
|
-
description: "Discuss dietary adjustments",
|
|
198
|
-
},
|
|
199
|
-
],
|
|
200
|
-
disableIntervals: [
|
|
201
|
-
{
|
|
202
|
-
resourceId: 4,
|
|
203
|
-
from: 18 * 60,
|
|
204
|
-
to: 24 * 60,
|
|
205
|
-
},
|
|
206
|
-
],
|
|
207
|
-
},
|
|
208
|
-
];
|
|
74
|
+
import {StyleSheet, TouchableOpacity, View} from 'react-native';
|
|
75
|
+
import {Calendar, DraggedEventDraft, Event, LayoutMode, useCalendarBinding} from "react-native-resource-calendar";
|
|
76
|
+
import {SafeAreaView} from "react-native-safe-area-context";
|
|
77
|
+
import {ThemedText} from "@/components/ThemedText";
|
|
78
|
+
import {resourceData} from "@/app/(tabs)/fakeData";
|
|
79
|
+
import EventTopRight from "@/components/EventTopRight";
|
|
80
|
+
import {FontAwesome} from "@expo/vector-icons";
|
|
81
|
+
import {statusColor} from "@/utilities/helpers";
|
|
209
82
|
|
|
210
83
|
export default function App() {
|
|
211
84
|
const {
|
|
@@ -217,7 +90,10 @@ export default function App() {
|
|
|
217
90
|
const setSelectedEvent = useSetSelectedEvent();
|
|
218
91
|
const draggedEventDraft = useGetDraggedEventDraft();
|
|
219
92
|
const [date, setDate] = React.useState(new Date());
|
|
220
|
-
const [resources, setResources] = React.useState(resourceData)
|
|
93
|
+
const [resources, setResources] = React.useState(resourceData);
|
|
94
|
+
const [hourHeight, setHourHeight] = React.useState(120);
|
|
95
|
+
const [numberOfColumns, setNumberOfColumns] = React.useState(3);
|
|
96
|
+
const [layoutMode, setLayoutMode] = React.useState<LayoutMode>('stacked');
|
|
221
97
|
|
|
222
98
|
const updateResourcesOnDrag = React.useCallback(
|
|
223
99
|
(draft: DraggedEventDraft) => {
|
|
@@ -257,39 +133,101 @@ export default function App() {
|
|
|
257
133
|
[setResources]
|
|
258
134
|
);
|
|
259
135
|
|
|
136
|
+
const eventStyleOverrides = (event: Event) => {
|
|
137
|
+
const bg = statusColor(event.meta?.status)
|
|
138
|
+
return {container: {backgroundColor: bg}};
|
|
139
|
+
};
|
|
140
|
+
|
|
141
|
+
const randomPropsGenerator = () => {
|
|
142
|
+
const randomHourHeight = Math.floor(Math.random() * (120 - 60 + 1)) + 60;
|
|
143
|
+
const randomNumberOfColumns = Math.floor(Math.random() * (5 - 1 + 1)) + 1;
|
|
144
|
+
setHourHeight(randomHourHeight);
|
|
145
|
+
setNumberOfColumns(randomNumberOfColumns);
|
|
146
|
+
setLayoutMode(layoutMode === 'stacked' ? 'columns' : 'stacked');
|
|
147
|
+
}
|
|
148
|
+
|
|
260
149
|
return (
|
|
261
|
-
<
|
|
262
|
-
<
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
{
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
150
|
+
<SafeAreaView style={{backgroundColor: "#fff", flex: 1}} edges={["top"]}>
|
|
151
|
+
<Calendar
|
|
152
|
+
theme={{
|
|
153
|
+
typography: {
|
|
154
|
+
fontFamily: 'NunitoSans',
|
|
155
|
+
},
|
|
156
|
+
}}
|
|
157
|
+
resources={resources}
|
|
158
|
+
date={date}
|
|
159
|
+
startMinutes={8 * 60}
|
|
160
|
+
numberOfColumns={numberOfColumns}
|
|
161
|
+
hourHeight={hourHeight}
|
|
162
|
+
eventSlots={{
|
|
163
|
+
// Body: ({event, ctx}) => <EventBody event={event} ctx={ctx}/>,
|
|
164
|
+
TopRight: ({event, ctx}) => <EventTopRight event={event} ctx={ctx}/>,
|
|
165
|
+
}}
|
|
166
|
+
eventStyleOverrides={eventStyleOverrides}
|
|
167
|
+
overLappingLayoutMode={layoutMode}
|
|
168
|
+
/>
|
|
169
|
+
{
|
|
170
|
+
selectedEvent && <View style={styles.bar}>
|
|
171
|
+
<TouchableOpacity
|
|
172
|
+
style={styles.button}
|
|
173
|
+
onPress={() => {
|
|
174
|
+
setSelectedEvent(null);
|
|
175
|
+
}}
|
|
176
|
+
>
|
|
177
|
+
<ThemedText type={'defaultSemiBold'} style={{
|
|
178
|
+
color: "#4d959c"
|
|
179
|
+
}}>
|
|
180
|
+
Cancel
|
|
181
|
+
</ThemedText>
|
|
182
|
+
</TouchableOpacity>
|
|
183
|
+
<TouchableOpacity
|
|
184
|
+
style={[styles.button, {backgroundColor: "#4d959c"}]}
|
|
185
|
+
onPress={() => {
|
|
186
|
+
if (draggedEventDraft) {
|
|
187
|
+
updateResourcesOnDrag(draggedEventDraft!);
|
|
188
|
+
}
|
|
189
|
+
setSelectedEvent(null);
|
|
190
|
+
}}
|
|
191
|
+
>
|
|
192
|
+
<ThemedText type={'defaultSemiBold'}
|
|
193
|
+
style={{
|
|
194
|
+
color: "#fff"
|
|
195
|
+
}}
|
|
196
|
+
>
|
|
197
|
+
Save
|
|
198
|
+
</ThemedText>
|
|
199
|
+
</TouchableOpacity>
|
|
200
|
+
</View>
|
|
201
|
+
}
|
|
202
|
+
<View style={{
|
|
203
|
+
right: 20,
|
|
204
|
+
bottom: 40,
|
|
205
|
+
position: "absolute",
|
|
206
|
+
gap: 12
|
|
207
|
+
}}>
|
|
208
|
+
<TouchableOpacity
|
|
209
|
+
style={styles.floatingButton}
|
|
210
|
+
onPress={() => {
|
|
211
|
+
setDate(new Date());
|
|
212
|
+
}}
|
|
213
|
+
>
|
|
214
|
+
<View
|
|
215
|
+
style={{
|
|
216
|
+
width: 16,
|
|
217
|
+
height: 16,
|
|
218
|
+
backgroundColor: "#4d959c",
|
|
219
|
+
borderRadius: 99
|
|
220
|
+
}}
|
|
221
|
+
/>
|
|
222
|
+
</TouchableOpacity>
|
|
223
|
+
<TouchableOpacity
|
|
224
|
+
style={styles.floatingButton}
|
|
225
|
+
onPress={randomPropsGenerator}
|
|
226
|
+
>
|
|
227
|
+
<FontAwesome name="random" size={16} color="#4d959c"/>
|
|
228
|
+
</TouchableOpacity>
|
|
291
229
|
</View>
|
|
292
|
-
</
|
|
230
|
+
</SafeAreaView>
|
|
293
231
|
);
|
|
294
232
|
}
|
|
295
233
|
```
|
|
@@ -373,3 +311,9 @@ type CalendarTheme = {
|
|
|
373
311
|
};
|
|
374
312
|
};
|
|
375
313
|
```
|
|
314
|
+
|
|
315
|
+
---
|
|
316
|
+
|
|
317
|
+
## 💫 Support the Project
|
|
318
|
+
|
|
319
|
+
If you find this project helpful or interesting, please consider giving it a **⭐️** on GitHub!
|
package/dist/index.d.mts
CHANGED
|
@@ -5,6 +5,7 @@ type ResourceId = number;
|
|
|
5
5
|
type Event = {
|
|
6
6
|
id: number;
|
|
7
7
|
resourceId: ResourceId;
|
|
8
|
+
date: string;
|
|
8
9
|
from: number;
|
|
9
10
|
to: number;
|
|
10
11
|
title?: string;
|
|
@@ -16,12 +17,14 @@ type Event = {
|
|
|
16
17
|
type DisabledBlock = {
|
|
17
18
|
id: number;
|
|
18
19
|
resourceId: ResourceId;
|
|
20
|
+
date: string;
|
|
19
21
|
from: number;
|
|
20
22
|
to: number;
|
|
21
23
|
title?: string;
|
|
22
24
|
};
|
|
23
25
|
type DisabledInterval = {
|
|
24
26
|
resourceId: ResourceId;
|
|
27
|
+
date: string;
|
|
25
28
|
from: number;
|
|
26
29
|
to: number;
|
|
27
30
|
};
|
|
@@ -33,6 +36,7 @@ type Resource = {
|
|
|
33
36
|
type DraggedEventDraft = {
|
|
34
37
|
event: Event;
|
|
35
38
|
from: number;
|
|
39
|
+
date: string;
|
|
36
40
|
to: number;
|
|
37
41
|
resourceId: ResourceId;
|
|
38
42
|
};
|
|
@@ -46,6 +50,7 @@ type LayoutMode = "columns" | "stacked";
|
|
|
46
50
|
type EventRenderContext = {
|
|
47
51
|
hourHeight: number;
|
|
48
52
|
};
|
|
53
|
+
type CalendarMode = 'day' | '3days' | 'week';
|
|
49
54
|
|
|
50
55
|
type EventSlots = {
|
|
51
56
|
TopRight?: React$1.ComponentType<{
|
|
@@ -88,9 +93,12 @@ interface CalendarProps {
|
|
|
88
93
|
isEventDisabled?: FlagFn;
|
|
89
94
|
theme?: CalendarTheme;
|
|
90
95
|
overLappingLayoutMode?: LayoutMode;
|
|
96
|
+
mode?: CalendarMode;
|
|
97
|
+
activeResourceId?: number;
|
|
91
98
|
}
|
|
92
99
|
declare const Calendar: React$1.FC<CalendarProps>;
|
|
93
100
|
|
|
101
|
+
type DayKey = string;
|
|
94
102
|
type SetDayDataPayload = {
|
|
95
103
|
events?: Record<ResourceId, Event[]>;
|
|
96
104
|
disabledBlocks?: Record<ResourceId, DisabledBlock[]>;
|
|
@@ -102,13 +110,15 @@ type CalendarStoreBinding = {
|
|
|
102
110
|
children: React.ReactNode;
|
|
103
111
|
}>;
|
|
104
112
|
useResourceById: (id: ResourceId) => Resource;
|
|
105
|
-
useEventsFor: (resourceId: ResourceId) => ReadonlyArray<Event>;
|
|
106
|
-
useDisabledBlocksFor: (resourceId: ResourceId) => ReadonlyArray<DisabledBlock>;
|
|
107
|
-
useDisabledIntervalsFor: (resourceId: ResourceId) => ReadonlyArray<DisabledInterval>;
|
|
113
|
+
useEventsFor: (resourceId: ResourceId, dayDate: Date) => ReadonlyArray<Event>;
|
|
114
|
+
useDisabledBlocksFor: (resourceId: ResourceId, dayDate: Date) => ReadonlyArray<DisabledBlock>;
|
|
115
|
+
useDisabledIntervalsFor: (resourceId: ResourceId, dayDate: Date) => ReadonlyArray<DisabledInterval>;
|
|
108
116
|
useUpsertResources: () => (rs: Array<Pick<Resource, 'id' | 'name' | 'avatar'>>) => void;
|
|
109
|
-
|
|
117
|
+
useSetDayDataFor: () => (dayKey: DayKey, payload: SetDayDataPayload) => void;
|
|
110
118
|
useGetSelectedEvent: () => Event | null;
|
|
111
119
|
useSetSelectedEvent: () => (ev: Event | null) => void;
|
|
120
|
+
useSetDate: () => (date: Date) => void;
|
|
121
|
+
useGetDate: () => Date;
|
|
112
122
|
useGetDraggedEventDraft: () => DraggedEventDraft | null;
|
|
113
123
|
useSetDraggedEventDraft: () => (draft: DraggedEventDraft | null) => void;
|
|
114
124
|
};
|
package/dist/index.d.ts
CHANGED
|
@@ -5,6 +5,7 @@ type ResourceId = number;
|
|
|
5
5
|
type Event = {
|
|
6
6
|
id: number;
|
|
7
7
|
resourceId: ResourceId;
|
|
8
|
+
date: string;
|
|
8
9
|
from: number;
|
|
9
10
|
to: number;
|
|
10
11
|
title?: string;
|
|
@@ -16,12 +17,14 @@ type Event = {
|
|
|
16
17
|
type DisabledBlock = {
|
|
17
18
|
id: number;
|
|
18
19
|
resourceId: ResourceId;
|
|
20
|
+
date: string;
|
|
19
21
|
from: number;
|
|
20
22
|
to: number;
|
|
21
23
|
title?: string;
|
|
22
24
|
};
|
|
23
25
|
type DisabledInterval = {
|
|
24
26
|
resourceId: ResourceId;
|
|
27
|
+
date: string;
|
|
25
28
|
from: number;
|
|
26
29
|
to: number;
|
|
27
30
|
};
|
|
@@ -33,6 +36,7 @@ type Resource = {
|
|
|
33
36
|
type DraggedEventDraft = {
|
|
34
37
|
event: Event;
|
|
35
38
|
from: number;
|
|
39
|
+
date: string;
|
|
36
40
|
to: number;
|
|
37
41
|
resourceId: ResourceId;
|
|
38
42
|
};
|
|
@@ -46,6 +50,7 @@ type LayoutMode = "columns" | "stacked";
|
|
|
46
50
|
type EventRenderContext = {
|
|
47
51
|
hourHeight: number;
|
|
48
52
|
};
|
|
53
|
+
type CalendarMode = 'day' | '3days' | 'week';
|
|
49
54
|
|
|
50
55
|
type EventSlots = {
|
|
51
56
|
TopRight?: React$1.ComponentType<{
|
|
@@ -88,9 +93,12 @@ interface CalendarProps {
|
|
|
88
93
|
isEventDisabled?: FlagFn;
|
|
89
94
|
theme?: CalendarTheme;
|
|
90
95
|
overLappingLayoutMode?: LayoutMode;
|
|
96
|
+
mode?: CalendarMode;
|
|
97
|
+
activeResourceId?: number;
|
|
91
98
|
}
|
|
92
99
|
declare const Calendar: React$1.FC<CalendarProps>;
|
|
93
100
|
|
|
101
|
+
type DayKey = string;
|
|
94
102
|
type SetDayDataPayload = {
|
|
95
103
|
events?: Record<ResourceId, Event[]>;
|
|
96
104
|
disabledBlocks?: Record<ResourceId, DisabledBlock[]>;
|
|
@@ -102,13 +110,15 @@ type CalendarStoreBinding = {
|
|
|
102
110
|
children: React.ReactNode;
|
|
103
111
|
}>;
|
|
104
112
|
useResourceById: (id: ResourceId) => Resource;
|
|
105
|
-
useEventsFor: (resourceId: ResourceId) => ReadonlyArray<Event>;
|
|
106
|
-
useDisabledBlocksFor: (resourceId: ResourceId) => ReadonlyArray<DisabledBlock>;
|
|
107
|
-
useDisabledIntervalsFor: (resourceId: ResourceId) => ReadonlyArray<DisabledInterval>;
|
|
113
|
+
useEventsFor: (resourceId: ResourceId, dayDate: Date) => ReadonlyArray<Event>;
|
|
114
|
+
useDisabledBlocksFor: (resourceId: ResourceId, dayDate: Date) => ReadonlyArray<DisabledBlock>;
|
|
115
|
+
useDisabledIntervalsFor: (resourceId: ResourceId, dayDate: Date) => ReadonlyArray<DisabledInterval>;
|
|
108
116
|
useUpsertResources: () => (rs: Array<Pick<Resource, 'id' | 'name' | 'avatar'>>) => void;
|
|
109
|
-
|
|
117
|
+
useSetDayDataFor: () => (dayKey: DayKey, payload: SetDayDataPayload) => void;
|
|
110
118
|
useGetSelectedEvent: () => Event | null;
|
|
111
119
|
useSetSelectedEvent: () => (ev: Event | null) => void;
|
|
120
|
+
useSetDate: () => (date: Date) => void;
|
|
121
|
+
useGetDate: () => Date;
|
|
112
122
|
useGetDraggedEventDraft: () => DraggedEventDraft | null;
|
|
113
123
|
useSetDraggedEventDraft: () => (draft: DraggedEventDraft | null) => void;
|
|
114
124
|
};
|