diff --git a/.eslintrc b/.eslintrc
index 4268f85bb..123d9b0c0 100644
--- a/.eslintrc
+++ b/.eslintrc
@@ -2,6 +2,8 @@ parser: babel-eslint
extends:
- jason/react
- prettier
+plugins:
+ - react-hooks
env:
es6: true
rules:
@@ -15,3 +17,5 @@ rules:
varsIgnorePattern: ^_,
argsIgnorePattern: ^_,
}]
+ react-hooks/rules-of-hooks: error
+ react-hooks/exhaustive-deps: warn
diff --git a/.size-snapshot.json b/.size-snapshot.json
index ed467b52f..b6e1a03e1 100644
--- a/.size-snapshot.json
+++ b/.size-snapshot.json
@@ -1,25 +1,25 @@
{
"./dist/react-big-calendar.js": {
- "bundled": 507436,
- "minified": 149016,
- "gzipped": 45496
+ "bundled": 509547,
+ "minified": 149932,
+ "gzipped": 45769
},
"./dist/react-big-calendar.min.js": {
- "bundled": 444328,
- "minified": 130089,
- "gzipped": 41174
+ "bundled": 446246,
+ "minified": 130919,
+ "gzipped": 41339
},
"dist/react-big-calendar.esm.js": {
- "bundled": 174497,
- "minified": 83179,
- "gzipped": 20739,
+ "bundled": 176376,
+ "minified": 84316,
+ "gzipped": 21023,
"treeshaked": {
"rollup": {
- "code": 60400,
- "import_statements": 1578
+ "code": 60196,
+ "import_statements": 1590
},
"webpack": {
- "code": 64923
+ "code": 64714
}
}
}
diff --git a/README.md b/README.md
index 18d03ade1..1059d5577 100644
--- a/README.md
+++ b/README.md
@@ -112,8 +112,9 @@ const MyCalendar = props => (
## Custom Styling
-Out of the box you can include the compiled css files and be up and running. But, sometimes, you may want to style
-Big Calendar to match your application styling. For this reason SASS files are included with Big Calendar.
+Out of the box, you can include the compiled CSS files and be up and running. But, sometimes, you may want to style
+Big Calendar to match your application styling. For this reason, SASS files are included with Big Calendar.
+
```
@import 'react-big-calendar/lib/sass/styles';
@@ -126,4 +127,4 @@ Big Calendar. Carefully test each change accordingly.
## Join us on Reactiflux Discord
-Join us on [Reactiflux Discord](https://discord.gg/uJsgpkC) community under the channel #react-big-calendar if you have any questions.
+Join us on [Reactiflux Discord](https://discord.gg/PPgj6tb) community under the channel #libraries if you have any questions.
diff --git a/examples/demos/dnd.js b/examples/demos/dnd.js
index 16bc2e8f2..ebe0ef1ee 100644
--- a/examples/demos/dnd.js
+++ b/examples/demos/dnd.js
@@ -12,16 +12,39 @@ class Dnd extends React.Component {
super(props)
this.state = {
events: events,
+ displayDragItemInCell: true,
}
this.moveEvent = this.moveEvent.bind(this)
this.newEvent = this.newEvent.bind(this)
}
- moveEvent({ event, start, end, isAllDay: droppedOnAllDaySlot }) {
+ handleDragStart = event => {
+ this.setState({ draggedEvent: event })
+ }
+
+ dragFromOutsideItem = () => {
+ return this.state.draggedEvent
+ }
+
+ onDropFromOutside = ({ start, end, allDay }) => {
+ const { draggedEvent } = this.state
+
+ const event = {
+ id: draggedEvent.id,
+ title: draggedEvent.title,
+ start,
+ end,
+ allDay: allDay,
+ }
+
+ this.setState({ draggedEvent: null })
+ this.moveEvent({ event, start, end })
+ }
+
+ moveEvent = ({ event, start, end, isAllDay: droppedOnAllDaySlot }) => {
const { events } = this.state
- const idx = events.indexOf(event)
let allDay = event.allDay
if (!event.allDay && droppedOnAllDaySlot) {
@@ -30,10 +53,11 @@ class Dnd extends React.Component {
allDay = false
}
- const updatedEvent = { ...event, start, end, allDay }
-
- const nextEvents = [...events]
- nextEvents.splice(idx, 1, updatedEvent)
+ const nextEvents = events.map(existingEvent => {
+ return existingEvent.id == event.id
+ ? { ...existingEvent, start, end }
+ : existingEvent
+ })
this.setState({
events: nextEvents,
@@ -86,6 +110,12 @@ class Dnd extends React.Component {
onDragStart={console.log}
defaultView={Views.MONTH}
defaultDate={new Date(2015, 3, 12)}
+ popup={true}
+ dragFromOutsideItem={
+ this.state.displayDragItemInCell ? this.dragFromOutsideItem : null
+ }
+ onDropFromOutside={this.onDropFromOutside}
+ handleDragStart={this.handleDragStart}
/>
)
}
diff --git a/package.json b/package.json
index 67c88d9e0..131c17ae8 100644
--- a/package.json
+++ b/package.json
@@ -8,7 +8,7 @@
},
"repository": {
"type": "git",
- "url": "git+https://github.com/intljusticemission/react-big-calendar.git"
+ "url": "git+https://github.com/jquense/react-big-calendar.git"
},
"license": "MIT",
"main": "lib/index.js",
@@ -95,6 +95,7 @@
"eslint-import-resolver-webpack": "^0.10.1",
"eslint-plugin-import": "^2.14.0",
"eslint-plugin-react": "^7.11.1",
+ "eslint-plugin-react-hooks": "^4.0.4",
"font-awesome": "^4.7.0",
"globalize": "^0.1.1",
"husky": "^0.14.3",
@@ -108,10 +109,10 @@
"postcss": "^7.0.16",
"postcss-cli": "^6.1.2",
"prettier": "^1.15.1",
- "react": "^16.6.1",
+ "react": "^16.13.1",
"react-bootstrap": "^0.32.4",
"react-docgen": "^3.0.0-rc.1",
- "react-dom": "^16.6.1",
+ "react-dom": "^16.13.1",
"react-tackle-box": "^2.1.0",
"rimraf": "^2.4.2",
"rollup": "^1.1.0",
@@ -134,8 +135,8 @@
"invariant": "^2.2.4",
"lodash": "^4.17.11",
"lodash-es": "^4.17.11",
- "memoize-one": "^4.0.3",
- "prop-types": "^15.6.2",
+ "memoize-one": "^5.1.1",
+ "prop-types": "^15.7.2",
"react-overlays": "^2.0.0-0",
"uncontrollable": "^7.0.0"
},
@@ -143,9 +144,9 @@
"babel-core": "7.0.0-bridge.0"
},
"bugs": {
- "url": "https://github.com/intljusticemission/react-big-calendar/issues"
+ "url": "https://github.com/jquense/react-big-calendar/issues"
},
"readme": "ERROR: No README data found!",
- "homepage": "https://github.com/intljusticemission/react-big-calendar#readme",
+ "homepage": "https://github.com/jquense/react-big-calendar#readme",
"_id": "react-big-calendar@0.22.1"
}
diff --git a/src/Agenda.js b/src/Agenda.js
index 46e5feeca..e8e5a9b8e 100644
--- a/src/Agenda.js
+++ b/src/Agenda.js
@@ -1,5 +1,5 @@
import PropTypes from 'prop-types'
-import React from 'react'
+import React, { useRef, useEffect } from 'react'
import addClass from 'dom-helpers/addClass'
import removeClass from 'dom-helpers/removeClass'
import getWidth from 'dom-helpers/width'
@@ -10,75 +10,28 @@ import { navigate } from './utils/constants'
import { inRange } from './utils/eventLevels'
import { isSelected } from './utils/selection'
-class Agenda extends React.Component {
- constructor(props) {
- super(props)
- this.headerRef = React.createRef()
- this.dateColRef = React.createRef()
- this.timeColRef = React.createRef()
- this.contentRef = React.createRef()
- this.tbodyRef = React.createRef()
- }
-
- componentDidMount() {
- this._adjustHeader()
- }
-
- componentDidUpdate() {
- this._adjustHeader()
- }
-
- render() {
- let { length, date, events, accessors, localizer } = this.props
- let { messages } = localizer
- let end = dates.add(date, length, 'day')
-
- let range = dates.range(date, end, 'day')
-
- events = events.filter(event => inRange(event, date, end, accessors))
-
- events.sort((a, b) => +accessors.start(a) - +accessors.start(b))
-
- return (
-
- {events.length !== 0 ? (
-
-
-
-
-
- {messages.date}
- |
-
- {messages.time}
- |
- {messages.event} |
-
-
-
-
-
-
- {range.map((day, idx) => this.renderDay(day, events, idx))}
-
-
-
-
- ) : (
-
{messages.noEventsInRange}
- )}
-
- )
- }
-
- renderDay = (day, events, dayKey) => {
- let {
- selected,
- getters,
- accessors,
- localizer,
- components: { event: Event, date: AgendaDate },
- } = this.props
+function Agenda({
+ selected,
+ getters,
+ accessors,
+ localizer,
+ components,
+ length,
+ date,
+ events,
+}) {
+ const headerRef = useRef(null)
+ const dateColRef = useRef(null)
+ const timeColRef = useRef(null)
+ const contentRef = useRef(null)
+ const tbodyRef = useRef(null)
+
+ useEffect(() => {
+ _adjustHeader()
+ })
+
+ const renderDay = (day, events, dayKey) => {
+ const { event: Event, date: AgendaDate } = components
events = events.filter(e =>
inRange(e, dates.startOf(day, 'day'), dates.endOf(day, 'day'), accessors)
@@ -117,9 +70,7 @@ class Agenda extends React.Component {
style={userProps.style}
>
{first}
-
- {this.timeRangeLabel(day, event)}
- |
+ {timeRangeLabel(day, event)} |
{Event ? : title}
|
@@ -128,9 +79,7 @@ class Agenda extends React.Component {
}, [])
}
- timeRangeLabel = (day, event) => {
- let { accessors, localizer, components } = this.props
-
+ const timeRangeLabel = (day, event) => {
let labelClass = '',
TimeComponent = components.time,
label = localizer.messages.allDay
@@ -164,27 +113,25 @@ class Agenda extends React.Component {
)
}
- _adjustHeader = () => {
- if (!this.tbodyRef.current) return
+ const _adjustHeader = () => {
+ if (!tbodyRef.current) return
- let header = this.headerRef.current
- let firstRow = this.tbodyRef.current.firstChild
+ let header = headerRef.current
+ let firstRow = tbodyRef.current.firstChild
if (!firstRow) return
let isOverflowing =
- this.contentRef.current.scrollHeight >
- this.contentRef.current.clientHeight
- let widths = this._widths || []
-
- this._widths = [
- getWidth(firstRow.children[0]),
- getWidth(firstRow.children[1]),
- ]
-
- if (widths[0] !== this._widths[0] || widths[1] !== this._widths[1]) {
- this.dateColRef.current.style.width = this._widths[0] + 'px'
- this.timeColRef.current.style.width = this._widths[1] + 'px'
+ contentRef.current.scrollHeight > contentRef.current.clientHeight
+
+ let _widths = []
+ let widths = _widths
+
+ _widths = [getWidth(firstRow.children[0]), getWidth(firstRow.children[1])]
+
+ if (widths[0] !== _widths[0] || widths[1] !== _widths[1]) {
+ dateColRef.current.style.width = _widths[0] + 'px'
+ timeColRef.current.style.width = _widths[1] + 'px'
}
if (isOverflowing) {
@@ -194,6 +141,46 @@ class Agenda extends React.Component {
removeClass(header, 'rbc-header-overflowing')
}
}
+
+ let { messages } = localizer
+ let end = dates.add(date, length, 'day')
+
+ let range = dates.range(date, end, 'day')
+
+ events = events.filter(event => inRange(event, date, end, accessors))
+
+ events.sort((a, b) => +accessors.start(a) - +accessors.start(b))
+
+ return (
+
+ {events.length !== 0 ? (
+
+
+
+
+
+ {messages.date}
+ |
+
+ {messages.time}
+ |
+ {messages.event} |
+
+
+
+
+
+
+ {range.map((day, idx) => renderDay(day, events, idx))}
+
+
+
+
+ ) : (
+
{messages.noEventsInRange}
+ )}
+
+ )
}
Agenda.propTypes = {
diff --git a/src/BackgroundCells.js b/src/BackgroundCells.js
index 31e44a22b..7b3d492d9 100644
--- a/src/BackgroundCells.js
+++ b/src/BackgroundCells.js
@@ -160,6 +160,7 @@ class BackgroundCells extends React.Component {
action,
bounds,
box,
+ resourceId: this.props.resourceId,
})
}
}
@@ -183,6 +184,7 @@ BackgroundCells.propTypes = {
range: PropTypes.arrayOf(PropTypes.instanceOf(Date)),
rtl: PropTypes.bool,
type: PropTypes.string,
+ resourceId: PropTypes.any,
}
export default BackgroundCells
diff --git a/src/Calendar.js b/src/Calendar.js
index 83480d991..884e8fa06 100644
--- a/src/Calendar.js
+++ b/src/Calendar.js
@@ -250,7 +250,7 @@ class Calendar extends React.Component {
/**
*
* ```js
- * (dates: Date[] | { start: Date; end: Date }, view?: 'month'|'week'|'work_week'|'day'|'agenda') => void
+ * (dates: Date[] | { start: Date; end: Date }, view: 'month'|'week'|'work_week'|'day'|'agenda'|undefined) => void
* ```
*
* Callback fired when the visible date range changes. Returns an Array of dates
@@ -312,6 +312,15 @@ class Calendar extends React.Component {
*/
onDoubleClickEvent: PropTypes.func,
+ /**
+ * Callback fired when a focused calendar event recieves a key press.
+ *
+ * ```js
+ * (event: Object, e: SyntheticEvent) => void
+ * ```
+ */
+ onKeyPressEvent: PropTypes.func,
+
/**
* Callback fired when dragging a selection in the Time views.
*
@@ -454,7 +463,7 @@ class Calendar extends React.Component {
longPressThreshold: PropTypes.number,
/**
- * Determines the selectable time increments in week and day views
+ * Determines the selectable time increments in week and day views, in minutes.
*/
step: PropTypes.number,
@@ -493,16 +502,16 @@ class Calendar extends React.Component {
* (date: Date, resourceId: (number|string)) => { className?: string, style?: Object }
* ```
*/
- slotPropGetter: PropTypes.func,
-
- /**
- * Optionally provide a function that returns an object of props to be applied
- * to the time-slot group node. Useful to dynamically change the sizing of time nodes.
- * ```js
- * () => { style?: Object }
- * ```
- */
- slotGroupPropGetter: PropTypes.func,
+ slotPropGetter: PropTypes.func,
+
+ /**
+ * Optionally provide a function that returns an object of props to be applied
+ * to the time-slot group node. Useful to dynamically change the sizing of time nodes.
+ * ```js
+ * () => { style?: Object }
+ * ```
+ */
+ slotGroupPropGetter: PropTypes.func,
/**
* Optionally provide a function that returns an object of className or style props
@@ -784,8 +793,8 @@ class Calendar extends React.Component {
resourceIdAccessor,
resourceTitleAccessor,
eventPropGetter,
- slotPropGetter,
- slotGroupPropGetter,
+ slotPropGetter,
+ slotGroupPropGetter,
dayPropGetter,
view,
views,
@@ -804,9 +813,9 @@ class Calendar extends React.Component {
eventProp: (...args) =>
(eventPropGetter && eventPropGetter(...args)) || {},
slotProp: (...args) =>
- (slotPropGetter && slotPropGetter(...args)) || {},
- slotGroupProp: (...args) =>
- (slotGroupPropGetter && slotGroupPropGetter(...args)) || {},
+ (slotPropGetter && slotPropGetter(...args)) || {},
+ slotGroupProp: (...args) =>
+ (slotGroupPropGetter && slotGroupPropGetter(...args)) || {},
dayProp: (...args) => (dayPropGetter && dayPropGetter(...args)) || {},
},
components: defaults(components[view] || {}, omit(components, names), {
@@ -930,6 +939,7 @@ class Calendar extends React.Component {
onDrillDown={this.handleDrillDown}
onSelectEvent={this.handleSelectEvent}
onDoubleClickEvent={this.handleDoubleClickEvent}
+ onKeyPressEvent={this.handleKeyPressEvent}
onSelectSlot={this.handleSelectSlot}
onShowMore={onShowMore}
/>
@@ -997,6 +1007,10 @@ class Calendar extends React.Component {
notify(this.props.onDoubleClickEvent, args)
}
+ handleKeyPressEvent = (...args) => {
+ notify(this.props.onKeyPressEvent, args)
+ }
+
handleSelectSlot = slotInfo => {
notify(this.props.onSelectSlot, slotInfo)
}
diff --git a/src/DateContentRow.js b/src/DateContentRow.js
index a0954c44c..931defeca 100644
--- a/src/DateContentRow.js
+++ b/src/DateContentRow.js
@@ -113,6 +113,7 @@ class DateContentRow extends React.Component {
onSelectStart,
onSelectEnd,
onDoubleClick,
+ onKeyPress,
resourceId,
longPressThreshold,
isAllDay,
@@ -133,6 +134,7 @@ class DateContentRow extends React.Component {
components,
onSelect,
onDoubleClick,
+ onKeyPress,
resourceId,
slotMetrics: metrics,
}
@@ -152,6 +154,7 @@ class DateContentRow extends React.Component {
onSelectSlot={this.handleSelectSlot}
components={components}
longPressThreshold={longPressThreshold}
+ resourceId={resourceId}
/>
@@ -199,6 +202,7 @@ DateContentRow.propTypes = {
onSelectEnd: PropTypes.func,
onSelectStart: PropTypes.func,
onDoubleClick: PropTypes.func,
+ onKeyPress: PropTypes.func,
dayPropGetter: PropTypes.func,
getNow: PropTypes.func.isRequired,
diff --git a/src/DayColumn.js b/src/DayColumn.js
index 8474a0eda..a3a2c04ed 100644
--- a/src/DayColumn.js
+++ b/src/DayColumn.js
@@ -99,6 +99,7 @@ class DayColumn extends React.Component {
if (current >= min && current <= max) {
const top = this.slotMetrics.getCurrentTimePosition(current)
+ this.intervalTriggered = true
this.setState({ timeIndicatorPosition: top })
} else {
this.clearTimeIndicatorInterval()
@@ -163,7 +164,7 @@ class DayColumn extends React.Component {
{localizer.format(selectDates, 'selectRangeFormat')}
)}
- {isNow && (
+ {isNow && this.intervalTriggered && (
this._select(event, e)}
onDoubleClick={e => this._doubleClick(event, e)}
+ onKeyPress={e => this._keyPress(event, e)}
/>
)
})
@@ -349,7 +351,7 @@ class DayColumn extends React.Component {
while (dates.lte(current, endDate)) {
slots.push(current)
- current = dates.add(current, this.props.step, 'minutes')
+ current = new Date(+current + this.props.step * 60 * 1000) // using Date ensures not to create an endless loop the day DST begins
}
notify(this.props.onSelectSlot, {
@@ -370,6 +372,10 @@ class DayColumn extends React.Component {
_doubleClick = (...args) => {
notify(this.props.onDoubleClickEvent, args)
}
+
+ _keyPress = (...args) => {
+ notify(this.props.onKeyPressEvent, args)
+ }
}
DayColumn.propTypes = {
@@ -401,6 +407,7 @@ DayColumn.propTypes = {
onSelectSlot: PropTypes.func.isRequired,
onSelectEvent: PropTypes.func.isRequired,
onDoubleClickEvent: PropTypes.func.isRequired,
+ onKeyPressEvent: PropTypes.func.isRequired,
className: PropTypes.string,
dragThroughEvents: PropTypes.bool,
diff --git a/src/EventCell.js b/src/EventCell.js
index ff59ed8b8..e83b6e46a 100644
--- a/src/EventCell.js
+++ b/src/EventCell.js
@@ -13,6 +13,7 @@ class EventCell extends React.Component {
isAllDay,
onSelect,
onDoubleClick,
+ onKeyPress,
localizer,
continuesPrior,
continuesAfter,
@@ -69,6 +70,7 @@ class EventCell extends React.Component {
})}
onClick={e => onSelect && onSelect(event, e)}
onDoubleClick={e => onDoubleClick && onDoubleClick(event, e)}
+ onKeyPress={e => onKeyPress && onKeyPress(event, e)}
>
{typeof children === 'function' ? children(content) : content}
@@ -94,6 +96,7 @@ EventCell.propTypes = {
onSelect: PropTypes.func,
onDoubleClick: PropTypes.func,
+ onKeyPress: PropTypes.func,
}
export default EventCell
diff --git a/src/EventRowMixin.js b/src/EventRowMixin.js
index 6c2efb81a..589e16a1d 100644
--- a/src/EventRowMixin.js
+++ b/src/EventRowMixin.js
@@ -18,6 +18,7 @@ export default {
onSelect: PropTypes.func,
onDoubleClick: PropTypes.func,
+ onKeyPress: PropTypes.func,
},
defaultProps: {
@@ -33,6 +34,7 @@ export default {
getters,
onSelect,
onDoubleClick,
+ onKeyPress,
localizer,
slotMetrics,
components,
@@ -50,6 +52,7 @@ export default {
components={components}
onSelect={onSelect}
onDoubleClick={onDoubleClick}
+ onKeyPress={onKeyPress}
continuesPrior={continuesPrior}
continuesAfter={continuesAfter}
slotStart={slotMetrics.first}
diff --git a/src/Month.js b/src/Month.js
index eb8cb769f..f358cd809 100644
--- a/src/Month.js
+++ b/src/Month.js
@@ -132,6 +132,7 @@ class MonthView extends React.Component {
onShowMore={this.handleShowMore}
onSelect={this.handleSelectEvent}
onDoubleClick={this.handleDoubleClickEvent}
+ onKeyPress={this.handleKeyPressEvent}
onSelectSlot={this.handleSelectSlot}
longPressThreshold={longPressThreshold}
rtl={this.props.rtl}
@@ -214,11 +215,14 @@ class MonthView extends React.Component {
components={components}
localizer={localizer}
position={overlay.position}
+ show={this.overlayDisplay}
events={overlay.events}
slotStart={overlay.date}
slotEnd={overlay.end}
onSelect={this.handleSelectEvent}
onDoubleClick={this.handleDoubleClickEvent}
+ onKeyPress={this.handleKeyPressEvent}
+ handleDragStart={this.props.handleDragStart}
/>
)}
@@ -255,6 +259,11 @@ class MonthView extends React.Component {
notify(this.props.onDoubleClickEvent, args)
}
+ handleKeyPressEvent = (...args) => {
+ this.clearSelection()
+ notify(this.props.onKeyPressEvent, args)
+ }
+
handleShowMore = (events, date, cell, slot, target) => {
const { popup, onDrillDown, onShowMore, getDrilldownView } = this.props
//cancel any pending selections so only the event click goes through.
@@ -273,6 +282,12 @@ class MonthView extends React.Component {
notify(onShowMore, [events, date, slot])
}
+ overlayDisplay = () => {
+ this.setState({
+ overlay: null,
+ })
+ }
+
selectDates(slotInfo) {
let slots = this._pendingSelection.slice()
@@ -323,11 +338,13 @@ MonthView.propTypes = {
onSelectSlot: PropTypes.func,
onSelectEvent: PropTypes.func,
onDoubleClickEvent: PropTypes.func,
+ onKeyPressEvent: PropTypes.func,
onShowMore: PropTypes.func,
onDrillDown: PropTypes.func,
getDrilldownView: PropTypes.func.isRequired,
popup: PropTypes.bool,
+ handleDragStart: PropTypes.func,
popupOffset: PropTypes.oneOfType([
PropTypes.number,
diff --git a/src/Popup.js b/src/Popup.js
index 43e8f590c..7421ea45b 100644
--- a/src/Popup.js
+++ b/src/Popup.js
@@ -38,6 +38,7 @@ class Popup extends React.Component {
components,
onSelect,
onDoubleClick,
+ onKeyPress,
slotStart,
slotEnd,
localizer,
@@ -73,11 +74,15 @@ class Popup extends React.Component {
accessors={accessors}
components={components}
onDoubleClick={onDoubleClick}
+ onKeyPress={onKeyPress}
continuesPrior={dates.lt(accessors.end(event), slotStart, 'day')}
continuesAfter={dates.gte(accessors.start(event), slotEnd, 'day')}
slotStart={slotStart}
slotEnd={slotEnd}
selected={isSelected(event, selected)}
+ draggable={true}
+ onDragStart={() => this.props.handleDragStart(event)}
+ onDragEnd={() => this.props.show()}
/>
))}
@@ -103,6 +108,9 @@ Popup.propTypes = {
localizer: PropTypes.object.isRequired,
onSelect: PropTypes.func,
onDoubleClick: PropTypes.func,
+ onKeyPress: PropTypes.func,
+ handleDragStart: PropTypes.func,
+ show: PropTypes.func,
slotStart: PropTypes.instanceOf(Date),
slotEnd: PropTypes.number,
popperRef: PropTypes.oneOfType([
diff --git a/src/TimeGrid.js b/src/TimeGrid.js
index 84a00436d..867baa554 100644
--- a/src/TimeGrid.js
+++ b/src/TimeGrid.js
@@ -101,6 +101,7 @@ export default class TimeGrid extends Component {
start: slots[0],
end: slots[slots.length - 1],
action: slotInfo.action,
+ resourceId: slotInfo.resourceId,
})
}
@@ -221,6 +222,7 @@ export default class TimeGrid extends Component {
onSelectSlot={this.handleSelectAllDaySlot}
onSelectEvent={this.handleSelectAlldayEvent}
onDoubleClickEvent={this.props.onDoubleClickEvent}
+ onKeyPressEvent={this.props.onKeyPressEvent}
onDrillDown={this.props.onDrillDown}
getDrilldownView={this.props.getDrilldownView}
/>
@@ -337,6 +339,7 @@ TimeGrid.propTypes = {
onSelectStart: PropTypes.func,
onSelectEvent: PropTypes.func,
onDoubleClickEvent: PropTypes.func,
+ onKeyPressEvent: PropTypes.func,
onDrillDown: PropTypes.func,
getDrilldownView: PropTypes.func.isRequired,
diff --git a/src/TimeGridEvent.js b/src/TimeGridEvent.js
index b0ccfbaa9..c50a06ab2 100644
--- a/src/TimeGridEvent.js
+++ b/src/TimeGridEvent.js
@@ -20,6 +20,7 @@ function TimeGridEvent(props) {
getters,
onClick,
onDoubleClick,
+ onKeyPress,
components: { event: Event, eventWrapper: EventWrapper },
} = props
let title = accessors.title(event)
@@ -44,6 +45,7 @@ function TimeGridEvent(props) {
@@ -180,6 +181,7 @@ class TimeGridHeader extends React.Component {
localizer={localizer}
onSelect={this.props.onSelectEvent}
onDoubleClick={this.props.onDoubleClickEvent}
+ onKeyPress={this.props.onKeyPressEvent}
onSelectSlot={this.props.onSelectSlot}
longPressThreshold={this.props.longPressThreshold}
/>
@@ -212,6 +214,7 @@ TimeGridHeader.propTypes = {
onSelectSlot: PropTypes.func,
onSelectEvent: PropTypes.func,
onDoubleClickEvent: PropTypes.func,
+ onKeyPressEvent: PropTypes.func,
onDrillDown: PropTypes.func,
getDrilldownView: PropTypes.func.isRequired,
scrollRef: PropTypes.any,
diff --git a/src/addons/dragAndDrop/EventContainerWrapper.js b/src/addons/dragAndDrop/EventContainerWrapper.js
index ec0cc985b..53eb96f76 100644
--- a/src/addons/dragAndDrop/EventContainerWrapper.js
+++ b/src/addons/dragAndDrop/EventContainerWrapper.js
@@ -133,6 +133,7 @@ class EventContainerWrapper extends React.Component {
_selectable = () => {
let node = findDOMNode(this)
+ let isBeingDragged = false
let selector = (this._selector = new Selection(() =>
node.closest('.rbc-time-view')
))
@@ -177,16 +178,22 @@ class EventContainerWrapper extends React.Component {
this.handleDropFromOutside(point, bounds)
})
- selector.on('selectStart', () => this.context.draggable.onStart())
+ selector.on('selectStart', () => {
+ isBeingDragged = true
+ this.context.draggable.onStart()
+ })
selector.on('select', point => {
const bounds = getBoundsForNode(node)
-
+ isBeingDragged = false
if (!this.state.event || !pointInColumn(bounds, point)) return
this.handleInteractionEnd()
})
- selector.on('click', () => this.context.draggable.onEnd(null))
+ selector.on('click', () => {
+ if (isBeingDragged) this.reset()
+ this.context.draggable.onEnd(null)
+ })
selector.on('reset', () => {
this.reset()
diff --git a/src/localizers/date-fns.js b/src/localizers/date-fns.js
index 7a45a97cc..e43e026b7 100644
--- a/src/localizers/date-fns.js
+++ b/src/localizers/date-fns.js
@@ -22,7 +22,7 @@ let weekRangeFormat = ({ start, end }, culture, local) =>
export let formats = {
dateFormat: 'dd',
- dayFormat: 'dd ddd',
+ dayFormat: 'dd eee',
weekdayFormat: 'cccc',
selectRangeFormat: timeRangeFormat,
@@ -33,11 +33,11 @@ export let formats = {
timeGutterFormat: 'p',
monthHeaderFormat: 'MMMM yyyy',
- dayHeaderFormat: 'dddd MMM dd',
+ dayHeaderFormat: 'cccc MMM dd',
dayRangeHeaderFormat: weekRangeFormat,
agendaHeaderFormat: dateRangeFormat,
- agendaDateFormat: 'ddd MMM dd',
+ agendaDateFormat: 'ccc MMM dd',
agendaTimeFormat: 'p',
agendaTimeRangeFormat: timeRangeFormat,
}
diff --git a/src/utils/DateSlotMetrics.js b/src/utils/DateSlotMetrics.js
index 269d56ee9..873680653 100644
--- a/src/utils/DateSlotMetrics.js
+++ b/src/utils/DateSlotMetrics.js
@@ -4,7 +4,8 @@ import { eventSegments, endOfRange, eventLevels } from './eventLevels'
let isSegmentInSlot = (seg, slot) => seg.left <= slot && seg.right >= slot
-const isEqual = (a, b) => a.range === b.range && a.events === b.events
+const isEqual = (a, b) =>
+ a[0].range === b[0].range && a[0].events === b[0].events
export function getSlotMetrics() {
return memoize(options => {
diff --git a/src/utils/DayEventLayout.js b/src/utils/DayEventLayout.js
index 4ee883772..c12fa5862 100644
--- a/src/utils/DayEventLayout.js
+++ b/src/utils/DayEventLayout.js
@@ -21,7 +21,7 @@ export function getStyledEvents({
dayLayoutAlgorithm, // one of DefaultAlgorithms keys
// or custom function
}) {
- let algorithm = null
+ let algorithm = dayLayoutAlgorithm
if (dayLayoutAlgorithm in DefaultAlgorithms)
algorithm = DefaultAlgorithms[dayLayoutAlgorithm]
diff --git a/src/utils/TimeSlots.js b/src/utils/TimeSlots.js
index ce9bc06b1..4132c10c3 100644
--- a/src/utils/TimeSlots.js
+++ b/src/utils/TimeSlots.js
@@ -131,7 +131,7 @@ export function getSlotMetrics({ min: start, max: end, step, timeslots }) {
const rangeStartMin = positionFromDate(rangeStart)
const rangeEndMin = positionFromDate(rangeEnd)
const top =
- rangeEndMin - rangeStartMin < step && !dates.eq(end, rangeEnd)
+ rangeEndMin > step * numSlots && !dates.eq(end, rangeEnd)
? ((rangeStartMin - step) / (step * numSlots)) * 100
: (rangeStartMin / (step * numSlots)) * 100
diff --git a/test/utils/TimeSlots.test.js b/test/utils/TimeSlots.test.js
index 9ec0d2971..770ec2dfc 100644
--- a/test/utils/TimeSlots.test.js
+++ b/test/utils/TimeSlots.test.js
@@ -2,8 +2,8 @@ import { getSlotMetrics } from '../../src/utils/TimeSlots'
import * as dates from '../../src/utils/dates'
describe('getSlotMetrics', () => {
- const min = dates.startOf(new Date(), 'day')
- const max = dates.endOf(new Date(), 'day')
+ const min = dates.startOf(new Date(2018, 0, 29, 0, 0, 0), 'day')
+ const max = dates.endOf(new Date(2018, 0, 29, 59, 59, 59), 'day')
const slotMetrics = getSlotMetrics({ min, max, step: 60, timeslots: 1 })
test('getSlotMetrics.closestSlotToPosition: always returns timeslot if valid percentage is given', () => {
expect(slotMetrics.closestSlotToPosition(0)).toBeDefined()
@@ -24,8 +24,8 @@ describe('getSlotMetrics', () => {
})
describe('getRange', () => {
- const min = dates.startOf(new Date(), 'day')
- const max = dates.endOf(new Date(), 'day')
+ const min = dates.startOf(new Date(2018, 0, 29, 0, 0, 0), 'day')
+ const max = dates.endOf(new Date(2018, 0, 29, 59, 59, 59), 'day')
const slotMetrics = getSlotMetrics({ min, max, step: 60, timeslots: 1 })
test('getRange: 15 minute start of day appointment stays within calendar', () => {
diff --git a/yarn.lock b/yarn.lock
index b0839071a..4ff68d9b8 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -1898,9 +1898,9 @@ acorn-walk@^6.0.1:
integrity sha512-OtUw6JUTgxA2QoqqmrmQ7F2NYqiBPi/L2jqHyFtllhOUvXYQXf0Z1CYUinIfyT4bTCGmrA7gX9FvHA81uzCoVw==
acorn@^5.5.3:
- version "5.7.3"
- resolved "https://registry.yarnpkg.com/acorn/-/acorn-5.7.3.tgz#67aa231bf8812974b85235a96771eb6bd07ea279"
- integrity sha512-T/zvzYRfbVojPWahDsE5evJdHb3oJoQfFbsrKM7w5Zcs++Tr257tia3BmMP8XYVjp1S9RZXQMh7gao96BlqZOw==
+ version "5.7.4"
+ resolved "https://registry.yarnpkg.com/acorn/-/acorn-5.7.4.tgz#3e8d8a9947d0599a1796d10225d7432f4a4acf5e"
+ integrity sha512-1D++VG7BhrtvQpNbBzovKNc1FLGGEE/oGe7b9xJm/RFHMBeUaUGpluV9RLjZa47YFdPcDAenEYuq9pQPcMdLJg==
acorn@^6.0.1, acorn@^6.0.5, acorn@^6.0.7, acorn@^6.1.1:
version "6.1.1"
@@ -3606,7 +3606,7 @@ commander@2.17.x:
resolved "https://registry.yarnpkg.com/commander/-/commander-2.17.1.tgz#bd77ab7de6de94205ceacc72f1716d29f20a77bf"
integrity sha512-wPMUt6FnH2yzG95SA6mzjQOEKUU3aLaDEmzs1ti+1E9h+CsrZghRlqEM/EJ4KscsQVG8uNN4uVreUeT8+drlgg==
-commander@^2.14.1, commander@^2.19.0, commander@^2.8.1, commander@^2.9.0, commander@~2.20.0:
+commander@^2.14.1, commander@^2.19.0, commander@^2.8.1, commander@^2.9.0:
version "2.20.0"
resolved "https://registry.yarnpkg.com/commander/-/commander-2.20.0.tgz#d58bb2b5c1ee8f87b0d340027e9e94e222c5a422"
integrity sha512-7j2y+40w61zy6YC2iRNpUe/NwhNyoXrYpHMrSunaMG64nRnaf96zO/KMQR4OyN/UnE5KLyEBnKHd4aG3rskjpQ==
@@ -5065,6 +5065,11 @@ eslint-plugin-import@^2.14.0:
read-pkg-up "^2.0.0"
resolve "^1.10.0"
+eslint-plugin-react-hooks@^4.0.4:
+ version "4.0.4"
+ resolved "https://registry.yarnpkg.com/eslint-plugin-react-hooks/-/eslint-plugin-react-hooks-4.0.4.tgz#aed33b4254a41b045818cacb047b81e6df27fa58"
+ integrity sha512-equAdEIsUETLFNCmmCkiCGq6rkSK5MoJhXFPFYeUebcjKgBmWWcgVOqZyQC8Bv1BwVCnTq9tBxgJFgAJTWoJtA==
+
eslint-plugin-react@^7.11.1:
version "7.13.0"
resolved "https://registry.yarnpkg.com/eslint-plugin-react/-/eslint-plugin-react-7.13.0.tgz#bc13fd7101de67996ea51b33873cd9dc2b7e5758"
@@ -6210,13 +6215,14 @@ handle-thing@^2.0.0:
integrity sha512-d4sze1JNC454Wdo2fkuyzCr6aHcbL6PGGuFAz0Li/NcOm1tCHGnWDRmJP85dh9IhQErTc2svWFEX5xHIOo//kQ==
handlebars@^4.0.3, handlebars@^4.1.0:
- version "4.1.2"
- resolved "https://registry.yarnpkg.com/handlebars/-/handlebars-4.1.2.tgz#b6b37c1ced0306b221e094fc7aca3ec23b131b67"
- integrity sha512-nvfrjqvt9xQ8Z/w0ijewdD/vvWDTOweBUm96NTr66Wfvo1mJenBLwcYmPs3TIBP5ruzYGD7Hx/DaM9RmhroGPw==
+ version "4.7.6"
+ resolved "https://registry.yarnpkg.com/handlebars/-/handlebars-4.7.6.tgz#d4c05c1baf90e9945f77aa68a7a219aa4a7df74e"
+ integrity sha512-1f2BACcBfiwAfStCKZNrUCgqNZkGsAT7UM3kkYtXuLo0KnaVfjKOyf7PRzB6++aK9STyT1Pd2ZCPe3EGOXleXA==
dependencies:
+ minimist "^1.2.5"
neo-async "^2.6.0"
- optimist "^0.6.1"
source-map "^0.6.1"
+ wordwrap "^1.0.0"
optionalDependencies:
uglify-js "^3.1.4"
@@ -8531,10 +8537,10 @@ mem@^4.0.0:
mimic-fn "^2.0.0"
p-is-promise "^2.0.0"
-memoize-one@^4.0.3:
- version "4.1.0"
- resolved "https://registry.yarnpkg.com/memoize-one/-/memoize-one-4.1.0.tgz#a2387c58c03fff27ca390c31b764a79addf3f906"
- integrity sha512-2GApq0yI/b22J2j9rhbrAlsHb0Qcz+7yWxeLG8h+95sl1XPUgeLimQSOdur4Vw7cUhrBHwaUZxWFZueojqNRzA==
+memoize-one@^5.1.1:
+ version "5.1.1"
+ resolved "https://registry.yarnpkg.com/memoize-one/-/memoize-one-5.1.1.tgz#047b6e3199b508eaec03504de71229b8eb1d75c0"
+ integrity sha512-HKeeBpWvqiVJD57ZUAsJNm71eHTykffzcLZVYWiVfQeI1rJtuEaS7hQiEpWfVVk18donPwJEcFKIkCmPJNOhHA==
memoizerific@^1.11.3:
version "1.11.3"
@@ -8776,15 +8782,10 @@ minimist@0.0.8:
resolved "https://registry.yarnpkg.com/minimist/-/minimist-0.0.8.tgz#857fcabfc3397d2625b8228262e86aa7a011b05d"
integrity sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0=
-minimist@^1.1.1, minimist@^1.1.3, minimist@^1.2.0:
- version "1.2.0"
- resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.0.tgz#a35008b20f41383eec1fb914f4cd5df79a264284"
- integrity sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=
-
-minimist@~0.0.1:
- version "0.0.10"
- resolved "https://registry.yarnpkg.com/minimist/-/minimist-0.0.10.tgz#de3f98543dbf96082be48ad1a0c7cda836301dcf"
- integrity sha1-3j+YVD2/lggr5IrRoMfNqDYwHc8=
+minimist@^1.1.1, minimist@^1.1.3, minimist@^1.2.0, minimist@^1.2.5:
+ version "1.2.5"
+ resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.5.tgz#67d66014b66a6a8aaa0c083c5fd58df4e4e97602"
+ integrity sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==
minipass@^2.2.1, minipass@^2.3.4:
version "2.3.5"
@@ -8944,9 +8945,9 @@ negotiator@0.6.2:
integrity sha512-hZXc7K2e+PgeI1eDBe/10Ard4ekbfrrqG8Ep+8Jmf4JID2bNg7NvCPOZN+kfF574pFQI7mum2AUqDidoKqcTOw==
neo-async@^2.5.0, neo-async@^2.6.0:
- version "2.6.0"
- resolved "https://registry.yarnpkg.com/neo-async/-/neo-async-2.6.0.tgz#b9d15e4d71c6762908654b5183ed38b753340835"
- integrity sha512-MFh0d/Wa7vkKO3Y3LlacqAEeHK0mckVqzDieUKTT+KGxi+zIpeVsFxymkIiRpbpDziHc290Xr9A1O4Om7otoRA==
+ version "2.6.2"
+ resolved "https://registry.yarnpkg.com/neo-async/-/neo-async-2.6.2.tgz#b4aafb93e3aeb2d8174ca53cf163ab7d7308305f"
+ integrity sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==
nested-error-stacks@^2.0.0, nested-error-stacks@^2.1.0:
version "2.1.0"
@@ -9380,14 +9381,6 @@ opn@^5.4.0, opn@^5.5.0:
dependencies:
is-wsl "^1.1.0"
-optimist@^0.6.1:
- version "0.6.1"
- resolved "https://registry.yarnpkg.com/optimist/-/optimist-0.6.1.tgz#da3ea74686fa21a19a111c326e90eb15a0196686"
- integrity sha1-2j6nRob6IaGaERwybpDrFaAZZoY=
- dependencies:
- minimist "~0.0.1"
- wordwrap "~0.0.2"
-
optimize-css-assets-webpack-plugin@^5.0.1:
version "5.0.1"
resolved "https://registry.yarnpkg.com/optimize-css-assets-webpack-plugin/-/optimize-css-assets-webpack-plugin-5.0.1.tgz#9eb500711d35165b45e7fd60ba2df40cb3eb9159"
@@ -10691,7 +10684,17 @@ react-docgen@^3.0.0, react-docgen@^3.0.0-rc.1:
node-dir "^0.1.10"
recast "^0.16.0"
-react-dom@^16.6.1, react-dom@^16.8.1:
+react-dom@^16.13.1:
+ version "16.13.1"
+ resolved "https://registry.yarnpkg.com/react-dom/-/react-dom-16.13.1.tgz#c1bd37331a0486c078ee54c4740720993b2e0e7f"
+ integrity sha512-81PIMmVLnCNLO/fFOQxdQkvEq/+Hfpv24XNJfpyZhTRfO0QcmQIF/PgCa1zCOj2w1hrn12MFLyaJ/G0+Mxtfag==
+ dependencies:
+ loose-envify "^1.1.0"
+ object-assign "^4.1.1"
+ prop-types "^15.6.2"
+ scheduler "^0.19.1"
+
+react-dom@^16.8.1:
version "16.8.6"
resolved "https://registry.yarnpkg.com/react-dom/-/react-dom-16.8.6.tgz#71d6303f631e8b0097f56165ef608f051ff6e10f"
integrity sha512-1nL7PIq9LTL3fthPqwkvr2zY7phIPjYrT0jp4HjyEQrEROnw4dG41VVwi/wfoCneoleqrNX7iAD+pXebJZwrwA==
@@ -10874,7 +10877,16 @@ react-transition-group@^2.0.0, react-transition-group@^2.2.0:
prop-types "^15.6.2"
react-lifecycles-compat "^3.0.4"
-react@^16.6.1, react@^16.8.1:
+react@^16.13.1:
+ version "16.13.1"
+ resolved "https://registry.yarnpkg.com/react/-/react-16.13.1.tgz#2e818822f1a9743122c063d6410d85c1e3afe48e"
+ integrity sha512-YMZQQq32xHLX0bz5Mnibv1/LHb3Sqzngu7xstSM+vrkE5Kzr9xE0yMByK5kMoTK30YVJE61WfbxIFFvfeDKT1w==
+ dependencies:
+ loose-envify "^1.1.0"
+ object-assign "^4.1.1"
+ prop-types "^15.6.2"
+
+react@^16.8.1:
version "16.8.6"
resolved "https://registry.yarnpkg.com/react/-/react-16.8.6.tgz#ad6c3a9614fd3a4e9ef51117f54d888da01f2bbe"
integrity sha512-pC0uMkhLaHm11ZSJULfOBqV4tIZkx87ZLvbbQYunNixAAvjnC+snJCg0XQXn9VIsttVsbZP/H/ewzgsd5fxKXw==
@@ -11654,6 +11666,14 @@ scheduler@^0.13.6:
loose-envify "^1.1.0"
object-assign "^4.1.1"
+scheduler@^0.19.1:
+ version "0.19.1"
+ resolved "https://registry.yarnpkg.com/scheduler/-/scheduler-0.19.1.tgz#4f3e2ed2c1a7d65681f4c854fa8c5a1ccb40f196"
+ integrity sha512-n/zwRWRYSUj0/3g/otKDRPMh6qv2SYMWNq85IEa8iZyAv8od9zDYpGSnpBEjNgcMNq6Scbu5KfIPxNF72R/2EA==
+ dependencies:
+ loose-envify "^1.1.0"
+ object-assign "^4.1.1"
+
schema-utils@^0.4.5:
version "0.4.7"
resolved "https://registry.yarnpkg.com/schema-utils/-/schema-utils-0.4.7.tgz#ba74f597d2be2ea880131746ee17d0a093c68187"
@@ -12844,12 +12864,9 @@ uglify-js@3.4.x:
source-map "~0.6.1"
uglify-js@^3.1.4:
- version "3.5.11"
- resolved "https://registry.yarnpkg.com/uglify-js/-/uglify-js-3.5.11.tgz#833442c0aa29b3a7d34344c7c63adaa3f3504f6a"
- integrity sha512-izPJg8RsSyqxbdnqX36ExpbH3K7tDBsAU/VfNv89VkMFy3z39zFjunQGsSHOlGlyIfGLGprGeosgQno3bo2/Kg==
- dependencies:
- commander "~2.20.0"
- source-map "~0.6.1"
+ version "3.10.4"
+ resolved "https://registry.yarnpkg.com/uglify-js/-/uglify-js-3.10.4.tgz#dd680f5687bc0d7a93b14a3482d16db6eba2bfbb"
+ integrity sha512-kBFT3U4Dcj4/pJ52vfjCSfyLyvG9VYYuGYPmrPvAxRw/i7xHiT4VvCev+uiEMcEEiu6UNB6KgWmGtSUYIWScbw==
uncontrollable@^5.0.0:
version "5.1.0"
@@ -13446,12 +13463,7 @@ widest-line@^2.0.0:
dependencies:
string-width "^2.1.1"
-wordwrap@~0.0.2:
- version "0.0.3"
- resolved "https://registry.yarnpkg.com/wordwrap/-/wordwrap-0.0.3.tgz#a3d5da6cd5c0bc0008d37234bbaf1bed63059107"
- integrity sha1-o9XabNXAvAAI03I0u68b7WMFkQc=
-
-wordwrap@~1.0.0:
+wordwrap@^1.0.0, wordwrap@~1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/wordwrap/-/wordwrap-1.0.0.tgz#27584810891456a4171c8d0226441ade90cbcaeb"
integrity sha1-J1hIEIkUVqQXHI0CJkQa3pDLyus=