Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Optimize horizontal space when 3+ events are overlapped #61

Merged
merged 1 commit into from
Feb 13, 2021
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
86 changes: 64 additions & 22 deletions src/Events/Events.js
Original file line number Diff line number Diff line change
Expand Up @@ -22,10 +22,10 @@ const EVENTS_CONTAINER_WIDTH = CONTAINER_WIDTH - EVENT_HORIZONTAL_PADDING;
const MIN_ITEM_WIDTH = 4;
const ALLOW_OVERLAP_SECONDS = 2;

const areEventsOverlapped = (event1, event2) => {
const endDate = moment(event1.endDate);
const areEventsOverlapped = (event1EndDate, event2StartDate) => {
const endDate = moment(event1EndDate);
endDate.subtract(ALLOW_OVERLAP_SECONDS, 'seconds');
return endDate.isSameOrAfter(event2.startDate);
return endDate.isSameOrAfter(event2StartDate);
};

class Events extends PureComponent {
Expand All @@ -50,53 +50,95 @@ class Events extends PureComponent {
addOverlappedToArray = (baseArr, overlappedArr, itemWidth) => {
// Given an array of overlapped events (with style), modifies their style to overlap them
// and adds them to a (base) array of events.
if (!overlappedArr) return;

const nOverlapped = overlappedArr.length;
if (nOverlapped === 0) {
return;
}
if (nOverlapped === 1) {
baseArr.push(overlappedArr[0]);
return;
}

let nLanes;
let horizontalPadding;
let indexToLane;
if (nOverlapped === 2) {
nLanes = nOverlapped;
horizontalPadding = 3;
indexToLane = (index) => index;
} else {
const dividedWidth = itemWidth / nOverlapped;
const horizontalPadding = 3;
overlappedArr.forEach((overlappedEventWithStyle, index) => {
const { data, style } = overlappedEventWithStyle;
baseArr.push({
data,
style: {
...style,
width: Math.max(dividedWidth - horizontalPadding, MIN_ITEM_WIDTH),
left: dividedWidth * index,
},
});
// Distribute events in multiple lanes
const maxLanes = nOverlapped;
const latestByLane = {};
const laneByEvent = {};
overlappedArr.forEach((event, index) => {
for (let lane = 0; lane < maxLanes; lane += 1) {
const lastEvtInLaneIndex = latestByLane[lane];
const lastEvtInLane =
(lastEvtInLaneIndex || lastEvtInLaneIndex === 0) &&
overlappedArr[lastEvtInLaneIndex];
if (
!lastEvtInLane ||
!areEventsOverlapped(
lastEvtInLane.data.endDate,
event.data.startDate,
)
) {
// Place in this lane
latestByLane[lane] = index;
laneByEvent[index] = lane;
break;
}
}
});

nLanes = Object.keys(latestByLane).length;
horizontalPadding = 2;
indexToLane = (index) => laneByEvent[index];
}
const dividedWidth = itemWidth / nLanes;
const width = Math.max(dividedWidth - horizontalPadding, MIN_ITEM_WIDTH);

overlappedArr.forEach((eventWithStyle, index) => {
const { data, style } = eventWithStyle;
baseArr.push({
data,
style: {
...style,
width,
left: dividedWidth * indexToLane(index),
},
});
});
};

getEventsWithPosition = (totalEvents) => {
const regularItemWidth = this.getEventItemWidth();

return totalEvents.map((events) => {
let overlappedSoFar = []; // Store events overlapped until now
let lastDate = null;
const eventsWithStyle = events.reduce((eventsAcc, event) => {
const style = this.getStyleForEvent(event);
const eventWithStyle = {
data: event,
style,
};

const nSoFar = overlappedSoFar.length;
const lastEventOverlapped =
nSoFar > 0 ? overlappedSoFar[nSoFar - 1] : null;
if (
!lastEventOverlapped ||
areEventsOverlapped(lastEventOverlapped.data, event)
) {
if (!lastDate || areEventsOverlapped(lastDate, event.startDate)) {
overlappedSoFar.push(eventWithStyle);
const endDate = moment(event.endDate);
lastDate = lastDate ? moment.max(endDate, lastDate) : endDate;
} else {
this.addOverlappedToArray(
eventsAcc,
overlappedSoFar,
regularItemWidth,
);
overlappedSoFar = [eventWithStyle];
lastDate = moment(event.endDate);
}
return eventsAcc;
}, []);
Expand Down