diff --git a/src/Calendar.js b/src/Calendar.js index cbb6e9c09..ff8504d96 100644 --- a/src/Calendar.js +++ b/src/Calendar.js @@ -80,6 +80,11 @@ class Calendar extends React.Component { */ events: PropTypes.arrayOf(PropTypes.object), + /** + * An array of resource objects that map events to a specific resource + */ + resources: PropTypes.arrayOf(PropTypes.object), + /** * Callback fired when the `date` value changes. * diff --git a/src/DayColumn.js b/src/DayColumn.js index c86f2a9e4..3014ad6e9 100644 --- a/src/DayColumn.js +++ b/src/DayColumn.js @@ -57,6 +57,7 @@ class DaySlot extends React.Component { dayWrapperComponent: elementType, eventComponent: elementType, eventWrapperComponent: elementType.isRequired, + resource: React.PropTypes.string }; static defaultProps = { dragThroughEvents: true }; @@ -299,6 +300,7 @@ class DaySlot extends React.Component { slots, start: startDate, end: endDate, + resourceId: this.props.resource, action }) }; diff --git a/src/TimeColumn.js b/src/TimeColumn.js index e4ef95aeb..22f409056 100644 --- a/src/TimeColumn.js +++ b/src/TimeColumn.js @@ -19,6 +19,7 @@ export default class TimeColumn extends Component { timeGutterFormat: PropTypes.string, type: PropTypes.string.isRequired, className: PropTypes.string, + resource: PropTypes.string, dayWrapperComponent: elementType, } @@ -31,7 +32,7 @@ export default class TimeColumn extends Component { dayWrapperComponent: BackgroundWrapper, } - renderTimeSliceGroup(key, isNow, date) { + renderTimeSliceGroup(key, isNow, date, resource) { const { dayWrapperComponent, timeslots, showLabels, step, timeGutterFormat, culture } = this.props; return ( @@ -42,6 +43,7 @@ export default class TimeColumn extends Component { step={step} culture={culture} timeslots={timeslots} + resource={resource} showLabels={showLabels} timeGutterFormat={timeGutterFormat} dayWrapperComponent={dayWrapperComponent} @@ -50,7 +52,7 @@ export default class TimeColumn extends Component { } render() { - const { className, children, style, now, min, max, step, timeslots } = this.props; + const { className, children, style, now, min, max, step, timeslots, resource } = this.props; const totalMin = dates.diff(min, max, 'minutes') const numGroups = Math.ceil(totalMin / (step * timeslots)) const renderedSlots = [] @@ -69,7 +71,7 @@ export default class TimeColumn extends Component { ) next = dates.add(date, groupLengthInMinutes, 'minutes'); - renderedSlots.push(this.renderTimeSliceGroup(i, isNow, date)) + renderedSlots.push(this.renderTimeSliceGroup(i, isNow, date, resource)) date = next } diff --git a/src/TimeGrid.js b/src/TimeGrid.js index 34c75cc4d..1309b0f1d 100644 --- a/src/TimeGrid.js +++ b/src/TimeGrid.js @@ -26,6 +26,7 @@ export default class TimeGrid extends Component { static propTypes = { events: PropTypes.array.isRequired, + resources: PropTypes.array, step: PropTypes.number, range: PropTypes.arrayOf( @@ -141,6 +142,7 @@ export default class TimeGrid extends Component { , width , startAccessor , endAccessor + , resources , allDayAccessor } = this.props; width = width || this.state.gutterWidth; @@ -174,10 +176,14 @@ export default class TimeGrid extends Component { let gutterRef = ref => this._gutters[1] = ref && findDOMNode(ref); + let eventsRendered = resources ? + this.renderEventsWithResource(range, rangeEvents, this.props.now, resources) : + this.renderEvents(range, rangeEvents, this.props.now); + return (
- {this.renderHeader(range, allDayEvents, width)} + {this.renderHeader(range, allDayEvents, width, resources)}
@@ -189,22 +195,55 @@ export default class TimeGrid extends Component { ref={gutterRef} className='rbc-time-gutter' /> - - {this.renderEvents(range, rangeEvents, this.props.now)} + {eventsRendered}
); } + renderEventsWithResource(range, events, today, resources) { + let { min, max, endAccessor, startAccessor, components } = this.props; + + return range.map((date, idx) => { + let daysEvents = events.filter( + event => dates.inRange(date, + get(event, startAccessor), + get(event, endAccessor), 'day') + ) + + return resources.map((resource, id) => { + + let eventsToDisplay = daysEvents.filter( + event => event.resourceId === resource.id + ) + + return ( + + ) + }) + }) + } - renderEvents(range, events, today){ + renderEvents(range, events, today, id){ let { min, max, endAccessor, startAccessor, components } = this.props; return range.map((date, idx) => { let daysEvents = events.filter( event => dates.inRange(date, get(event, startAccessor), - get(event, endAccessor), 'day') + get(event, endAccessor), 'day') && event.resourceId === id ) return ( @@ -225,7 +264,7 @@ export default class TimeGrid extends Component { }) } - renderHeader(range, events, width) { + renderHeader(range, events, width, resources) { let { messages, rtl, selectable, components, now } = this.props; let { isOverflowing } = this.state || {}; @@ -233,6 +272,10 @@ export default class TimeGrid extends Component { if (isOverflowing) style[rtl ? 'marginLeft' : 'marginRight'] = scrollbarSize() + 'px'; + let headerRendered = resources ? + this.renderHeaderResources(range, resources) : + message(messages).allDay; + return (
{ this.renderHeaderCells(range) }
+ { resources &&
+
+ { headerRendered } +
}
this._gutters[0] = ref} @@ -282,6 +332,27 @@ export default class TimeGrid extends Component { ) } + renderHeaderResources(range, resources) { + return range.map((date, i) => { + return resources.map((resource, j) => { + return ( +
+ + {resource.title} + +
+ ) + }) + }) + } + renderHeaderCells(range){ let { dayFormat, culture, components, getDrilldownView } = this.props; let HeaderComponent = components.header || Header diff --git a/src/TimeSlot.js b/src/TimeSlot.js index bb03b9c4f..db4353379 100644 --- a/src/TimeSlot.js +++ b/src/TimeSlot.js @@ -11,7 +11,8 @@ export default class TimeSlot extends Component { isNow: PropTypes.bool, showLabel: PropTypes.bool, content: PropTypes.string, - culture: PropTypes.string + culture: PropTypes.string, + resource: PropTypes.string } static defaultProps = { @@ -21,11 +22,11 @@ export default class TimeSlot extends Component { } render() { - const { value } = this.props; + const { value, resource } = this.props; const Wrapper = this.props.dayWrapperComponent; return ( - +
.rbc-row.rbc-row-resource { + border-bottom: 1px solid @cell-border; + } + .rbc-gutter-cell { flex: none; } diff --git a/stories/Calendar.js b/stories/Calendar.js index 3e55f88eb..dfc712a57 100644 --- a/stories/Calendar.js +++ b/stories/Calendar.js @@ -1,12 +1,17 @@ import { storiesOf, action } from '@kadira/storybook'; import moment from 'moment'; import React from 'react'; +import HTML5Backend from 'react-dnd-html5-backend'; +import { DragDropContext } from 'react-dnd'; import Calendar from '../src'; import momentLocalizer from '../src/localizers/moment.js' import '../src/less/styles.less' +import '../src/addons/dragAndDrop/styles.less' import demoEvents from '../examples/events'; import createEvents from './createEvents'; +import resources from './resourceEvents'; +import withDragAndDrop from '../src/addons/dragAndDrop'; // Setup the localizer by providing the moment (or globalize) Object // to the correct localizer. @@ -38,6 +43,25 @@ const events = [{ allDay: true }] +const DragAndDropCalendar = withDragAndDrop(Calendar) + +const DragCalendar = () => { + return ( + { action(event) }} + onSelectEvent={action('event selected')} + onSelectSlot={action('slot selected')} + defaultDate={new Date(2015, 3, 1)} + /> + ) +} + +const DragableCalendar = DragDropContext(HTML5Backend)(DragCalendar) + storiesOf('module.Calendar.week', module) .add('demo', () => { return ( @@ -78,6 +102,13 @@ storiesOf('module.Calendar.week', module)
) }) + .add('resource', () => { + return ( +
+ +
+ ) + }) .add('selectable', () => { return ( diff --git a/stories/resourceEvents.js b/stories/resourceEvents.js new file mode 100644 index 000000000..d99ab5975 --- /dev/null +++ b/stories/resourceEvents.js @@ -0,0 +1,43 @@ +export default { + events: [ { + 'title': 'Rencontre', + 'resourceId': 'a', + 'start': new Date(2015, 3, 2, 5, 30, 0, 0), + 'end': new Date(2015, 3, 2, 10, 30, 0, 0), + }, { + 'title': 'Another Meeting', + 'resourceId': 'b', + 'start': new Date(2015, 3, 1, 2, 30, 0, 0), + 'end': new Date(2015, 3, 1, 4, 30, 0, 0), + }, + { + 'title': 'A', + 'resourceId': 'a', + 'start': new Date(2015, 3, 4, 5, 30, 0, 0), + 'end': new Date(2015, 3, 4, 10, 30, 0, 0), + }, + { + 'title': 'B', + 'resourceId': 'b', + 'start': new Date(2015, 3, 4, 5, 30, 0, 0), + 'end': new Date(2015, 3, 4, 10, 30, 0, 0), + }, + { + 'title': 'C', + 'resourceId': 'c', + 'start': new Date(2015, 3, 4, 5, 30, 0, 0), + 'end': new Date(2015, 3, 4, 10, 30, 0, 0), + } + ], + + list: [{ + id: 'a', + title: 'Room A' + }, { + id: 'b', + title: 'Room B' + }, { + id: 'c', + title: 'Room C' + }] +} \ No newline at end of file