diff --git a/examples/App.js b/examples/App.js
index 358e8d9e4..d37721a18 100644
--- a/examples/App.js
+++ b/examples/App.js
@@ -17,6 +17,7 @@ import Card from './Card'
import ExampleControlSlot from './ExampleControlSlot'
import Basic from './demos/basic'
import Selectable from './demos/selectable'
+import CreateEventWithNoOverlap from './demos/createEventWithNoOverlap'
import Cultures from './demos/cultures'
import Popup from './demos/popup'
import Rendering from './demos/rendering'
@@ -37,6 +38,7 @@ let demoRoot =
const EXAMPLES = {
basic: 'Basic Calendar',
selectable: 'Create events',
+ createEventWithNoOverlap: 'Create events with no-overlap algorithm',
cultures: 'Localization',
popup: 'Show more via a popup',
timeslots: 'Custom Time Grids',
@@ -82,6 +84,7 @@ class Example extends React.Component {
dnd: Dnd,
dndresource: DndResource,
dndOutsideSource: DndOutsideSource,
+ createEventWithNoOverlap: CreateEventWithNoOverlap,
}[selected]
return (
diff --git a/examples/demos/createEventWithNoOverlap.js b/examples/demos/createEventWithNoOverlap.js
new file mode 100644
index 000000000..f35ef5c37
--- /dev/null
+++ b/examples/demos/createEventWithNoOverlap.js
@@ -0,0 +1,64 @@
+import React from 'react'
+import { Calendar, Views } from 'react-big-calendar'
+import events from '../events'
+import ExampleControlSlot from '../ExampleControlSlot'
+import _ from 'lodash'
+
+const propTypes = {}
+
+class CreateEventWithNoOverlap extends React.Component {
+ constructor(...args) {
+ super(...args)
+
+ this.state = {
+ events: _.cloneDeep(events),
+ dayLayoutAlgorithm: 'no-overlap',
+ }
+ }
+
+ handleSelect = ({ start, end }) => {
+ const title = window.prompt('New Event name')
+ if (title)
+ this.setState({
+ events: [
+ ...this.state.events,
+ {
+ start,
+ end,
+ title,
+ },
+ ],
+ })
+ }
+
+ render() {
+ const { localizer } = this.props
+ return (
+ <>
+
+
+ Click an event to see more info, or drag the mouse over the calendar
+ to select a date/time range.
+
+ The events are being arranged by `no-overlap` algorithm.
+
+
+ alert(event.title)}
+ onSelectSlot={this.handleSelect}
+ dayLayoutAlgorithm={this.state.dayLayoutAlgorithm}
+ />
+ >
+ )
+ }
+}
+
+CreateEventWithNoOverlap.propTypes = propTypes
+
+export default CreateEventWithNoOverlap
diff --git a/examples/events.js b/examples/events.js
index 5be55c81b..08e377027 100644
--- a/examples/events.js
+++ b/examples/events.js
@@ -111,4 +111,52 @@ export default [
start: now,
end: now,
},
+ {
+ id: 16,
+ title: 'Video Record',
+ start: new Date(2015, 3, 14, 15, 30, 0),
+ end: new Date(2015, 3, 14, 19, 0, 0),
+ },
+ {
+ id: 17,
+ title: 'Dutch Song Producing',
+ start: new Date(2015, 3, 14, 16, 30, 0),
+ end: new Date(2015, 3, 14, 20, 0, 0),
+ },
+ {
+ id: 18,
+ title: 'Itaewon Halloween Meeting',
+ start: new Date(2015, 3, 14, 16, 30, 0),
+ end: new Date(2015, 3, 14, 17, 30, 0),
+ },
+ {
+ id: 19,
+ title: 'Online Coding Test',
+ start: new Date(2015, 3, 14, 17, 30, 0),
+ end: new Date(2015, 3, 14, 20, 30, 0),
+ },
+ {
+ id: 20,
+ title: 'An overlapped Event',
+ start: new Date(2015, 3, 14, 17, 0, 0),
+ end: new Date(2015, 3, 14, 18, 30, 0),
+ },
+ {
+ id: 21,
+ title: 'Phone Interview',
+ start: new Date(2015, 3, 14, 17, 0, 0),
+ end: new Date(2015, 3, 14, 18, 30, 0),
+ },
+ {
+ id: 22,
+ title: 'Cooking Class',
+ start: new Date(2015, 3, 14, 17, 30, 0),
+ end: new Date(2015, 3, 14, 19, 0, 0),
+ },
+ {
+ id: 23,
+ title: 'Go to the gym',
+ start: new Date(2015, 3, 14, 18, 30, 0),
+ end: new Date(2015, 3, 14, 20, 0, 0),
+ },
]
diff --git a/src/Calendar.js b/src/Calendar.js
index 0111d180b..91cbbc299 100644
--- a/src/Calendar.js
+++ b/src/Calendar.js
@@ -6,6 +6,7 @@ import {
accessor,
dateFormat,
dateRangeFormat,
+ DayLayoutAlgorithmPropType,
views as componentViews,
} from './utils/propTypes'
import warning from 'warning'
@@ -719,6 +720,14 @@ class Calendar extends React.Component {
noEventsInRange: PropTypes.node,
showMore: PropTypes.func,
}),
+
+ /**
+ * A day event layout(arrangement) algorithm.
+ * `overlap` allows events to be overlapped.
+ * `no-overlap` resizes events to avoid overlap.
+ * or custom `Function(events, minimumStartDifference, slotMetrics, accessors)`
+ */
+ dayLayoutAlgorithm: DayLayoutAlgorithmPropType,
}
static defaultProps = {
@@ -744,6 +753,7 @@ class Calendar extends React.Component {
longPressThreshold: 250,
getNow: () => new Date(),
+ dayLayoutAlgorithm: 'overlap',
}
constructor(...args) {
diff --git a/src/DayColumn.js b/src/DayColumn.js
index 0bd09a0a9..0e508f2a8 100644
--- a/src/DayColumn.js
+++ b/src/DayColumn.js
@@ -12,9 +12,11 @@ import { notify } from './utils/helpers'
import * as DayEventLayout from './utils/DayEventLayout'
import TimeSlotGroup from './TimeSlotGroup'
import TimeGridEvent from './TimeGridEvent'
+import { DayLayoutAlgorithmPropType } from './utils/propTypes'
class DayColumn extends React.Component {
state = { selecting: false, timeIndicatorPosition: null }
+ intervalTriggered = false
constructor(...args) {
super(...args)
@@ -70,7 +72,6 @@ class DayColumn extends React.Component {
}
}
- intervalTriggered = false
/**
* @param tail {Boolean} - whether `positionTimeIndicator` call should be
* deferred or called upon setting interval (`true` - if deferred);
@@ -183,6 +184,7 @@ class DayColumn extends React.Component {
components,
step,
timeslots,
+ dayLayoutAlgorithm,
} = this.props
const { slotMetrics } = this
@@ -193,6 +195,7 @@ class DayColumn extends React.Component {
accessors,
slotMetrics,
minimumStartDifference: Math.ceil((step * timeslots) / 2),
+ dayLayoutAlgorithm,
})
return styledEvents.map(({ event, style }, idx) => {
@@ -402,6 +405,8 @@ DayColumn.propTypes = {
className: PropTypes.string,
dragThroughEvents: PropTypes.bool,
resource: PropTypes.any,
+
+ dayLayoutAlgorithm: DayLayoutAlgorithmPropType,
}
DayColumn.defaultProps = {
diff --git a/src/TimeGrid.js b/src/TimeGrid.js
index d28507861..d0379cfef 100644
--- a/src/TimeGrid.js
+++ b/src/TimeGrid.js
@@ -14,6 +14,7 @@ import TimeGridHeader from './TimeGridHeader'
import { notify } from './utils/helpers'
import { inRange, sortEvents } from './utils/eventLevels'
import Resources from './utils/Resources'
+import { DayLayoutAlgorithmPropType } from './utils/propTypes'
export default class TimeGrid extends Component {
constructor(props) {
@@ -103,7 +104,14 @@ export default class TimeGrid extends Component {
}
renderEvents(range, events, now) {
- let { min, max, components, accessors, localizer } = this.props
+ let {
+ min,
+ max,
+ components,
+ accessors,
+ localizer,
+ dayLayoutAlgorithm,
+ } = this.props
const resources = this.memoizedResources(this.props.resources, accessors)
const groupedEvents = resources.groupEvents(events)
@@ -131,6 +139,7 @@ export default class TimeGrid extends Component {
key={i + '-' + jj}
date={date}
events={daysEvents}
+ dayLayoutAlgorithm={dayLayoutAlgorithm}
/>
)
})
@@ -328,6 +337,8 @@ TimeGrid.propTypes = {
onDoubleClickEvent: PropTypes.func,
onDrillDown: PropTypes.func,
getDrilldownView: PropTypes.func.isRequired,
+
+ dayLayoutAlgorithm: DayLayoutAlgorithmPropType,
}
TimeGrid.defaultProps = {
diff --git a/src/TimeGridEvent.js b/src/TimeGridEvent.js
index 4fbd27a88..b0ccfbaa9 100644
--- a/src/TimeGridEvent.js
+++ b/src/TimeGridEvent.js
@@ -1,6 +1,10 @@
import clsx from 'clsx'
import React from 'react'
+function stringifyPercent(v) {
+ return typeof v === 'string' ? v : v + '%'
+}
+
/* eslint-disable react/prop-types */
function TimeGridEvent(props) {
const {
@@ -42,10 +46,10 @@ function TimeGridEvent(props) {
onDoubleClick={onDoubleClick}
style={{
...userProps.style,
- top: `${top}%`,
- height: `${height}%`,
- [rtl ? 'right' : 'left']: `${Math.max(0, xOffset)}%`,
- width: `${width}%`,
+ top: stringifyPercent(top),
+ [rtl ? 'right' : 'left']: stringifyPercent(xOffset),
+ width: stringifyPercent(width),
+ height: stringifyPercent(height),
}}
title={
tooltip
diff --git a/src/utils/DayEventLayout.js b/src/utils/DayEventLayout.js
index 80610c45a..4ee883772 100644
--- a/src/utils/DayEventLayout.js
+++ b/src/utils/DayEventLayout.js
@@ -1,200 +1,35 @@
-import sortBy from 'lodash/sortBy'
+/*eslint no-unused-vars: "off"*/
-class Event {
- constructor(data, { accessors, slotMetrics }) {
- const {
- start,
- startDate,
- end,
- endDate,
- top,
- height,
- } = slotMetrics.getRange(accessors.start(data), accessors.end(data))
+import overlap from './layout-algorithms/overlap'
+import noOverlap from './layout-algorithms/no-overlap'
- this.start = start
- this.end = end
- this.startMs = +startDate
- this.endMs = +endDate
- this.top = top
- this.height = height
- this.data = data
- }
-
- /**
- * The event's width without any overlap.
- */
- get _width() {
- // The container event's width is determined by the maximum number of
- // events in any of its rows.
- if (this.rows) {
- const columns =
- this.rows.reduce(
- (max, row) => Math.max(max, row.leaves.length + 1), // add itself
- 0
- ) + 1 // add the container
-
- return 100 / columns
- }
-
- const availableWidth = 100 - this.container._width
-
- // The row event's width is the space left by the container, divided
- // among itself and its leaves.
- if (this.leaves) {
- return availableWidth / (this.leaves.length + 1)
- }
-
- // The leaf event's width is determined by its row's width
- return this.row._width
- }
-
- /**
- * The event's calculated width, possibly with extra width added for
- * overlapping effect.
- */
- get width() {
- const noOverlap = this._width
- const overlap = Math.min(100, this._width * 1.7)
-
- // Containers can always grow.
- if (this.rows) {
- return overlap
- }
-
- // Rows can grow if they have leaves.
- if (this.leaves) {
- return this.leaves.length > 0 ? overlap : noOverlap
- }
-
- // Leaves can grow unless they're the last item in a row.
- const { leaves } = this.row
- const index = leaves.indexOf(this)
- return index === leaves.length - 1 ? noOverlap : overlap
- }
-
- get xOffset() {
- // Containers have no offset.
- if (this.rows) return 0
-
- // Rows always start where their container ends.
- if (this.leaves) return this.container._width
-
- // Leaves are spread out evenly on the space left by its row.
- const { leaves, xOffset, _width } = this.row
- const index = leaves.indexOf(this) + 1
- return xOffset + index * _width
- }
+const DefaultAlgorithms = {
+ overlap: overlap,
+ 'no-overlap': noOverlap,
}
-/**
- * Return true if event a and b is considered to be on the same row.
- */
-function onSameRow(a, b, minimumStartDifference) {
- return (
- // Occupies the same start slot.
- Math.abs(b.start - a.start) < minimumStartDifference ||
- // A's start slot overlaps with b's end slot.
- (b.start > a.start && b.start < a.end)
- )
+function isFunction(a) {
+ return !!(a && a.constructor && a.call && a.apply)
}
-function sortByRender(events) {
- const sortedByTime = sortBy(events, ['startMs', e => -e.endMs])
-
- const sorted = []
- while (sortedByTime.length > 0) {
- const event = sortedByTime.shift()
- sorted.push(event)
-
- for (let i = 0; i < sortedByTime.length; i++) {
- const test = sortedByTime[i]
-
- // Still inside this event, look for next.
- if (event.endMs > test.startMs) continue
-
- // We've found the first event of the next event group.
- // If that event is not right next to our current event, we have to
- // move it here.
- if (i > 0) {
- const event = sortedByTime.splice(i, 1)[0]
- sorted.push(event)
- }
-
- // We've already found the next event group, so stop looking.
- break
- }
- }
-
- return sorted
-}
-
-function getStyledEvents({
+//
+export function getStyledEvents({
events,
minimumStartDifference,
slotMetrics,
accessors,
+ dayLayoutAlgorithm, // one of DefaultAlgorithms keys
+ // or custom function
}) {
- // Create proxy events and order them so that we don't have
- // to fiddle with z-indexes.
- const proxies = events.map(
- event => new Event(event, { slotMetrics, accessors })
- )
- const eventsInRenderOrder = sortByRender(proxies)
-
- // Group overlapping events, while keeping order.
- // Every event is always one of: container, row or leaf.
- // Containers can contain rows, and rows can contain leaves.
- const containerEvents = []
- for (let i = 0; i < eventsInRenderOrder.length; i++) {
- const event = eventsInRenderOrder[i]
-
- // Check if this event can go into a container event.
- const container = containerEvents.find(
- c =>
- c.end > event.start ||
- Math.abs(event.start - c.start) < minimumStartDifference
- )
+ let algorithm = null
- // Couldn't find a container — that means this event is a container.
- if (!container) {
- event.rows = []
- containerEvents.push(event)
- continue
- }
+ if (dayLayoutAlgorithm in DefaultAlgorithms)
+ algorithm = DefaultAlgorithms[dayLayoutAlgorithm]
- // Found a container for the event.
- event.container = container
-
- // Check if the event can be placed in an existing row.
- // Start looking from behind.
- let row = null
- for (let j = container.rows.length - 1; !row && j >= 0; j--) {
- if (onSameRow(container.rows[j], event, minimumStartDifference)) {
- row = container.rows[j]
- }
- }
-
- if (row) {
- // Found a row, so add it.
- row.leaves.push(event)
- event.row = row
- } else {
- // Couldn't find a row – that means this event is a row.
- event.leaves = []
- container.rows.push(event)
- }
+ if (!isFunction(algorithm)) {
+ // invalid algorithm
+ return []
}
- // Return the original events, along with their styles.
- return eventsInRenderOrder.map(event => ({
- event: event.data,
- style: {
- top: event.top,
- height: event.height,
- width: event.width,
- xOffset: event.xOffset,
- },
- }))
+ return algorithm.apply(this, arguments)
}
-
-export { getStyledEvents }
diff --git a/src/utils/layout-algorithms/no-overlap.js b/src/utils/layout-algorithms/no-overlap.js
new file mode 100644
index 000000000..94bc4f3bc
--- /dev/null
+++ b/src/utils/layout-algorithms/no-overlap.js
@@ -0,0 +1,108 @@
+import overlap from './overlap'
+
+function getMaxIdxDFS(node, maxIdx, visited) {
+ for (let i = 0; i < node.friends.length; ++i) {
+ if (visited.indexOf(node.friends[i]) > -1) continue
+ maxIdx = maxIdx > node.friends[i].idx ? maxIdx : node.friends[i].idx
+ // TODO : trace it by not object but kinda index or something for performance
+ visited.push(node.friends[i])
+ const newIdx = getMaxIdxDFS(node.friends[i], maxIdx, visited)
+ maxIdx = maxIdx > newIdx ? maxIdx : newIdx
+ }
+ return maxIdx
+}
+
+export default function({
+ events,
+ minimumStartDifference,
+ slotMetrics,
+ accessors,
+}) {
+ const styledEvents = overlap({
+ events,
+ minimumStartDifference,
+ slotMetrics,
+ accessors,
+ })
+
+ styledEvents.sort((a, b) => {
+ a = a.style
+ b = b.style
+ if (a.top !== b.top) return a.top > b.top ? 1 : -1
+ else return a.top + a.height < b.top + b.height ? 1 : -1
+ })
+
+ for (let i = 0; i < styledEvents.length; ++i) {
+ styledEvents[i].friends = []
+ delete styledEvents[i].style.left
+ delete styledEvents[i].style.left
+ delete styledEvents[i].idx
+ delete styledEvents[i].size
+ }
+
+ for (let i = 0; i < styledEvents.length - 1; ++i) {
+ const se1 = styledEvents[i]
+ const y1 = se1.style.top
+ const y2 = se1.style.top + se1.style.height
+
+ for (let j = i + 1; j < styledEvents.length; ++j) {
+ const se2 = styledEvents[j]
+ const y3 = se2.style.top
+ const y4 = se2.style.top + se2.style.height
+
+ // be friends when overlapped
+ if ((y3 <= y1 && y1 < y4) || (y1 <= y3 && y3 < y2)) {
+ // TODO : hashmap would be effective for performance
+ se1.friends.push(se2)
+ se2.friends.push(se1)
+ }
+ }
+ }
+
+ for (let i = 0; i < styledEvents.length; ++i) {
+ const se = styledEvents[i]
+ const bitmap = []
+ for (let j = 0; j < 100; ++j) bitmap.push(1) // 1 means available
+
+ for (let j = 0; j < se.friends.length; ++j)
+ if (se.friends[j].idx !== undefined) bitmap[se.friends[j].idx] = 0 // 0 means reserved
+
+ se.idx = bitmap.indexOf(1)
+ }
+
+ for (let i = 0; i < styledEvents.length; ++i) {
+ let size = 0
+
+ if (styledEvents[i].size) continue
+
+ const allFriends = []
+ const maxIdx = getMaxIdxDFS(styledEvents[i], 0, allFriends)
+ size = 100 / (maxIdx + 1)
+ styledEvents[i].size = size
+
+ for (let j = 0; j < allFriends.length; ++j) allFriends[j].size = size
+ }
+
+ for (let i = 0; i < styledEvents.length; ++i) {
+ const e = styledEvents[i]
+ e.style.left = e.idx * e.size
+
+ // stretch to maximum
+ let maxIdx = 0
+ for (let j = 0; j < e.friends.length; ++j) {
+ const idx = e.friends[j]
+ maxIdx = maxIdx > idx ? maxIdx : idx
+ }
+ if (maxIdx <= e.idx) e.size = 100 - e.idx * e.size
+
+ // padding between events
+ // for this feature, `width` is not percentage based unit anymore
+ // it will be used with calc()
+ const padding = e.idx === 0 ? 0 : 3
+ e.style.width = `calc(${e.size}% - ${padding}px)`
+ e.style.height = `calc(${e.style.height}% - 2px)`
+ e.style.xOffset = `calc(${e.style.left}% + ${padding}px)`
+ }
+
+ return styledEvents
+}
diff --git a/src/utils/layout-algorithms/overlap.js b/src/utils/layout-algorithms/overlap.js
new file mode 100644
index 000000000..b3fa5a200
--- /dev/null
+++ b/src/utils/layout-algorithms/overlap.js
@@ -0,0 +1,198 @@
+import sortBy from 'lodash/sortBy'
+
+class Event {
+ constructor(data, { accessors, slotMetrics }) {
+ const {
+ start,
+ startDate,
+ end,
+ endDate,
+ top,
+ height,
+ } = slotMetrics.getRange(accessors.start(data), accessors.end(data))
+
+ this.start = start
+ this.end = end
+ this.startMs = +startDate
+ this.endMs = +endDate
+ this.top = top
+ this.height = height
+ this.data = data
+ }
+
+ /**
+ * The event's width without any overlap.
+ */
+ get _width() {
+ // The container event's width is determined by the maximum number of
+ // events in any of its rows.
+ if (this.rows) {
+ const columns =
+ this.rows.reduce(
+ (max, row) => Math.max(max, row.leaves.length + 1), // add itself
+ 0
+ ) + 1 // add the container
+
+ return 100 / columns
+ }
+
+ const availableWidth = 100 - this.container._width
+
+ // The row event's width is the space left by the container, divided
+ // among itself and its leaves.
+ if (this.leaves) {
+ return availableWidth / (this.leaves.length + 1)
+ }
+
+ // The leaf event's width is determined by its row's width
+ return this.row._width
+ }
+
+ /**
+ * The event's calculated width, possibly with extra width added for
+ * overlapping effect.
+ */
+ get width() {
+ const noOverlap = this._width
+ const overlap = Math.min(100, this._width * 1.7)
+
+ // Containers can always grow.
+ if (this.rows) {
+ return overlap
+ }
+
+ // Rows can grow if they have leaves.
+ if (this.leaves) {
+ return this.leaves.length > 0 ? overlap : noOverlap
+ }
+
+ // Leaves can grow unless they're the last item in a row.
+ const { leaves } = this.row
+ const index = leaves.indexOf(this)
+ return index === leaves.length - 1 ? noOverlap : overlap
+ }
+
+ get xOffset() {
+ // Containers have no offset.
+ if (this.rows) return 0
+
+ // Rows always start where their container ends.
+ if (this.leaves) return this.container._width
+
+ // Leaves are spread out evenly on the space left by its row.
+ const { leaves, xOffset, _width } = this.row
+ const index = leaves.indexOf(this) + 1
+ return xOffset + index * _width
+ }
+}
+
+/**
+ * Return true if event a and b is considered to be on the same row.
+ */
+function onSameRow(a, b, minimumStartDifference) {
+ return (
+ // Occupies the same start slot.
+ Math.abs(b.start - a.start) < minimumStartDifference ||
+ // A's start slot overlaps with b's end slot.
+ (b.start > a.start && b.start < a.end)
+ )
+}
+
+function sortByRender(events) {
+ const sortedByTime = sortBy(events, ['startMs', e => -e.endMs])
+
+ const sorted = []
+ while (sortedByTime.length > 0) {
+ const event = sortedByTime.shift()
+ sorted.push(event)
+
+ for (let i = 0; i < sortedByTime.length; i++) {
+ const test = sortedByTime[i]
+
+ // Still inside this event, look for next.
+ if (event.endMs > test.startMs) continue
+
+ // We've found the first event of the next event group.
+ // If that event is not right next to our current event, we have to
+ // move it here.
+ if (i > 0) {
+ const event = sortedByTime.splice(i, 1)[0]
+ sorted.push(event)
+ }
+
+ // We've already found the next event group, so stop looking.
+ break
+ }
+ }
+
+ return sorted
+}
+
+export default function getStyledEvents({
+ events,
+ minimumStartDifference,
+ slotMetrics,
+ accessors,
+}) {
+ // Create proxy events and order them so that we don't have
+ // to fiddle with z-indexes.
+ const proxies = events.map(
+ event => new Event(event, { slotMetrics, accessors })
+ )
+ const eventsInRenderOrder = sortByRender(proxies)
+
+ // Group overlapping events, while keeping order.
+ // Every event is always one of: container, row or leaf.
+ // Containers can contain rows, and rows can contain leaves.
+ const containerEvents = []
+ for (let i = 0; i < eventsInRenderOrder.length; i++) {
+ const event = eventsInRenderOrder[i]
+
+ // Check if this event can go into a container event.
+ const container = containerEvents.find(
+ c =>
+ c.end > event.start ||
+ Math.abs(event.start - c.start) < minimumStartDifference
+ )
+
+ // Couldn't find a container — that means this event is a container.
+ if (!container) {
+ event.rows = []
+ containerEvents.push(event)
+ continue
+ }
+
+ // Found a container for the event.
+ event.container = container
+
+ // Check if the event can be placed in an existing row.
+ // Start looking from behind.
+ let row = null
+ for (let j = container.rows.length - 1; !row && j >= 0; j--) {
+ if (onSameRow(container.rows[j], event, minimumStartDifference)) {
+ row = container.rows[j]
+ }
+ }
+
+ if (row) {
+ // Found a row, so add it.
+ row.leaves.push(event)
+ event.row = row
+ } else {
+ // Couldn't find a row – that means this event is a row.
+ event.leaves = []
+ container.rows.push(event)
+ }
+ }
+
+ // Return the original events, along with their styles.
+ return eventsInRenderOrder.map(event => ({
+ event: event.data,
+ style: {
+ top: event.top,
+ height: event.height,
+ width: event.width,
+ xOffset: Math.max(0, event.xOffset),
+ },
+ }))
+}
diff --git a/src/utils/propTypes.js b/src/utils/propTypes.js
index b95c524ee..b08bd5788 100644
--- a/src/utils/propTypes.js
+++ b/src/utils/propTypes.js
@@ -39,3 +39,8 @@ export let views = PropTypes.oneOfType([
}
}),
])
+
+export const DayLayoutAlgorithmPropType = PropTypes.oneOfType([
+ PropTypes.oneOf(['overlap', 'no-overlap']),
+ PropTypes.func,
+])
diff --git a/test/utils/DayEventLayout.test.js b/test/utils/DayEventLayout.test.js
index f6505b1a3..927c69728 100644
--- a/test/utils/DayEventLayout.test.js
+++ b/test/utils/DayEventLayout.test.js
@@ -8,6 +8,7 @@ describe('getStyledEvents', () => {
const max = dates.endOf(d(), 'day')
const slotMetrics = getSlotMetrics({ min, max, step: 30, timeslots: 4 })
const accessors = { start: e => e.start, end: e => e.end }
+ const dayLayoutAlgorithm = 'overlap'
describe('matrix', () => {
function compare(title, events, expectedResults) {
@@ -17,6 +18,7 @@ describe('getStyledEvents', () => {
accessors,
slotMetrics,
minimumStartDifference: 10,
+ dayLayoutAlgorithm,
})
const results = styledEvents.map(result => ({
width: Math.floor(result.style.width),