react-native-week-view
react-native-week-view copied to clipboard
Is it possible to change the background color of specific time blocks?

I'm using this library to create a group calendar and it's pretty amazing so far!
Is it possible to change the background color of specific blocks of time? ie. from an array of startDate and endDate objects similar to events. The use case would be to show when the group is free (the green cells). These cells should still be pressable for the onGridClick callback. I have attached a simple mockup. Could you add this in the future for me?
Sounds interesting! Thanks for the detailed description and image, is very helpful
I think you can already achieve this by: setting an empty description, setting colors with a low opacity, and using the onEventPress callback:
const myEvents = [
{
id: 1,
description: '', // Empty, so it looks like an empty timeslot
startDate: new Date(2021, 4, 12, 13), // Specify the timeslot
endDate: new Date(2021, 4, 12, 14),
color: 'rgba(0,255,0,0.2)', // Use a green with low opacity (or any other color, of course)
isTimeslot: true, // Used in the callback later
},
{
id: 2,
description: '',
startDate: new Date(2021, 4, 12, 14),
endDate: new Date(2021, 4, 12, 15),
color: 'rgba(0,255,0,0.2)',
isTimeslot: true,
},
{
id: 3,
description: '',
startDate: new Date(2021, 4, 12, 15),
endDate: new Date(2021, 4, 12, 16),
color: 'rgba(0,255,0,0.2)',
isTimeslot: true,
},
{
id: 4,
description: '',
startDate: new Date(2021, 4, 13, 16),
endDate: new Date(2021, 4, 13, 17),
color: 'rgba(0,255,0,0.2)',
isTimeslot: true,
},
{
id: 5,
description: 'Actual event', // This is an actual event
startDate: new Date(2021, 4, 14, 16),
endDate: new Date(2021, 4, 14, 18, 30),
color: 'blue',
isTimeslot: false,
},
];
const MyComponent = () => (
<WeekView
events={myEvents}
onEventPress={(event) => {
if (event.isTimeslot) {
// handle as empty timeslot...
} else {
// handle as regular event
}
}}
// ...other props
/>
);
This would look like this:

Comments:
- Notice you can use both "actual events" and "timeslot events" in the same events array
- You can change the
isTimeslotfor another name if you want. - You can provide more elements in the event if necessary, like
isTimeslotBusy, or any other information that you may need in the callback.
@jeminsieow does this work for you?
This works perfectly! Thanks a lot @pdpino
Hi @pdpino , how will this work if I want the event will be over the colored grid? Something like this:

I think the current workaround will halve the "actual event" into 2, something like this:

