@babylimon/react-calendar-timeline
Version:
react calendar timeline
1,023 lines (886 loc) • 29.8 kB
JavaScript
import PropTypes from 'prop-types'
import React, { Component } from 'react'
import Sidebar from './layout/Sidebar'
import ScrollElement from './scroll/ScrollElement'
import MarkerCanvas from './markers/MarkerCanvas'
import Rows from './rows/Rows'
import windowResizeDetector from '../resize-detector/window'
import {
getMinUnit,
calculateTimeForXPosition,
getCanvasBoundariesFromVisibleTime,
getCanvasWidth,
calculateScrollCanvas,
stackTimelineItems,
} from './utility/calendar'
import { _get, _length } from './utility/generic'
import {
defaultKeys,
defaultTimeSteps,
defaultHeaderLabelFormats,
defaultSubHeaderLabelFormats
} from './default-config'
import { TimelineStateProvider } from './timeline/TimelineStateContext'
import { TimelineMarkersProvider } from './markers/TimelineMarkersContext'
import { TimelineHeadersProvider } from './headers/HeadersContext'
import TimelineHeaders from './headers/TimelineHeaders'
import DateHeader from './headers/DateHeader'
import DefaultLayer from './rows/DefaultLayer'
import Columns from './columns/Columns'
import { HelpersContextProvider } from './timeline/HelpersContext'
export default class ReactCalendarTimeline extends Component {
static propTypes = {
groups: PropTypes.oneOfType([PropTypes.array, PropTypes.object]).isRequired,
items: PropTypes.oneOfType([PropTypes.array, PropTypes.object]).isRequired,
sidebarWidth: PropTypes.number,
rightSidebarWidth: PropTypes.number,
dragSnap: PropTypes.number,
minResizeWidth: PropTypes.number,
stickyHeader: PropTypes.bool,
lineHeight: PropTypes.number,
itemHeightRatio: PropTypes.number,
minZoom: PropTypes.number,
maxZoom: PropTypes.number,
clickTolerance: PropTypes.number,
canChangeGroup: PropTypes.bool,
canMove: PropTypes.bool,
canResize: PropTypes.oneOf([true, false, 'left', 'right', 'both']),
useResizeHandle: PropTypes.bool,
canSelect: PropTypes.bool,
stackItems: PropTypes.bool,
traditionalZoom: PropTypes.bool,
itemTouchSendsClick: PropTypes.bool,
horizontalLineClassNamesForGroup: PropTypes.func,
onItemMove: PropTypes.func,
onItemResize: PropTypes.func,
onItemClick: PropTypes.func,
onItemSelect: PropTypes.func,
onItemDeselect: PropTypes.func,
onCanvasClick: PropTypes.func,
onItemDoubleClick: PropTypes.func,
onItemContextMenu: PropTypes.func,
onCanvasDoubleClick: PropTypes.func,
onCanvasContextMenu: PropTypes.func,
onZoom: PropTypes.func,
onItemDrag: PropTypes.func,
moveResizeValidator: PropTypes.func,
itemRenderer: PropTypes.func,
groupRenderer: PropTypes.func,
style: PropTypes.object,
keys: PropTypes.shape({
groupIdKey: PropTypes.string,
groupTitleKey: PropTypes.string,
groupLabelKey: PropTypes.string,
groupRightTitleKey: PropTypes.string,
itemIdKey: PropTypes.string,
itemTitleKey: PropTypes.string,
itemDivTitleKey: PropTypes.string,
itemGroupKey: PropTypes.string,
itemTimeStartKey: PropTypes.string,
itemTimeEndKey: PropTypes.string
}),
headerRef: PropTypes.func,
scrollRef: PropTypes.func,
timeSteps: PropTypes.shape({
second: PropTypes.number,
minute: PropTypes.number,
hour: PropTypes.number,
day: PropTypes.number,
month: PropTypes.number,
year: PropTypes.number
}),
defaultTimeStart: PropTypes.object,
defaultTimeEnd: PropTypes.object,
visibleTimeStart: PropTypes.number,
visibleTimeEnd: PropTypes.number,
onTimeChange: PropTypes.func,
onBoundsChange: PropTypes.func,
selected: PropTypes.array,
headerLabelFormats: PropTypes.shape({
yearShort: PropTypes.string,
yearLong: PropTypes.string,
monthShort: PropTypes.string,
monthMedium: PropTypes.string,
monthMediumLong: PropTypes.string,
monthLong: PropTypes.string,
dayShort: PropTypes.string,
dayLong: PropTypes.string,
hourShort: PropTypes.string,
hourMedium: PropTypes.string,
hourMediumLong: PropTypes.string,
hourLong: PropTypes.string
}),
subHeaderLabelFormats: PropTypes.shape({
yearShort: PropTypes.string,
yearLong: PropTypes.string,
monthShort: PropTypes.string,
monthMedium: PropTypes.string,
monthLong: PropTypes.string,
dayShort: PropTypes.string,
dayMedium: PropTypes.string,
dayMediumLong: PropTypes.string,
dayLong: PropTypes.string,
hourShort: PropTypes.string,
hourLong: PropTypes.string,
minuteShort: PropTypes.string,
minuteLong: PropTypes.string
}),
resizeDetector: PropTypes.shape({
addListener: PropTypes.func,
removeListener: PropTypes.func
}),
verticalLineClassNamesForTime: PropTypes.func,
children: PropTypes.node,
rowRenderer: PropTypes.func,
rowData: PropTypes.object,
hideHorizontalLines : PropTypes.bool,
}
static defaultProps = {
sidebarWidth: 150,
rightSidebarWidth: 0,
dragSnap: 1000 * 60 * 15, // 15min
minResizeWidth: 20,
stickyHeader: true,
lineHeight: 30,
itemHeightRatio: 0.65,
minZoom: 60 * 60 * 1000, // 1 hour
maxZoom: 5 * 365.24 * 86400 * 1000, // 5 years
clickTolerance: 3, // how many pixels can we drag for it to be still considered a click?
canChangeGroup: true,
canMove: true,
canResize: 'right',
useResizeHandle: false,
canSelect: true,
stackItems: false,
traditionalZoom: false,
horizontalLineClassNamesForGroup: null,
onItemMove: null,
onItemResize: null,
onItemClick: null,
onItemSelect: null,
onItemDeselect: null,
onItemDrag: null,
onCanvasClick: null,
onItemDoubleClick: null,
onItemContextMenu: null,
onZoom: null,
verticalLineClassNamesForTime: null,
moveResizeValidator: null,
dayBackground: null,
defaultTimeStart: null,
defaultTimeEnd: null,
itemTouchSendsClick: false,
style: {},
keys: defaultKeys,
timeSteps: defaultTimeSteps,
headerRef: () => {},
scrollRef: () => {},
// if you pass in visibleTimeStart and visibleTimeEnd, you must also pass onTimeChange(visibleTimeStart, visibleTimeEnd),
// which needs to update the props visibleTimeStart and visibleTimeEnd to the ones passed
visibleTimeStart: null,
visibleTimeEnd: null,
onTimeChange: function(
visibleTimeStart,
visibleTimeEnd,
updateScrollCanvas
) {
updateScrollCanvas(visibleTimeStart, visibleTimeEnd)
},
// called when the canvas area of the calendar changes
onBoundsChange: null,
children: null,
headerLabelFormats: defaultHeaderLabelFormats,
subHeaderLabelFormats: defaultSubHeaderLabelFormats,
selected: null,
rowRenderer: DefaultLayer,
rowData: {},
hideHorizontalLines: false,
}
static childContextTypes = {
getTimelineContext: PropTypes.func
}
getChildContext() {
return {
getTimelineContext: () => {
return this.getTimelineContext()
}
}
}
getTimelineContext = () => {
const {
width,
visibleTimeStart,
visibleTimeEnd,
canvasTimeStart,
canvasTimeEnd
} = this.state
return {
timelineWidth: width,
visibleTimeStart,
visibleTimeEnd,
canvasTimeStart,
canvasTimeEnd
}
}
constructor(props) {
super(props)
this.getSelected = this.getSelected.bind(this)
this.hasSelectedItem = this.hasSelectedItem.bind(this)
this.isItemSelected= this.isItemSelected.bind(this)
let visibleTimeStart = null
let visibleTimeEnd = null
if (this.props.defaultTimeStart && this.props.defaultTimeEnd) {
visibleTimeStart = this.props.defaultTimeStart.valueOf()
visibleTimeEnd = this.props.defaultTimeEnd.valueOf()
} else if (this.props.visibleTimeStart && this.props.visibleTimeEnd) {
visibleTimeStart = this.props.visibleTimeStart
visibleTimeEnd = this.props.visibleTimeEnd
} else {
//throwing an error because neither default or visible time props provided
throw new Error(
'You must provide either "defaultTimeStart" and "defaultTimeEnd" or "visibleTimeStart" and "visibleTimeEnd" to initialize the Timeline'
)
}
const [canvasTimeStart, canvasTimeEnd] = getCanvasBoundariesFromVisibleTime(
visibleTimeStart,
visibleTimeEnd
)
this.state = {
width: 1000,
visibleTimeStart: visibleTimeStart,
visibleTimeEnd: visibleTimeEnd,
canvasTimeStart: canvasTimeStart,
canvasTimeEnd: canvasTimeEnd,
selectedItem: null,
dragTime: null,
resizingItem: null,
resizeTime: undefined,
resizingEdge: undefined
}
const canvasWidth = getCanvasWidth(this.state.width)
const {
groupsWithItemsDimensions,
height,
groupHeights,
groupTops,
itemsWithInteractions,
} = stackTimelineItems(
props.items,
props.groups,
canvasWidth,
this.state.canvasTimeStart,
this.state.canvasTimeEnd,
props.keys,
props.lineHeight,
props.itemHeightRatio,
props.stackItems,
this.state.draggingItem,
this.state.resizingItem,
this.state.dragTime,
this.state.resizingEdge,
this.state.resizeTime,
this.state.newGroupId
)
/* eslint-disable react/no-direct-mutation-state */
this.state.groupsWithItemsDimensions = groupsWithItemsDimensions
this.state.height = height
this.state.groupHeights = groupHeights
this.state.groupTops = groupTops
this.state.itemsWithInteractions = itemsWithInteractions
/* eslint-enable */
}
componentDidMount() {
this.resize(this.props)
if (this.props.resizeDetector && this.props.resizeDetector.addListener) {
this.props.resizeDetector.addListener(this)
}
windowResizeDetector.addListener(this)
this.lastTouchDistance = null
}
componentWillUnmount() {
if (this.props.resizeDetector && this.props.resizeDetector.addListener) {
this.props.resizeDetector.removeListener(this)
}
windowResizeDetector.removeListener(this)
}
static getDerivedStateFromProps(nextProps, prevState) {
const { visibleTimeStart, visibleTimeEnd, items, groups } = nextProps
// This is a gross hack pushing items and groups in to state only to allow
// For the forceUpdate check
let derivedState = { items, groups }
// if the items or groups have changed we must re-render
const forceUpdate = items !== prevState.items || groups !== prevState.groups
// We are a controlled component
if (visibleTimeStart && visibleTimeEnd) {
// Get the new canvas position
Object.assign(
derivedState,
calculateScrollCanvas(
visibleTimeStart,
visibleTimeEnd,
forceUpdate,
items,
groups,
nextProps,
prevState
)
)
} else if (forceUpdate) {
// Calculate new item stack position as canvas may have changed
const canvasWidth = getCanvasWidth(prevState.width)
Object.assign(
derivedState,
stackTimelineItems(
items,
groups,
canvasWidth,
prevState.canvasTimeStart,
prevState.canvasTimeEnd,
nextProps.keys,
nextProps.lineHeight,
nextProps.itemHeightRatio,
nextProps.stackItems,
prevState.draggingItem,
prevState.resizingItem,
prevState.dragTime,
prevState.resizingEdge,
prevState.resizeTime,
prevState.newGroupId
)
)
}
return derivedState
}
componentDidUpdate(prevProps, prevState) {
const newZoom = this.state.visibleTimeEnd - this.state.visibleTimeStart
const oldZoom = prevState.visibleTimeEnd - prevState.visibleTimeStart
// are we changing zoom? Report it!
if (this.props.onZoom && newZoom !== oldZoom) {
this.props.onZoom(this.getTimelineContext())
}
// The bounds have changed? Report it!
if (
this.props.onBoundsChange &&
this.state.canvasTimeStart !== prevState.canvasTimeStart
) {
this.props.onBoundsChange(
this.state.canvasTimeStart,
this.state.canvasTimeStart + newZoom * 3
)
}
// Check the scroll is correct
const scrollLeft = Math.round(
this.state.width *
(this.state.visibleTimeStart - this.state.canvasTimeStart) /
newZoom
)
const componentScrollLeft = Math.round(
prevState.width *
(prevState.visibleTimeStart - prevState.canvasTimeStart) /
oldZoom
)
if (componentScrollLeft !== scrollLeft) {
this.scrollComponent.scrollLeft = scrollLeft
this.scrollHeaderRef.scrollLeft = scrollLeft
}
}
resize = (props = this.props) => {
const { width: containerWidth } = this.container.getBoundingClientRect()
let width = containerWidth - props.sidebarWidth - props.rightSidebarWidth
const canvasWidth = getCanvasWidth(width)
const {
groupsWithItemsDimensions,
height,
groupHeights,
groupTops
} = stackTimelineItems(
props.items,
props.groups,
canvasWidth,
this.state.canvasTimeStart,
this.state.canvasTimeEnd,
props.keys,
props.lineHeight,
props.itemHeightRatio,
props.stackItems,
this.state.draggingItem,
this.state.resizingItem,
this.state.dragTime,
this.state.resizingEdge,
this.state.resizeTime,
this.state.newGroupId
)
// this is needed by dragItem since it uses pageY from the drag events
// if this was in the context of the scrollElement, this would not be necessary
this.setState({
width,
groupsWithItemsDimensions,
height,
groupHeights,
groupTops
})
this.scrollComponent.scrollLeft = width
this.scrollHeaderRef.scrollLeft = width
}
onScroll = scrollX => {
const width = this.state.width
const canvasTimeStart = this.state.canvasTimeStart
const zoom = this.state.visibleTimeEnd - this.state.visibleTimeStart
const visibleTimeStart = canvasTimeStart + zoom * scrollX / width
if (
this.state.visibleTimeStart !== visibleTimeStart ||
this.state.visibleTimeEnd !== visibleTimeStart + zoom
) {
this.props.onTimeChange(
visibleTimeStart,
visibleTimeStart + zoom,
this.updateScrollCanvas
)
}
}
// called when the visible time changes
updateScrollCanvas = (
visibleTimeStart,
visibleTimeEnd,
forceUpdateDimensions,
items = this.props.items,
groups = this.props.groups
) => {
this.setState(
calculateScrollCanvas(
visibleTimeStart,
visibleTimeEnd,
forceUpdateDimensions,
items,
groups,
this.props,
this.state
)
)
}
handleWheelZoom = (speed, xPosition, deltaY) => {
this.changeZoom(1.0 + speed * deltaY / 500, xPosition / this.state.width)
}
changeZoom = (scale, offset = 0.5) => {
const { minZoom, maxZoom } = this.props
const oldZoom = this.state.visibleTimeEnd - this.state.visibleTimeStart
const newZoom = Math.min(
Math.max(Math.round(oldZoom * scale), minZoom),
maxZoom
) // min 1 min, max 20 years
const newVisibleTimeStart = Math.round(
this.state.visibleTimeStart + (oldZoom - newZoom) * offset
)
this.props.onTimeChange(
newVisibleTimeStart,
newVisibleTimeStart + newZoom,
this.updateScrollCanvas
)
}
showPeriod = (from, to) => {
let visibleTimeStart = from.valueOf()
let visibleTimeEnd = to.valueOf()
let zoom = visibleTimeEnd - visibleTimeStart
// can't zoom in more than to show one hour
if (zoom < 360000) {
return
}
this.props.onTimeChange(
visibleTimeStart,
visibleTimeStart + zoom,
this.updateScrollCanvas
)
}
selectItem = (item, clickType, e) => {
if (
this.isItemSelected(item) ||
(this.props.itemTouchSendsClick && clickType === 'touch')
) {
if (item && this.props.onItemClick) {
const time = this.timeFromItemEvent(e)
this.props.onItemClick(item, e, time)
}
} else {
this.setState({ selectedItem: item })
if (item && this.props.onItemSelect) {
const time = this.timeFromItemEvent(e)
this.props.onItemSelect(item, e, time)
} else if (item === null && this.props.onItemDeselect) {
this.props.onItemDeselect(e) // this isnt in the docs. Is this function even used?
}
}
}
doubleClickItem = (item, e) => {
if (this.props.onItemDoubleClick) {
const time = this.timeFromItemEvent(e)
this.props.onItemDoubleClick(item, e, time)
}
}
contextMenuClickItem = (item, e) => {
if (this.props.onItemContextMenu) {
const time = this.timeFromItemEvent(e)
this.props.onItemContextMenu(item, e, time)
}
}
// TODO: this is very similar to timeFromItemEvent, aside from which element to get offsets
// from. Look to consolidate the logic for determining coordinate to time
// as well as generalizing how we get time from click on the canvas
getTimeFromRowClickEvent = e => {
const { dragSnap } = this.props
const { width, canvasTimeStart, canvasTimeEnd } = this.state
// this gives us distance from left of row element, so event is in
// context of the row element, not client or page
const { offsetX } = e.nativeEvent
let time = calculateTimeForXPosition(
canvasTimeStart,
canvasTimeEnd,
getCanvasWidth(width),
offsetX
)
time = Math.floor(time / dragSnap) * dragSnap
return time
}
timeFromItemEvent = e => {
const { width, visibleTimeStart, visibleTimeEnd } = this.state
const { dragSnap } = this.props
const scrollComponent = this.scrollComponent
const { left: scrollX } = scrollComponent.getBoundingClientRect()
const xRelativeToTimeline = e.clientX - scrollX
const relativeItemPosition = xRelativeToTimeline / width
const zoom = visibleTimeEnd - visibleTimeStart
const timeOffset = relativeItemPosition * zoom
let time = Math.round(visibleTimeStart + timeOffset)
time = Math.floor(time / dragSnap) * dragSnap
return time
}
dragItem = (item, dragTime, newGroupId) => {
this.setState({
draggingItem: item,
dragTime: dragTime,
newGroupId: newGroupId,
})
this.updatingItem({
eventType: 'move',
itemId: item,
time: dragTime,
newGroupId
})
}
dropItem = (item, dragTime, newGroupId) => {
this.setState({ draggingItem: null, dragTime: null })
if (this.props.onItemMove) {
this.props.onItemMove(item, dragTime, newGroupId)
}
}
resizingItem = (item, resizeTime, edge) => {
this.setState({
resizingItem: item,
resizingEdge: edge,
resizeTime: resizeTime
})
this.updatingItem({
eventType: 'resize',
itemId: item,
time: resizeTime,
edge
})
}
resizedItem = (item, resizeTime, edge, timeDelta) => {
this.setState({ resizingItem: null, resizingEdge: null, resizeTime: null })
if (this.props.onItemResize && timeDelta !== 0) {
this.props.onItemResize(item, resizeTime, edge)
}
}
updatingItem = ({ eventType, itemId, time, edge, newGroupId }) => {
if (this.props.onItemDrag) {
this.props.onItemDrag({ eventType, itemId, time, edge, newGroupId })
}
}
handleRowClick = (e, rowIndex) => {
// shouldnt this be handled by the user, as far as when to deselect an item?
if (this.hasSelectedItem()) {
this.selectItem(null)
}
if (this.props.onCanvasClick == null) return
const time = this.getTimeFromRowClickEvent(e)
const groupId = _get(
this.props.groups[rowIndex],
this.props.keys.groupIdKey
)
this.props.onCanvasClick(groupId, time, e)
}
handleRowDoubleClick = (e, rowIndex) => {
if (this.props.onCanvasDoubleClick == null) return
const time = this.getTimeFromRowClickEvent(e)
const groupId = _get(
this.props.groups[rowIndex],
this.props.keys.groupIdKey
)
this.props.onCanvasDoubleClick(groupId, time, e)
}
handleScrollContextMenu = (e, rowIndex) => {
if (this.props.onCanvasContextMenu == null) return
const timePosition = this.getTimeFromRowClickEvent(e)
const groupId = _get(
this.props.groups[rowIndex],
this.props.keys.groupIdKey
)
if (this.props.onCanvasContextMenu) {
e.preventDefault()
this.props.onCanvasContextMenu(groupId, timePosition, e)
}
}
handleHeaderRef = el => {
this.scrollHeaderRef = el
this.props.headerRef(el)
}
sidebar(height, groupHeights) {
const { sidebarWidth } = this.props
return (
sidebarWidth && (
<Sidebar
groups={this.props.groups}
groupRenderer={this.props.groupRenderer}
keys={this.props.keys}
width={sidebarWidth}
groupHeights={groupHeights}
height={height}
/>
)
)
}
rightSidebar(height, groupHeights) {
const { rightSidebarWidth } = this.props
return (
rightSidebarWidth && (
<Sidebar
groups={this.props.groups}
keys={this.props.keys}
groupRenderer={this.props.groupRenderer}
isRightSidebar
width={rightSidebarWidth}
groupHeights={groupHeights}
height={height}
/>
)
)
}
/**
* check if child of type TimelineHeader
* refer to for explanation https://github.com/gaearon/react-hot-loader#checking-element-types
*/
isTimelineHeader = (child) => {
if(child.type === undefined) return false
return child.type.secretKey ===TimelineHeaders.secretKey
}
childrenWithProps() {
if (!this.props.children) {
return null
}
// convert to an array and remove the nulls
const childArray = Array.isArray(this.props.children)
? this.props.children.filter(c => c)
: [this.props.children]
return React.Children.map(childArray, child => {
if (!this.isTimelineHeader(child)) {
return child
} else {
return null
}
})
}
renderHeaders = () => {
if (this.props.children) {
let headerRenderer
React.Children.map(this.props.children, child => {
if (this.isTimelineHeader(child)) {
headerRenderer = child
}
})
if (headerRenderer) {
return headerRenderer
}
}
return (
<TimelineHeaders>
<DateHeader unit="primaryHeader" />
<DateHeader />
</TimelineHeaders>
)
}
getSelected() {
return this.state.selectedItem && !this.props.selected
? [this.state.selectedItem]
: this.props.selected || [];
}
hasSelectedItem(){
if(!Array.isArray(this.props.selected)) return !!this.state.selectedItem
return this.props.selected.length > 0
}
isItemSelected(itemId){
const selectedItems = this.getSelected()
return selectedItems.some(i => i === itemId)
}
getScrollElementRef = el => {
this.props.scrollRef(el)
this.scrollComponent = el
}
render() {
const {
items,
groups,
sidebarWidth,
rightSidebarWidth,
timeSteps,
traditionalZoom,
itemRenderer,
keys,
hideHorizontalLines,
} = this.props
const {
draggingItem,
resizingItem,
width,
visibleTimeStart,
visibleTimeEnd,
canvasTimeStart,
canvasTimeEnd
} = this.state
let { groupsWithItemsDimensions, height, groupHeights, groupTops, itemsWithInteractions } = this.state
const zoom = visibleTimeEnd - visibleTimeStart
const canvasWidth = getCanvasWidth(width)
const minUnit = getMinUnit(zoom, width, timeSteps)
const isInteractingWithItem = !!draggingItem || !!resizingItem
if (isInteractingWithItem) {
const stackResults = stackTimelineItems(
items,
groups,
canvasWidth,
this.state.canvasTimeStart,
this.state.canvasTimeEnd,
this.props.keys,
this.props.lineHeight,
this.props.itemHeightRatio,
this.props.stackItems,
this.state.draggingItem,
this.state.resizingItem,
this.state.dragTime,
this.state.resizingEdge,
this.state.resizeTime,
this.state.newGroupId
)
groupsWithItemsDimensions = stackResults.groupsWithItemsDimensions
height = stackResults.height
groupHeights = stackResults.groupHeights
groupTops = stackResults.groupTops
itemsWithInteractions = stackResults.itemsWithInteractions
}
const outerComponentStyle = {
height: `${height}px`
}
return (
<TimelineStateProvider
visibleTimeStart={visibleTimeStart}
visibleTimeEnd={visibleTimeEnd}
canvasTimeStart={canvasTimeStart}
canvasTimeEnd={canvasTimeEnd}
canvasWidth={canvasWidth}
showPeriod={this.showPeriod}
timelineUnit={minUnit}
timelineWidth={this.state.width}
keys={keys}
>
<TimelineMarkersProvider>
<TimelineHeadersProvider
registerScroll={this.handleHeaderRef}
timeSteps={timeSteps}
leftSidebarWidth={this.props.sidebarWidth}
rightSidebarWidth={this.props.rightSidebarWidth}
>
<HelpersContextProvider
groupsWithItemsDimensions={groupsWithItemsDimensions}
items={isInteractingWithItem? itemsWithInteractions : items}
keys={keys}
groupHeights={groupHeights}
groupTops={groupTops}
>
<div
style={this.props.style}
ref={el => (this.container = el)}
className="react-calendar-timeline"
>
{this.renderHeaders()}
<div style={outerComponentStyle} className="rct-outer">
{sidebarWidth > 0 ? this.sidebar(height, groupHeights) : null}
<ScrollElement
scrollRef={this.getScrollElementRef}
width={width}
height={height}
onZoom={this.changeZoom}
onWheelZoom={this.handleWheelZoom}
traditionalZoom={traditionalZoom}
onScroll={this.onScroll}
isInteractingWithItem={isInteractingWithItem}
>
<MarkerCanvas>
{this.childrenWithProps()}
<Rows
items={isInteractingWithItem? itemsWithInteractions : items}
groupHeights={groupHeights}
itemRenderer={itemRenderer}
itemResized={this.resizedItem}
itemResizing={this.resizingItem}
itemSelect={this.selectItem}
itemDrag={this.dragItem}
itemDrop={this.dropItem}
onItemDoubleClick={this.doubleClickItem}
onItemContextMenu={this.contextMenuClickItem}
scrollRef={this.scrollComponent}
selectedItem={this.state.selectedItem}
onRowClick={this.handleRowClick}
onRowDoubleClick={this.handleRowDoubleClick}
onRowContextClick={this.handleScrollContextMenu}
groupsWithItemsDimensions={groupsWithItemsDimensions}
//props
groups={groups}
keys={keys}
resizeEdge={this.state.resizingEdge}
canChangeGroup={this.props.canChangeGroup}
canMove={this.props.canMove}
canResize={this.props.canResize}
canSelect={this.props.canSelect}
useResizeHandle={this.props.useResizeHandle}
dragSnap={this.props.dragSnap}
minResizeWidth={this.props.minResizeWidth}
moveResizeValidator={this.props.moveResizeValidator}
selected={this.props.selected}
rowRenderer={this.props.rowRenderer}
rowData={this.props.rowData}
clickTolerance={this.props.clickTolerance}
horizontalLineClassNamesForGroup={
this.props.horizontalLineClassNamesForGroup
}
/>
{hideHorizontalLines? null : <Columns
lineCount={_length(groups)}
minUnit={minUnit}
timeSteps={timeSteps}
verticalLineClassNamesForTime={this.props.verticalLineClassNamesForTime}
canvasTimeStart={canvasTimeStart}
canvasTimeEnd={canvasTimeEnd}
canvasWidth={canvasWidth}
/>}
</MarkerCanvas>
</ScrollElement>
{rightSidebarWidth > 0
? this.rightSidebar(height, groupHeights)
: null}
</div>
</div>
</HelpersContextProvider>
</TimelineHeadersProvider>
</TimelineMarkersProvider>
</TimelineStateProvider>
)
}
}