# react-calendar-timeline - https://github.com/namespace-ee/react-calendar-timeline ## custom-items ~~~javascript import React, { Component } from 'react' import moment from 'moment' import Timeline from 'react-calendar-timeline' // import containerResizeDetector from 'react-calendar-timeline/lib/resize-detector/container' import generateFakeData from '../generate-fake-data' var minTime = moment() .add(-6, 'months') .valueOf() var maxTime = moment() .add(6, 'months') .valueOf() var keys = { groupIdKey: 'id', groupTitleKey: 'title', groupRightTitleKey: 'rightTitle', itemIdKey: 'id', itemTitleKey: 'title', itemDivTitleKey: 'title', itemGroupKey: 'group', itemTimeStartKey: 'start', itemTimeEndKey: 'end' } export default class App extends Component { constructor(props) { super(props) const { groups, items } = generateFakeData() const defaultTimeStart = moment() .startOf('day') .toDate() const defaultTimeEnd = moment() .startOf('day') .add(1, 'day') .toDate() this.state = { groups, items, defaultTimeStart, defaultTimeEnd } } handleCanvasClick = (groupId, time, event) => { console.log('Canvas clicked', groupId, moment(time).format()) } handleCanvasContextMenu = (group, time, e) => { console.log('Canvas context menu', group, moment(time).format()) } handleItemClick = (itemId, _, time) => { console.log('Clicked: ' + itemId, moment(time).format()) } handleItemSelect = (itemId, _, time) => { console.log('Selected: ' + itemId, moment(time).format()) } handleItemDoubleClick = (itemId, _, time) => { console.log('Double Click: ' + itemId, moment(time).format()) } handleItemContextMenu = (itemId, _, time) => { console.log('Context Menu: ' + itemId, moment(time).format()) } handleItemMove = (itemId, dragTime, newGroupOrder) => { const { items, groups } = this.state const group = groups[newGroupOrder] this.setState({ items: items.map( item => item.id === itemId ? Object.assign({}, item, { start: dragTime, end: dragTime + (item.end - item.start), group: group.id }) : item ) }) console.log('Moved', itemId, dragTime, newGroupOrder) } handleItemResize = (itemId, time, edge) => { const { items } = this.state this.setState({ items: items.map( item => item.id === itemId ? Object.assign({}, item, { start: edge === 'left' ? time : item.start, end: edge === 'left' ? item.end : time }) : item ) }) console.log('Resized', itemId, time, edge) } // this limits the timeline to -6 months ... +6 months handleTimeChange = (visibleTimeStart, visibleTimeEnd, updateScrollCanvas) => { if (visibleTimeStart < minTime && visibleTimeEnd > maxTime) { updateScrollCanvas(minTime, maxTime) } else if (visibleTimeStart < minTime) { updateScrollCanvas(minTime, minTime + (visibleTimeEnd - visibleTimeStart)) } else if (visibleTimeEnd > maxTime) { updateScrollCanvas(maxTime - (visibleTimeEnd - visibleTimeStart), maxTime) } else { updateScrollCanvas(visibleTimeStart, visibleTimeEnd) } } moveResizeValidator = (action, item, time, resizeEdge) => { if (time < new Date().getTime()) { var newTime = Math.ceil(new Date().getTime() / (15 * 60 * 1000)) * (15 * 60 * 1000) return newTime } return time } itemRenderer = ({ item, timelineContext, itemContext, getItemProps, getResizeProps, }) => { const { left: leftResizeProps, right: rightResizeProps } = getResizeProps() const backgroundColor = itemContext.selected ? itemContext.dragging ? 'red' : item.selectedBgColor : item.bgColor; const borderColor = itemContext.resizing ? 'red' : item.color; return ( <div {...getItemProps({ style: { backgroundColor, color: item.color, borderColor, borderStyle: 'solid', borderWidth: 1, borderRadius: 4, borderLeftWidth: itemContext.selected ? 3 : 1, borderRightWidth: itemContext.selected ? 3 : 1, } }) } > {itemContext.useResizeHandle ? ( <div {...leftResizeProps} /> ) : null} <div style={{ height: itemContext.dimensions.height, overflow: 'hidden', paddingLeft:3, textOverflow: 'ellipsis', whiteSpace: 'nowrap' }} > {itemContext.title} </div> {itemContext.useResizeHandle ? ( <div {...rightResizeProps} /> ) : null} </div> ) } // groupRenderer = ({ group }) => { // return ( // <div className='custom-group'> // {group.title} // </div> // ) // } render() { const { groups, items, defaultTimeStart, defaultTimeEnd } = this.state console.log("render") return ( <Timeline groups={groups} items={items} keys={keys} sidebarWidth={150} sidebarContent={<div>Above The Left</div>} // rightSidebarWidth={150} // rightSidebarContent={<div>Above The Right</div>} canMove canResize="right" canSelect itemsSorted itemTouchSendsClick={false} stackItems itemHeightRatio={0.75} lineHeight={40} showCursorLine // resizeDetector={containerResizeDetector} defaultTimeStart={defaultTimeStart} defaultTimeEnd={defaultTimeEnd} itemRenderer={this.itemRenderer} // groupRenderer={this.groupRenderer} onCanvasClick={this.handleCanvasClick} onCanvasContextMenu={this.handleCanvasContextMenu} onItemClick={this.handleItemClick} onItemSelect={this.handleItemSelect} onItemContextMenu={this.handleItemContextMenu} onItemMove={this.handleItemMove} onItemResize={this.handleItemResize} onItemDoubleClick={this.handleItemDoubleClick} onTimeChange={this.handleTimeChange} moveResizeValidator={this.moveResizeValidator} /> ) } } ~~~ # controlled selected ~~~javascript /* eslint-disable no-console */ import React, { Component } from 'react' import moment from 'moment' import Timeline, { TimelineMarkers, TimelineHeaders, TodayMarker, CustomMarker, CursorMarker, CustomHeader, SidebarHeader, DateHeader } from 'react-calendar-timeline' import generateFakeData from '../generate-fake-data' var minTime = moment() .add(-6, 'months') .valueOf() var maxTime = moment() .add(6, 'months') .valueOf() var keys = { groupIdKey: 'id', groupTitleKey: 'title', groupRightTitleKey: 'rightTitle', itemIdKey: 'id', itemTitleKey: 'title', itemDivTitleKey: 'title', itemGroupKey: 'group', itemTimeStartKey: 'start', itemTimeEndKey: 'end' } export default class App extends Component { constructor(props) { super(props) const { groups, items } = generateFakeData() console.log("groups: ", groups) console.log("items: ", items) const defaultTimeStart = moment() .startOf('day') .toDate() const defaultTimeEnd = moment() .startOf('day') .add(1, 'day') .toDate() this.state = { groups, items, defaultTimeStart, defaultTimeEnd, selected: undefined, } } handleCanvasClick = (groupId, time) => { console.log('Canvas clicked', groupId, moment(time).format()) } handleCanvasDoubleClick = (groupId, time) => { console.log('Canvas double clicked', groupId, moment(time).format()) } handleCanvasContextMenu = (group, time) => { console.log('Canvas context menu', group, moment(time).format()) } handleItemClick = (itemId, _, time) => { console.log('Clicked: ' + itemId, moment(time).format()) } handleItemSelect = (itemId, _, time) => { this.setState({ selected: [itemId] }) console.log('Selected: ' + itemId, moment(time).format()) } handleItemDeselect = () => { this.setState({selected: undefined}) } handleItemDoubleClick = (itemId, _, time) => { console.log('Double Click: ' + itemId, moment(time).format()) } handleItemContextMenu = (itemId, _, time) => { console.log('Context Menu: ' + itemId, moment(time).format()) } handleItemMove = (itemId, dragTime, newGroupOrder) => { const { items, groups } = this.state const group = groups[newGroupOrder] this.setState({ items: items.map( item => item.id === itemId ? Object.assign({}, item, { start: dragTime, end: dragTime + (item.end - item.start), group: group.id }) : item ) }) console.log('Moved', itemId, dragTime, newGroupOrder) } handleItemResize = (itemId, time, edge) => { const { items } = this.state this.setState({ items: items.map( item => item.id === itemId ? Object.assign({}, item, { start: edge === 'left' ? time : item.start, end: edge === 'left' ? item.end : time }) : item ) }) console.log('Resized', itemId, time, edge) } // this limits the timeline to -6 months ... +6 months handleTimeChange = (visibleTimeStart, visibleTimeEnd, updateScrollCanvas) => { if (visibleTimeStart < minTime && visibleTimeEnd > maxTime) { updateScrollCanvas(minTime, maxTime) } else if (visibleTimeStart < minTime) { updateScrollCanvas(minTime, minTime + (visibleTimeEnd - visibleTimeStart)) } else if (visibleTimeEnd > maxTime) { updateScrollCanvas(maxTime - (visibleTimeEnd - visibleTimeStart), maxTime) } else { updateScrollCanvas(visibleTimeStart, visibleTimeEnd) } } moveResizeValidator = (action, item, time) => { if (time < new Date().getTime()) { var newTime = Math.ceil(new Date().getTime() / (15 * 60 * 1000)) * (15 * 60 * 1000) return newTime } return time } render() { const { groups, items, defaultTimeStart, defaultTimeEnd } = this.state return ( <Timeline groups={groups} items={items} keys={keys} sidebarWidth={150} sidebarContent={<div>Above The Left</div>} canMove canResize="right" canSelect itemsSorted itemTouchSendsClick={false} stackItems itemHeightRatio={0.75} defaultTimeStart={defaultTimeStart} defaultTimeEnd={defaultTimeEnd} onCanvasClick={this.handleCanvasClick} onCanvasDoubleClick={this.handleCanvasDoubleClick} onCanvasContextMenu={this.handleCanvasContextMenu} onItemClick={this.handleItemClick} onItemSelect={this.handleItemSelect} onItemContextMenu={this.handleItemContextMenu} onItemMove={this.handleItemMove} onItemResize={this.handleItemResize} onItemDoubleClick={this.handleItemDoubleClick} onTimeChange={this.handleTimeChange} moveResizeValidator={this.moveResizeValidator} selected={this.state.selected} onItemDeselect={this.handleItemDeselect} > <TimelineMarkers> <TodayMarker /> <CustomMarker date={ moment() .startOf('day') .valueOf() + 1000 * 60 * 60 * 2 } /> <CustomMarker date={moment() .add(3, 'day') .valueOf()} > {({ styles }) => { const newStyles = { ...styles } return <div style={newStyles} /> }} </CustomMarker> <CursorMarker /> </TimelineMarkers> </Timeline> ) } } ~~~