Appreciate the help!
I assume you have two events in tuesday 11: the blue event from 14hrs to 18hrs, and the placeholder event from 14hrs to 23hrs. The events are overlapping, hence their width is reduced so they fit in the day column. To avoid the shrinking and get the expected result, you would have to prevent the events from overlapping, thus you have to change the placeholder event to be from 18hrs to 23hrs.
@pdpino That's correct. While I understand the workaround, it really limits the application of the "available timeslots", as events cannot be created on it. For the application I'm using it for, an overlap of the "available dates" and "actual events" is required.
Is it possible to set an array of time ranges, with its own Component, but keep the grid functionality? (ie. onGridClick, and allowing Events to overlap it). Essentially it'll be like the current events array and EventComponent, but it is just for appearance. onGridClick will still work on it and real events will be placed over it when created.
@jeminsieow to make sure I understand correctly: do you need to be able to press the actual events? (using onPressEvent). I suppose the answer is no. If yes, what happens when the user press a point that is overlapping between a placeholder-event and an actual-event? (e.g. tuesday 16hrs). Should the touch go to the actual event (blue) or the placeholder (light green)?
Is it possible to set an array of time ranges, with its own Component, but keep the grid functionality? (ie. onGridClick, and allowing Events to overlap it).
Regarding this, there is no way to keep a separate array of events that are not reduced in width when there is overlap, something like "background-events". However, if the onEventPress prop is not passed, onGridClick should receive any touch in the grid (even those over the events).
If I understood correctly what you need, and assuming the answer to my question is no, I think you can make it work like this:
-
Use
onGridClickinstead ofonEventPress:- Remove
onEventPress(i.e. do not pass anything, or passonEventPress={null}). - Use
onGridClickto receive presses in any point of the grid (including where there is an event). When the events are not pressable (onEventPress == null), any press should be ignored by the event box and passed to the grid, triggeringonGridClick()(I have not tested this recently, but I have seen it before. I will check again soon).
- Remove
-
Decouple available-slots and placeholder-events:
- Available-slots will be the actual time ranges that are available
- Placeholder-events will represent the available-slots in the grid (the green boxes), but making sure they do not overlap with actual-events. In other words, placeholder-events will represent the available-slots that do not have any actual-events saved yet.
- If you create an actual-event where there is an available-slot, you should remove or reduce the placeholder-event to avoid the overlap (e.g. if you create the blue event from 14hrs to 18hrs, then you reduce the placeholder from 18hrs to 23hrs)
- If that slot is still available, you should keep that information elsewhere (hence, the decoupling)
- This will also allow you to decide how many events you allow per available-slot, without changing the placeholder-events
- Pass actual-events and placeholder-events (without overlap) in the
eventsprop
So, your code would be something like this:
const availableTimeSlots = {} // Save this in any convenient way
const myEvents = [
// actual events...
// placeholder events without overlap...
];
const MyComponent = () => (
<WeekView
events={myEvents}
onGridClick={(pressEvent, hour, date) => {
// Here you need to check if the slot is available, by checking availableTimeSlots
if (isSlotAvailable(availableTimeSlots, hour, date)) {
// handle as available slot
// If you create a new actual-event that will use that place,
// make sure to remove or reduce the corresponding placeholder-event
} else {
// handle as non-available place on the grid
}
}}
// ...other props
/>
);
Does that make sense to you? Am I understanding the problem correctly?
As a side note, consider onGridClick only provides precision up to the hour (not minutes or seconds). If you need, we could add precision up to the minutes, although the press with the fingers is usually not so precise.
Hi @pdpino, thanks for the reply.
@jeminsieow to make sure I understand correctly: do you need to be able to press the actual events? (using
onPressEvent). I suppose the answer is no. If yes, what happens when the user press a point that is overlapping between a placeholder-event and an actual-event? (e.g. tuesday 16hrs). Should the touch go to the actual event (blue) or the placeholder (light green)?
For this, I actually do need to press the actual events. In the case that the actual event overlaps the placeholder-event, the touch will go to the actual event and not the placeholder. (This behaves like onEventPress having priority over onGridPress).
The placeholder (green spaces) is just a change in background of the grid.
Really appreciate the help!
@jeminsieow I see. For now, I think we cannot implement a specific solution for this problem, something like backgroundEvents=[...] or timeslots=[...] (placeholder-events that would be placed in the grid, but do not get halved if there is an overlap, and have less priority than actual-events). It looks like this is too specific, and could still be solved in your app code (see next comment). Also, the implementation inside week-view would get messy, as we'd have to handle two array of events (the regular events and the new ones), handle touch conflicts and z-index conflicts between events, and more.
If we see more use-cases for this in the future, or think of a different approach to tackle this, or if @hoangnm thinks otherwise, I'd be happy to take a shot at it :slightly_smiling_face: UPDATE (october 2021): #140 mentioned "business hours", which is a similar use-case to this. We can definitely address both cases (still in the future, though)
I think the current solution would be:
- Using
onEventPressas press-callback, and distinguish actual-events vs placeholder-events with a boolean likeisTimeslot(like the first example I provided) - Providing actual-events and placeholder-events in the
eventsprop - Decouple available-time-slots and placeholder-events (like the last example I provided), and making sure placeholder-events do not overlap with actual-events (so the width is not halved)
Although is not perfect, it should work (unless I'm missing something)
Update: now you can create blocks with colors to achieve this

The light green events do not get shrank, cannot be pressed neither dragged.
const timeslotFields = {
description: '',
color: 'rgba(0,255,0,0.2)',
eventKind: 'block',
style: {
borderWidth: 0,
},
disablePress: true,
disableDrag: true,
disableLongPress: true,
};
const sampleEvents = [
// Regular events
{
id: 1,
description: 'Event 1',
startDate: new Date(2023, 1, 21, 15),
endDate: new Date(2023, 1, 21, 16, 30),
color: 'blue',
},
{
id: 2,
description: 'Event 2',
startDate: new Date(2023, 1, 23, 12),
endDate: new Date(2023, 1, 23, 14, 30),
color: 'orange',
},
{
id: 3,
description: 'Event 3',
startDate: new Date(2023, 1, 21, 12),
endDate: new Date(2023, 1, 21, 16),
color: 'red',
},
// Timeslot events:
{
id: 10,
startDate: new Date(2023, 1, 20, 9),
endDate: new Date(2023, 1, 20, 17),
...timeslotFields,
},
{
id: 11,
startDate: new Date(2023, 1, 21, 9),
endDate: new Date(2023, 1, 21, 17),
...timeslotFields,
},
{
id: 12,
startDate: new Date(2023, 1, 22, 9),
endDate: new Date(2023, 1, 22, 17),
...timeslotFields,
},
{
id: 13,
startDate: new Date(2023, 1, 23, 9),
endDate: new Date(2023, 1, 23, 17),
...timeslotFields,
},
{
id: 14,
startDate: new Date(2023, 1, 24, 9),
endDate: new Date(2023, 1, 24, 13),
...timeslotFields,
},
];