From 9564cb0524560a645d0ad0664f98d91f29e7598b Mon Sep 17 00:00:00 2001 From: Jason Quense Date: Tue, 2 May 2017 08:45:27 -0400 Subject: [PATCH] Switch to classes --- src/Agenda.js | 35 +- src/Calendar.js | 1061 ++++++++++++++++++++--------------------- src/Day.js | 9 +- src/DayColumn.js | 50 +- src/EventEndingRow.js | 119 ++--- src/EventRow.js | 59 +-- src/EventRowMixin.js | 22 +- src/EventWrapper.js | 4 +- src/Month.js | 293 ++++++------ src/Week.js | 14 +- 10 files changed, 815 insertions(+), 851 deletions(-) diff --git a/src/Agenda.js b/src/Agenda.js index ff11ded9a..462612534 100644 --- a/src/Agenda.js +++ b/src/Agenda.js @@ -13,9 +13,8 @@ import { accessor, dateFormat, dateRangeFormat } from './utils/propTypes'; import { inRange } from './utils/eventLevels'; -let Agenda = React.createClass({ - - propTypes: { +class Agenda extends React.Component { + static propTypes = { events: PropTypes.array, date: PropTypes.instanceOf(Date), length: PropTypes.number.isRequired, @@ -34,21 +33,19 @@ let Agenda = React.createClass({ date: PropTypes.string, time: PropTypes.string, }) - }, + }; - getDefaultProps() { - return { - length: 30 - }; - }, + static defaultProps = { + length: 30 + }; componentDidMount() { this._adjustHeader() - }, + } componentDidUpdate() { this._adjustHeader() - }, + } render() { let { length, date, events, startAccessor } = this.props; @@ -89,9 +86,9 @@ let Agenda = React.createClass({ ); - }, + } - renderDay(day, events, dayKey){ + renderDay = (day, events, dayKey) => { let { culture, components , titleAccessor, agendaDateFormat } = this.props; @@ -130,9 +127,9 @@ let Agenda = React.createClass({ ) }, []) - }, + }; - timeRangeLabel(day, event){ + timeRangeLabel = (day, event) => { let { endAccessor, startAccessor, allDayAccessor , culture, messages, components } = this.props; @@ -167,9 +164,9 @@ let Agenda = React.createClass({ } ) - }, + }; - _adjustHeader() { + _adjustHeader = () => { let header = this.refs.header; let firstRow = this.refs.tbody.firstChild @@ -196,8 +193,8 @@ let Agenda = React.createClass({ else { classes.removeClass(header, 'rbc-header-overflowing') } - } -}); + }; +} Agenda.navigate = (date, action)=>{ switch (action){ diff --git a/src/Calendar.js b/src/Calendar.js index f84133726..02ee881de 100644 --- a/src/Calendar.js +++ b/src/Calendar.js @@ -52,538 +52,535 @@ let now = new Date(); * on `Apr 8th 12:01:00 am` will. If you want _inclusive_ ranges consider providing a * function `endAccessor` that returns the end date + 1 day for those events that end at midnight. */ -let Calendar = React.createClass({ - - propTypes: { - - /** - * Props passed to main calendar `
`. - */ - elementProps: PropTypes.object, - - /** - * The current date value of the calendar. Determines the visible view range - * - * @controllable onNavigate - */ - date: PropTypes.instanceOf(Date), - - /** - * The current view of the calendar. - * - * @default 'month' - * @controllable onView - */ - view: PropTypes.string, - - /** - * An array of event objects to display on the calendar - */ - events: PropTypes.arrayOf(PropTypes.object), - - /** - * Callback fired when the `date` value changes. - * - * @controllable date - */ - onNavigate: PropTypes.func, - - /** - * Callback fired when the `view` value changes. - * - * @controllable date - */ - onView: PropTypes.func, - - /** - * A callback fired when a date selection is made. Only fires when `selectable` is `true`. - * - * ```js - * function( - * slotInfo: object { - * start: Date, - * end: Date, - * slots: array, - * action: "select" | "click" - * } - * ) - * ``` - */ - onSelectSlot: PropTypes.func, - - /** - * Callback fired when a calendar event is selected. - * - * ```js - * function(event: object, e: SyntheticEvent) - * ``` - */ - onSelectEvent: PropTypes.func, - - /** - * Callback fired when dragging a selection in the Time views. - * - * Returning `false` from the handler will prevent a selection. - * - * ```js - * function ({ start: Date, end: Date }) : boolean - * ``` - */ - onSelecting: PropTypes.func, - - /** - * An array of built-in view names to allow the calendar to display. - * - * @type Calendar.views - * @default ['month', 'week', 'day', 'agenda'] - */ - views: componentViews, - - /** - * The string name of the destination view for drill-down actions, such - * as clicking a date header, or the truncated events links. If - * `getDrilldownView` is also specified it will be used instead. - * - * Set to `null` to disable drill-down actions. - * - * ```js - * - * ``` - */ - drilldownView: PropTypes.string, - - /** - * Functionally equivalent to `drilldownView`, but accepts a function - * that can return a view name. It's useful for customizing the drill-down - * actions depending on the target date and triggering view. - * - * Return `null` to disable drill-down actions. - * - * ```js - * - * if (currentViewName === 'month' && configuredViewNames.includes('week')) - * return 'week' - * - * return null; - * }} - * /> - * ``` - */ - getDrilldownView: PropTypes.func, - - /** - * Determines whether the toolbar is displayed - */ - toolbar: PropTypes.bool, - - /** - * Show truncated events in an overlay when you click the "+_x_ more" link. - */ - popup: PropTypes.bool, - - /** - * Distance in pixels, from the edges of the viewport, the "show more" overlay should be positioned. - * - * ```js - * - * - * ``` - */ - popupOffset: PropTypes.oneOfType([ - PropTypes.number, - PropTypes.shape({ x: PropTypes.number, y: PropTypes.number }) - ]), - /** - * Allows mouse selection of ranges of dates/times. - * - * The 'ignoreEvents' option prevents selection code from running when a - * drag begins over an event. Useful when you want custom event click or drag - * logic - */ - selectable: PropTypes.oneOf([true, false, 'ignoreEvents']), - - /** - * Determines the selectable time increments in week and day views - */ - step: PropTypes.number, - - /** - * The number of slots per "section" in the time grid views. Adjust with `step` - * to change the default of 1 hour long groups, with 30 minute slots. - */ - timeslots: PropTypes.number, - - /** - *Switch the calendar to a `right-to-left` read direction. - */ - rtl: PropTypes.bool, - - /** - * Optionally provide a function that returns an object of className or style props - * to be applied to the the event node. - * - * ```js - * function( - * event: object, - * start: date, - * end: date, - * isSelected: bool - * ) -> { className: string?, style: object? } - * ``` - */ - eventPropGetter: PropTypes.func, - - /** - * Accessor for the event title, used to display event information. Should - * resolve to a `renderable` value. - * - * @type {(func|string)} - */ - titleAccessor: accessor, - - /** - * Determines whether the event should be considered an "all day" event and ignore time. - * Must resolve to a `boolean` value. - * - * @type {(func|string)} - */ - allDayAccessor: accessor, - - /** - * The start date/time of the event. Must resolve to a JavaScript `Date` object. - * - * @type {(func|string)} - */ - startAccessor: accessor, - - /** - * The end date/time of the event. Must resolve to a JavaScript `Date` object. - * - * @type {(func|string)} - */ - endAccessor: accessor, - - /** - * Constrains the minimum _time_ of the Day and Week views. - */ - min: PropTypes.instanceOf(Date), - - /** - * Constrains the maximum _time_ of the Day and Week views. - */ - max: PropTypes.instanceOf(Date), - - /** - * Determines how far down the scroll pane is initially scrolled down. - */ - scrollToTime: PropTypes.instanceOf(Date), - - /** - * Specify a specific culture code for the Calendar. - * - * **Note: it's generally better to handle this globally via your i18n library.** - */ - culture: PropTypes.string, - - /** - * Localizer specific formats, tell the Calendar how to format and display dates. - * - * `format` types are dependent on the configured localizer; both Moment and Globalize - * accept strings of tokens according to their own specification, such as: `'DD mm yyyy'`. - * - * ```jsx - * let formats = { - * dateFormat: 'dd', - * - * dayFormat: (date, culture, localizer) => - * localizer.format(date, 'DDD', culture), - * - * dayRangeHeaderFormat: ({ start, end }, culture, local) => - * local.format(start, { date: 'short' }, culture) + ' — ' + - * local.format(end, { date: 'short' }, culture) - * } - * - * - * ``` - * - * All localizers accept a function of - * the form `(date: Date, culture: ?string, localizer: Localizer) -> string` - */ - formats: PropTypes.shape({ - /** - * Format for the day of the month heading in the Month view. - * e.g. "01", "02", "03", etc - */ - dateFormat, - - /** - * A day of the week format for Week and Day headings, - * e.g. "Wed 01/04" - * - */ - dayFormat: dateFormat, - - /** - * Week day name format for the Month week day headings, - * e.g: "Sun", "Mon", "Tue", etc - * - */ - weekdayFormat: dateFormat, - - /** - * The timestamp cell formats in Week and Time views, e.g. "4:00 AM" - */ - timeGutterFormat: dateFormat, - - /** - * Toolbar header format for the Month view, e.g "2015 April" - * - */ - monthHeaderFormat: dateFormat, - - /** - * Toolbar header format for the Week views, e.g. "Mar 29 - Apr 04" - */ - dayRangeHeaderFormat: dateRangeFormat, - - /** - * Toolbar header format for the Day view, e.g. "Wednesday Apr 01" - */ - dayHeaderFormat: dateFormat, - - /** - * Toolbar header format for the Agenda view, e.g. "4/1/2015 — 5/1/2015" - */ - agendaHeaderFormat: dateFormat, - - /** - * A time range format for selecting time slots, e.g "8:00am — 2:00pm" - */ - selectRangeFormat: dateRangeFormat, - - agendaDateFormat: dateFormat, - agendaTimeFormat: dateFormat, - agendaTimeRangeFormat: dateRangeFormat, - - /** - * Time range displayed on events. - */ - eventTimeRangeFormat: dateRangeFormat - }), - - /** - * Customize how different sections of the calendar render by providing custom Components. - * In particular the `Event` component can be specified for the entire calendar, or you can - * provide an individual component for each view type. - * - * ```jsx - * let components = { - * event: MyEvent, // used by each view (Month, Day, Week) - * toolbar: MyToolbar, - * agenda: { - * event: MyAgendaEvent // with the agenda view use a different component to render events - * } - * } - * - * ``` - */ - components: PropTypes.shape({ - event: elementType, - eventWrapper: elementType, - dayWrapper: elementType, - dateCellWrapper: elementType, - - toolbar: elementType, - - agenda: PropTypes.shape({ - date: elementType, - time: elementType, - event: elementType - }), - - day: PropTypes.shape({ - header: elementType, - event: elementType - }), - week: PropTypes.shape({ - header: elementType, - event: elementType - }), - month: PropTypes.shape({ - header: elementType, - event: elementType - }) - }), - - /** - * String messages used throughout the component, override to provide localizations - */ - messages: PropTypes.shape({ - allDay: PropTypes.node, - previous: PropTypes.node, - next: PropTypes.node, - today: PropTypes.node, - month: PropTypes.node, - week: PropTypes.node, - day: PropTypes.node, - agenda: PropTypes.node, - showMore: PropTypes.func - }) - }, - - getDefaultProps() { - return { - elementProps: {}, - popup: false, - toolbar: true, - view: views.MONTH, - views: [views.MONTH, views.WEEK, views.DAY, views.AGENDA], - date: now, - step: 30, - - drilldownView: views.DAY, - - titleAccessor: 'title', - allDayAccessor: 'allDay', - startAccessor: 'start', - endAccessor: 'end' - }; - }, - - getViews() { - const views = this.props.views; - - if (Array.isArray(views)) { - return transform(views, (obj, name) => obj[name] = VIEWS[name], {}); - } - - if (typeof views === 'object') { - return mapValues(views, (value, key) => { - if (value === true) { - return VIEWS[key]; - } - - return value; - }); - } - - return VIEWS; - }, - - getView() { - const views = this.getViews(); - - return views[this.props.view]; - }, - - getDrilldownView(date) { - const { view, drilldownView, getDrilldownView } = this.props - - if (!getDrilldownView) return drilldownView - - return getDrilldownView(date, view, Object.keys(this.getViews())); - }, - - render() { - let { - view, toolbar, events - , culture - , components = {} - , formats = {} - , style - , className - , elementProps - , date: current - , ...props } = this.props; - - formats = defaultFormats(formats) - - let View = this.getView(); - let names = viewNames(this.props.views) - - let viewComponents = defaults( - components[view] || {}, - omit(components, names), - { - eventWrapper: EventWrapper, - dayWrapper: BackgroundWrapper, - dateCellWrapper: BackgroundWrapper - } - ) - - let ToolbarToRender = components.toolbar || Toolbar - - return ( -
- {toolbar && - - } - -
- ); - }, - - handleNavigate(action, newDate) { - let { view, date, onNavigate } = this.props; - let ViewComponent = this.getView(); - - date = moveDate(action, newDate || date, ViewComponent) - - onNavigate(date, view) - }, - - handleViewChange(view) { - if (view !== this.props.view && isValidView(view, this.props)) - this.props.onView(view) - }, - - handleSelectEvent(...args) { - notify(this.props.onSelectEvent, args) - }, - - handleSelectSlot(slotInfo) { - notify(this.props.onSelectSlot, slotInfo) - }, - - handleDrillDown(date, view) { - if (view) - this.handleViewChange(view) - - this.handleNavigate(navigate.DATE, date) - } -}); +class Calendar extends React.Component { + static propTypes = { + + /** + * Props passed to main calendar `
`. + */ + elementProps: PropTypes.object, + + /** + * The current date value of the calendar. Determines the visible view range + * + * @controllable onNavigate + */ + date: PropTypes.instanceOf(Date), + + /** + * The current view of the calendar. + * + * @default 'month' + * @controllable onView + */ + view: PropTypes.string, + + /** + * An array of event objects to display on the calendar + */ + events: PropTypes.arrayOf(PropTypes.object), + + /** + * Callback fired when the `date` value changes. + * + * @controllable date + */ + onNavigate: PropTypes.func, + + /** + * Callback fired when the `view` value changes. + * + * @controllable date + */ + onView: PropTypes.func, + + /** + * A callback fired when a date selection is made. Only fires when `selectable` is `true`. + * + * ```js + * function( + * slotInfo: object { + * start: Date, + * end: Date, + * slots: array, + * action: "select" | "click" + * } + * ) + * ``` + */ + onSelectSlot: PropTypes.func, + + /** + * Callback fired when a calendar event is selected. + * + * ```js + * function(event: object, e: SyntheticEvent) + * ``` + */ + onSelectEvent: PropTypes.func, + + /** + * Callback fired when dragging a selection in the Time views. + * + * Returning `false` from the handler will prevent a selection. + * + * ```js + * function ({ start: Date, end: Date }) : boolean + * ``` + */ + onSelecting: PropTypes.func, + + /** + * An array of built-in view names to allow the calendar to display. + * + * @type Calendar.views + * @default ['month', 'week', 'day', 'agenda'] + */ + views: componentViews, + + /** + * The string name of the destination view for drill-down actions, such + * as clicking a date header, or the truncated events links. If + * `getDrilldownView` is also specified it will be used instead. + * + * Set to `null` to disable drill-down actions. + * + * ```js + * + * ``` + */ + drilldownView: PropTypes.string, + + /** + * Functionally equivalent to `drilldownView`, but accepts a function + * that can return a view name. It's useful for customizing the drill-down + * actions depending on the target date and triggering view. + * + * Return `null` to disable drill-down actions. + * + * ```js + * + * if (currentViewName === 'month' && configuredViewNames.includes('week')) + * return 'week' + * + * return null; + * }} + * /> + * ``` + */ + getDrilldownView: PropTypes.func, + + /** + * Determines whether the toolbar is displayed + */ + toolbar: PropTypes.bool, + + /** + * Show truncated events in an overlay when you click the "+_x_ more" link. + */ + popup: PropTypes.bool, + + /** + * Distance in pixels, from the edges of the viewport, the "show more" overlay should be positioned. + * + * ```js + * + * + * ``` + */ + popupOffset: PropTypes.oneOfType([ + PropTypes.number, + PropTypes.shape({ x: PropTypes.number, y: PropTypes.number }) + ]), + /** + * Allows mouse selection of ranges of dates/times. + * + * The 'ignoreEvents' option prevents selection code from running when a + * drag begins over an event. Useful when you want custom event click or drag + * logic + */ + selectable: PropTypes.oneOf([true, false, 'ignoreEvents']), + + /** + * Determines the selectable time increments in week and day views + */ + step: PropTypes.number, + + /** + * The number of slots per "section" in the time grid views. Adjust with `step` + * to change the default of 1 hour long groups, with 30 minute slots. + */ + timeslots: PropTypes.number, + + /** + *Switch the calendar to a `right-to-left` read direction. + */ + rtl: PropTypes.bool, + + /** + * Optionally provide a function that returns an object of className or style props + * to be applied to the the event node. + * + * ```js + * function( + * event: object, + * start: date, + * end: date, + * isSelected: bool + * ) -> { className: string?, style: object? } + * ``` + */ + eventPropGetter: PropTypes.func, + + /** + * Accessor for the event title, used to display event information. Should + * resolve to a `renderable` value. + * + * @type {(func|string)} + */ + titleAccessor: accessor, + + /** + * Determines whether the event should be considered an "all day" event and ignore time. + * Must resolve to a `boolean` value. + * + * @type {(func|string)} + */ + allDayAccessor: accessor, + + /** + * The start date/time of the event. Must resolve to a JavaScript `Date` object. + * + * @type {(func|string)} + */ + startAccessor: accessor, + + /** + * The end date/time of the event. Must resolve to a JavaScript `Date` object. + * + * @type {(func|string)} + */ + endAccessor: accessor, + + /** + * Constrains the minimum _time_ of the Day and Week views. + */ + min: PropTypes.instanceOf(Date), + + /** + * Constrains the maximum _time_ of the Day and Week views. + */ + max: PropTypes.instanceOf(Date), + + /** + * Determines how far down the scroll pane is initially scrolled down. + */ + scrollToTime: PropTypes.instanceOf(Date), + + /** + * Specify a specific culture code for the Calendar. + * + * **Note: it's generally better to handle this globally via your i18n library.** + */ + culture: PropTypes.string, + + /** + * Localizer specific formats, tell the Calendar how to format and display dates. + * + * `format` types are dependent on the configured localizer; both Moment and Globalize + * accept strings of tokens according to their own specification, such as: `'DD mm yyyy'`. + * + * ```jsx + * let formats = { + * dateFormat: 'dd', + * + * dayFormat: (date, culture, localizer) => + * localizer.format(date, 'DDD', culture), + * + * dayRangeHeaderFormat: ({ start, end }, culture, local) => + * local.format(start, { date: 'short' }, culture) + ' — ' + + * local.format(end, { date: 'short' }, culture) + * } + * + * + * ``` + * + * All localizers accept a function of + * the form `(date: Date, culture: ?string, localizer: Localizer) -> string` + */ + formats: PropTypes.shape({ + /** + * Format for the day of the month heading in the Month view. + * e.g. "01", "02", "03", etc + */ + dateFormat, + + /** + * A day of the week format for Week and Day headings, + * e.g. "Wed 01/04" + * + */ + dayFormat: dateFormat, + + /** + * Week day name format for the Month week day headings, + * e.g: "Sun", "Mon", "Tue", etc + * + */ + weekdayFormat: dateFormat, + + /** + * The timestamp cell formats in Week and Time views, e.g. "4:00 AM" + */ + timeGutterFormat: dateFormat, + + /** + * Toolbar header format for the Month view, e.g "2015 April" + * + */ + monthHeaderFormat: dateFormat, + + /** + * Toolbar header format for the Week views, e.g. "Mar 29 - Apr 04" + */ + dayRangeHeaderFormat: dateRangeFormat, + + /** + * Toolbar header format for the Day view, e.g. "Wednesday Apr 01" + */ + dayHeaderFormat: dateFormat, + + /** + * Toolbar header format for the Agenda view, e.g. "4/1/2015 — 5/1/2015" + */ + agendaHeaderFormat: dateFormat, + + /** + * A time range format for selecting time slots, e.g "8:00am — 2:00pm" + */ + selectRangeFormat: dateRangeFormat, + + agendaDateFormat: dateFormat, + agendaTimeFormat: dateFormat, + agendaTimeRangeFormat: dateRangeFormat, + + /** + * Time range displayed on events. + */ + eventTimeRangeFormat: dateRangeFormat + }), + + /** + * Customize how different sections of the calendar render by providing custom Components. + * In particular the `Event` component can be specified for the entire calendar, or you can + * provide an individual component for each view type. + * + * ```jsx + * let components = { + * event: MyEvent, // used by each view (Month, Day, Week) + * toolbar: MyToolbar, + * agenda: { + * event: MyAgendaEvent // with the agenda view use a different component to render events + * } + * } + * + * ``` + */ + components: PropTypes.shape({ + event: elementType, + eventWrapper: elementType, + dayWrapper: elementType, + dateCellWrapper: elementType, + + toolbar: elementType, + + agenda: PropTypes.shape({ + date: elementType, + time: elementType, + event: elementType + }), + + day: PropTypes.shape({ + header: elementType, + event: elementType + }), + week: PropTypes.shape({ + header: elementType, + event: elementType + }), + month: PropTypes.shape({ + header: elementType, + event: elementType + }) + }), + + /** + * String messages used throughout the component, override to provide localizations + */ + messages: PropTypes.shape({ + allDay: PropTypes.node, + previous: PropTypes.node, + next: PropTypes.node, + today: PropTypes.node, + month: PropTypes.node, + week: PropTypes.node, + day: PropTypes.node, + agenda: PropTypes.node, + showMore: PropTypes.func + }) + }; + + static defaultProps = { + elementProps: {}, + popup: false, + toolbar: true, + view: views.MONTH, + views: [views.MONTH, views.WEEK, views.DAY, views.AGENDA], + date: now, + step: 30, + + drilldownView: views.DAY, + + titleAccessor: 'title', + allDayAccessor: 'allDay', + startAccessor: 'start', + endAccessor: 'end' + }; + + getViews = () => { + const views = this.props.views; + + if (Array.isArray(views)) { + return transform(views, (obj, name) => obj[name] = VIEWS[name], {}); + } + + if (typeof views === 'object') { + return mapValues(views, (value, key) => { + if (value === true) { + return VIEWS[key]; + } + + return value; + }); + } + + return VIEWS; + }; + + getView = () => { + const views = this.getViews(); + + return views[this.props.view]; + }; + + getDrilldownView = (date) => { + const { view, drilldownView, getDrilldownView } = this.props + + if (!getDrilldownView) return drilldownView + + return getDrilldownView(date, view, Object.keys(this.getViews())); + }; + + render() { + let { + view, toolbar, events + , culture + , components = {} + , formats = {} + , style + , className + , elementProps + , date: current + , ...props } = this.props; + + formats = defaultFormats(formats) + + let View = this.getView(); + let names = viewNames(this.props.views) + + let viewComponents = defaults( + components[view] || {}, + omit(components, names), + { + eventWrapper: EventWrapper, + dayWrapper: BackgroundWrapper, + dateCellWrapper: BackgroundWrapper + } + ) + + let ToolbarToRender = components.toolbar || Toolbar + + return ( +
+ {toolbar && + + } + +
+ ); + } + + handleNavigate = (action, newDate) => { + let { view, date, onNavigate } = this.props; + let ViewComponent = this.getView(); + + date = moveDate(action, newDate || date, ViewComponent) + + onNavigate(date, view) + }; + + handleViewChange = (view) => { + if (view !== this.props.view && isValidView(view, this.props)) + this.props.onView(view) + }; + + handleSelectEvent = (...args) => { + notify(this.props.onSelectEvent, args) + }; + + handleSelectSlot = (slotInfo) => { + notify(this.props.onSelectSlot, slotInfo) + }; + + handleDrillDown = (date, view) => { + if (view) + this.handleViewChange(view) + + this.handleNavigate(navigate.DATE, date) + }; +} export default uncontrollable(Calendar, { view: 'onView', diff --git a/src/Day.js b/src/Day.js index 56cd41f62..9a3bc1800 100644 --- a/src/Day.js +++ b/src/Day.js @@ -4,11 +4,10 @@ import dates from './utils/dates'; import TimeGrid from './TimeGrid'; import { navigate } from './utils/constants'; -let Day = React.createClass({ - - propTypes: { +class Day extends React.Component { + static propTypes = { date: PropTypes.instanceOf(Date).isRequired, - }, + }; render() { let { date, ...props } = this.props; @@ -18,7 +17,7 @@ let Day = React.createClass({ ); } -}); +} Day.navigate = (date, action)=>{ switch (action){ diff --git a/src/DayColumn.js b/src/DayColumn.js index 1ed6e9277..c86f2a9e4 100644 --- a/src/DayColumn.js +++ b/src/DayColumn.js @@ -25,9 +25,8 @@ function startsAfter(date, max) { return dates.gt(dates.merge(max, date), max, 'minutes') } -let DaySlot = React.createClass({ - - propTypes: { +class DaySlot extends React.Component { + static propTypes = { events: PropTypes.array.isRequired, step: PropTypes.number.isRequired, min: PropTypes.instanceOf(Date).isRequired, @@ -58,31 +57,26 @@ let DaySlot = React.createClass({ dayWrapperComponent: elementType, eventComponent: elementType, eventWrapperComponent: elementType.isRequired, - }, - - getDefaultProps() { - return { dragThroughEvents: true } - }, + }; - getInitialState() { - return { selecting: false }; - }, + static defaultProps = { dragThroughEvents: true }; + state = { selecting: false }; componentDidMount() { this.props.selectable && this._selectable() - }, + } componentWillUnmount() { this._teardownSelectable(); - }, + } componentWillReceiveProps(nextProps) { if (nextProps.selectable && !this.props.selectable) this._selectable(); if (!nextProps.selectable && this.props.selectable) this._teardownSelectable(); - }, + } render() { const { @@ -128,9 +122,9 @@ let DaySlot = React.createClass({ } ); - }, + } - renderEvents() { + renderEvents = () => { let { events , min @@ -194,9 +188,9 @@ let DaySlot = React.createClass({ ) }) - }, + }; - _slotStyle(startSlot, endSlot) { + _slotStyle = (startSlot, endSlot) => { let top = ((startSlot / this._totalMin) * 100); let bottom = ((endSlot / this._totalMin) * 100); @@ -204,9 +198,9 @@ let DaySlot = React.createClass({ top: top + '%', height: bottom - top + '%' } - }, + }; - _selectable(){ + _selectable = () => { let node = findDOMNode(this); let selector = this._selector = new Selection(()=> findDOMNode(this)) @@ -284,15 +278,15 @@ let DaySlot = React.createClass({ this.setState({ selecting: false }) } }) - }, + }; - _teardownSelectable() { + _teardownSelectable = () => { if (!this._selector) return this._selector.teardown(); this._selector = null; - }, + }; - _selectSlot({ startDate, endDate, action }) { + _selectSlot = ({ startDate, endDate, action }) => { let current = startDate , slots = []; @@ -307,12 +301,12 @@ let DaySlot = React.createClass({ end: endDate, action }) - }, + }; - _select(...args) { + _select = (...args) => { notify(this.props.onSelectEvent, args) - } -}); + }; +} function minToDate(min, date){ diff --git a/src/EventEndingRow.js b/src/EventEndingRow.js index 3526bebbc..667a3fbe5 100644 --- a/src/EventEndingRow.js +++ b/src/EventEndingRow.js @@ -1,106 +1,111 @@ -import PropTypes from 'prop-types'; -import React from 'react'; -import EventRowMixin from './EventRowMixin'; -import { eventLevels } from './utils/eventLevels'; -import message from './utils/messages'; -import range from 'lodash/range'; - -let isSegmentInSlot = (seg, slot) => seg.left <= slot && seg.right >= slot; -let eventsInSlot = (segments, slot) => segments.filter(seg => isSegmentInSlot(seg, slot)).length - -let EventRow = React.createClass({ - - displayName: 'EventRow', - - propTypes: { +import PropTypes from 'prop-types' +import React from 'react' +import EventRowMixin from './EventRowMixin' +import { eventLevels } from './utils/eventLevels' +import message from './utils/messages' +import range from 'lodash/range' + +let isSegmentInSlot = (seg, slot) => seg.left <= slot && seg.right >= slot +let eventsInSlot = (segments, slot) => + segments.filter(seg => isSegmentInSlot(seg, slot)).length + +class EventEndingRow extends React.Component { + static propTypes = { segments: PropTypes.array, slots: PropTypes.number, messages: PropTypes.object, onShowMore: PropTypes.func, - }, - - mixins: [ EventRowMixin ], + ...EventRowMixin.propTypes, + } + static defaultProps = { + ...EventRowMixin.defaultProps, + } - render(){ - let { segments, slots: slotCount } = this.props; - let rowSegments = eventLevels(segments).levels[0]; + render() { + let { segments, slots: slotCount } = this.props + let rowSegments = eventLevels(segments).levels[0] - let current = 1 - , lastEnd = 1 - , row = []; + let current = 1, lastEnd = 1, row = [] - while (current <= slotCount){ - let key = '_lvl_' + current; + while (current <= slotCount) { + let key = '_lvl_' + current - let { event, left, right, span } = rowSegments - .filter(seg => isSegmentInSlot(seg, current))[0] || {} //eslint-disable-line + let { event, left, right, span } = rowSegments.filter(seg => + isSegmentInSlot(seg, current) + )[0] || {} //eslint-disable-line if (!event) { current++ - continue; + continue } - let gap = Math.max(0, left - lastEnd); + let gap = Math.max(0, left - lastEnd) if (this.canRenderSlotEvent(left, span)) { - let content = this.renderEvent(event) + let content = EventRowMixin.renderEvent(this.props, event) - if (gap) - row.push(this.renderSpan(gap, key + '_gap')) + if (gap) { + row.push(EventRowMixin.renderSpan(this.props, gap, key + '_gap')) + } - row.push( - this.renderSpan(span, key, content) - ) + row.push(EventRowMixin.renderSpan(this.props, span, key, content)) - lastEnd = current = (right + 1); - } - else { - if (gap) - row.push(this.renderSpan(gap, key + '_gap')) + lastEnd = current = right + 1 + } else { + if (gap) { + row.push(EventRowMixin.renderSpan(this.props, gap, key + '_gap')) + } - row.push(this.renderSpan(1, key, this.renderShowMore(segments, current))) + row.push( + EventRowMixin.renderSpan( + this.props, + 1, + key, + this.renderShowMore(segments, current) + ) + ) lastEnd = current = current + 1 } } return ( -
- { row } +
+ {row}
) - }, + } + S - canRenderSlotEvent(slot, span){ - let { segments } = this.props; + canRenderSlotEvent(slot, span) { + let { segments } = this.props return range(slot, slot + span).every(s => { let count = eventsInSlot(segments, s) return count === 1 }) - }, + } renderShowMore(segments, slot) { let messages = message(this.props.messages) let count = eventsInSlot(segments, slot) return count - ? ( - this.showMore(slot, e)} > {messages.showMore(count)} - ) : false - }, + : false + } - _showMore(slot, e){ + showMore(slot, e) { e.preventDefault() this.props.onShowMore(slot) } -}); +} -export default EventRow +export default EventEndingRow diff --git a/src/EventRow.js b/src/EventRow.js index 467203f5f..af87d440b 100644 --- a/src/EventRow.js +++ b/src/EventRow.js @@ -1,47 +1,40 @@ -import PropTypes from 'prop-types'; -import React from 'react'; -import EventRowMixin from './EventRowMixin'; - - -let EventRow = React.createClass({ - - displayName: 'EventRow', - - propTypes: { - segments: PropTypes.array - }, - - mixins: [EventRowMixin], - - render(){ - let { segments } = this.props; +import PropTypes from 'prop-types' +import React from 'react' +import EventRowMixin from './EventRowMixin' + +class EventRow extends React.Component { + static propTypes = { + segments: PropTypes.array, + ...EventRowMixin.propTypes, + } + static defaultProps = { + ...EventRowMixin.defaultProps, + } + render() { + let { segments } = this.props - let lastEnd = 1; + let lastEnd = 1 return ( -
- { - segments.reduce((row, { event, left, right, span }, li) => { - let key = '_lvl_' + li; - let gap = left - lastEnd; +
+ {segments.reduce((row, { event, left, right, span }, li) => { + let key = '_lvl_' + li + let gap = left - lastEnd - let content = this.renderEvent(event) + let content = EventRowMixin.renderEvent(this.props, event) if (gap) - row.push(this.renderSpan(gap, key + '_gap')) + row.push(EventRowMixin.renderSpan(this.props, gap, key + '_gap')) - row.push( - this.renderSpan(span, key, content) - ) + row.push(EventRowMixin.renderSpan(this.props, span, key, content)) - lastEnd = (right + 1); + lastEnd = right + 1 - return row; - }, []) - } + return row + }, [])}
) } -}); +} export default EventRow diff --git a/src/EventRowMixin.js b/src/EventRowMixin.js index 76146bb9e..7a94900b2 100644 --- a/src/EventRowMixin.js +++ b/src/EventRowMixin.js @@ -7,9 +7,9 @@ import { accessor, elementType } from './utils/propTypes'; import { segStyle } from './utils/eventLevels'; import { isSelected } from './utils/selection'; - +/* eslint-disable react/prop-types */ export default { - propType: { + propTypes: { slots: PropTypes.number.isRequired, end: PropTypes.instanceOf(Date), start: PropTypes.instanceOf(Date), @@ -26,21 +26,19 @@ export default { onSelect: PropTypes.func }, - getDefaultProps() { - return { - segments: [], - selected: [], - slots: 7 - } + defaultProps: { + segments: [], + selected: [], + slots: 7 }, - renderEvent(event) { + renderEvent(props, event) { let { eventPropGetter, selected, start, end , startAccessor, endAccessor, titleAccessor , allDayAccessor, eventComponent , eventWrapperComponent - , onSelect } = this.props; + , onSelect } = props; return ( diff --git a/src/EventWrapper.js b/src/EventWrapper.js index 4fabbc4a9..73dfedc7a 100644 --- a/src/EventWrapper.js +++ b/src/EventWrapper.js @@ -1,9 +1,9 @@ import React from 'react'; -let EventWrapper = React.createClass({ +class EventWrapper extends React.Component { render() { return this.props.children; } -}); +} export default EventWrapper; diff --git a/src/Month.js b/src/Month.js index ec0090a9e..54cb84bdb 100644 --- a/src/Month.js +++ b/src/Month.js @@ -1,28 +1,27 @@ -import PropTypes from 'prop-types'; -import React from 'react'; -import { findDOMNode } from 'react-dom'; -import cn from 'classnames'; +import PropTypes from 'prop-types' +import React from 'react' +import { findDOMNode } from 'react-dom' +import cn from 'classnames' -import dates from './utils/dates'; +import dates from './utils/dates' import localizer from './localizer' -import chunk from 'lodash/chunk'; +import chunk from 'lodash/chunk' -import { navigate, views } from './utils/constants'; -import { notify } from './utils/helpers'; -import getPosition from 'dom-helpers/query/position'; -import raf from 'dom-helpers/util/requestAnimationFrame'; +import { navigate, views } from './utils/constants' +import { notify } from './utils/helpers' +import getPosition from 'dom-helpers/query/position' +import raf from 'dom-helpers/util/requestAnimationFrame' -import Popup from './Popup'; -import Overlay from 'react-overlays/lib/Overlay'; -import DateContentRow from './DateContentRow'; -import Header from './Header'; +import Popup from './Popup' +import Overlay from 'react-overlays/lib/Overlay' +import DateContentRow from './DateContentRow' +import Header from './Header' -import { accessor, dateFormat } from './utils/propTypes'; -import { segStyle, inRange, sortEvents } from './utils/eventLevels'; +import { accessor, dateFormat } from './utils/propTypes' +import { segStyle, inRange, sortEvents } from './utils/eventLevels' let eventsForWeek = (evts, start, end, props) => - evts.filter(e => inRange(e, start, end, props)); - + evts.filter(e => inRange(e, start, end, props)) let propTypes = { events: PropTypes.array.isRequired, @@ -69,87 +68,82 @@ let propTypes = { PropTypes.number, PropTypes.shape({ x: PropTypes.number, - y: PropTypes.number - }) + y: PropTypes.number, + }), ]), -}; - -let MonthView = React.createClass({ +} - displayName: 'MonthView', +class MonthView extends React.Component { + static displayName = 'MonthView' + static propTypes = propTypes - propTypes, + constructor(...args) { + super(...args) - getInitialState(){ - return { - rowLimit: 5, - needLimitMeasure: true - } - }, - - componentWillMount() { this._bgRows = [] this._pendingSelection = [] - }, + this.state = { + rowLimit: 5, + needLimitMeasure: true, + } + } componentWillReceiveProps({ date }) { this.setState({ - needLimitMeasure: !dates.eq(date, this.props.date) + needLimitMeasure: !dates.eq(date, this.props.date), }) - }, + } componentDidMount() { - let running; - - if (this.state.needLimitMeasure) - this.measureRowLimit(this.props) - - window.addEventListener('resize', this._resizeListener = ()=> { - if (!running) { - raf(()=> { - running = false - this.setState({ needLimitMeasure: true }) //eslint-disable-line - }) - } - }, false) - }, + let running + + if (this.state.needLimitMeasure) this.measureRowLimit(this.props) + + window.addEventListener( + 'resize', + (this._resizeListener = () => { + if (!running) { + raf(() => { + running = false + this.setState({ needLimitMeasure: true }) //eslint-disable-line + }) + } + }), + false + ) + } componentDidUpdate() { - if (this.state.needLimitMeasure) - this.measureRowLimit(this.props) - }, + if (this.state.needLimitMeasure) this.measureRowLimit(this.props) + } componentWillUnmount() { window.removeEventListener('resize', this._resizeListener, false) - }, + } - getContainer() { + getContainer = () => { return findDOMNode(this) - }, + } render() { - let { date, culture, weekdayFormat, className } = this.props - , month = dates.visibleDays(date, culture) - , weeks = chunk(month, 7); + let { date, culture, weekdayFormat, className } = this.props, + month = dates.visibleDays(date, culture), + weeks = chunk(month, 7) - this._weekCount = weeks.length; + this._weekCount = weeks.length return (
-
- {this._headers(weeks[0], weekdayFormat, culture)} +
+ {this.renderHeaders(weeks[0], weekdayFormat, culture)}
- { weeks.map((week, idx) => - this.renderWeek(week, idx)) - } - { this.props.popup && - this._renderOverlay() - } + {weeks.map((week, idx) => this.renderWeek(week, idx))} + {this.props.popup && this.renderOverlay()}
) - }, + } - renderWeek(week, weekIdx) { + renderWeek = (week, weekIdx) => { let { events, components, @@ -160,9 +154,10 @@ let MonthView = React.createClass({ allDayAccessor, eventPropGetter, messages, - selected } = this.props; + selected, + } = this.props - const { needLimitMeasure, rowLimit } = this.state; + const { needLimitMeasure, rowLimit } = this.state events = eventsForWeek(events, week[0], week[week.length - 1], this.props) events.sort((a, b) => sortEvents(a, b, this.props)) @@ -170,45 +165,44 @@ let MonthView = React.createClass({ return ( ) - }, + } - readerDateHeading({ date, className, ...props }) { - let { date: currentDate, getDrilldownView, dateFormat, culture } = this.props; + readerDateHeading = ({ date, className, ...props }) => { + let { + date: currentDate, + getDrilldownView, + dateFormat, + culture, + } = this.props - let isOffRange = dates.month(date) !== dates.month(currentDate); - let isCurrent = dates.eq(date, currentDate, 'day'); - let drilldownView = getDrilldownView(date); - let label = localizer.format(date, dateFormat, culture); + let isOffRange = dates.month(date) !== dates.month(currentDate) + let isCurrent = dates.eq(date, currentDate, 'day') + let drilldownView = getDrilldownView(date) + let label = localizer.format(date, dateFormat, culture) return (
- {drilldownView ? ( - this.handleHeadingClick(date, drilldownView, e)} - > - {label} - - ) : ( - - {label} - - )} + {drilldownView + ? this.handleHeadingClick(date, drilldownView, e)} + > + {label} + + : + {label} + }
) - }, + } - _headers(row, format, culture) { + renderHeaders(row, format, culture) { let first = row[0] let last = row[row.length - 1] let HeaderComponent = this.props.components.header || Header - return dates.range(first, last, 'day').map((day, idx) => -
+ return dates.range(first, last, 'day').map((day, idx) => ( +
- ) - }, + )) + } - _renderOverlay() { - let overlay = (this.state && this.state.overlay) || {}; - let { components } = this.props; + renderOverlay () { + let overlay = (this.state && this.state.overlay) || {} + let { components } = this.props return ( this.setState({ overlay: null })} @@ -281,85 +269,82 @@ let MonthView = React.createClass({ /> ) - }, + } measureRowLimit() { this.setState({ needLimitMeasure: false, rowLimit: this.refs.slotRow.getRowLimit(), }) - }, + } - handleSelectSlot(range, slotInfo) { - this._pendingSelection = this._pendingSelection - .concat(range) + handleSelectSlot = (range, slotInfo) => { + this._pendingSelection = this._pendingSelection.concat(range) clearTimeout(this._selectTimer) - this._selectTimer = setTimeout(()=> this._selectDates(slotInfo)) - }, + this._selectTimer = setTimeout(() => this.selectDates(slotInfo)) + } - handleHeadingClick(date, view, e){ - e.preventDefault(); + handleHeadingClick = (date, view, e) => { + e.preventDefault() this.clearSelection() notify(this.props.onDrillDown, [date, view]) - }, + } - handleSelectEvent(...args){ + handleSelectEvent = (...args) => { this.clearSelection() notify(this.props.onSelectEvent, args) - }, - - _selectDates(slotInfo) { - let slots = this._pendingSelection.slice() - - this._pendingSelection = [] - - slots.sort((a, b) => +a - +b) - - notify(this.props.onSelectSlot, { - slots, - start: slots[0], - end: slots[slots.length - 1], - action: slotInfo.action - }) - }, + } - handleShowMore(events, date, cell, slot) { + handleShowMore = (events, date, cell, slot) => { const { popup, onDrillDown, onShowMore, getDrilldownView } = this.props //cancel any pending selections so only the event click goes through. this.clearSelection() if (popup) { - let position = getPosition(cell, findDOMNode(this)); + let position = getPosition(cell, findDOMNode(this)) this.setState({ - overlay: { date, events, position } + overlay: { date, events, position }, }) - } - else { + } else { notify(onDrillDown, [date, getDrilldownView(date) || views.DAY]) } notify(onShowMore, [events, date, slot]) - }, + } - clearSelection(){ - clearTimeout(this._selectTimer) - this._pendingSelection = []; + selectDates(slotInfo) { + let slots = this._pendingSelection.slice() + + this._pendingSelection = [] + + slots.sort((a, b) => +a - +b) + + notify(this.props.onSelectSlot, { + slots, + start: slots[0], + end: slots[slots.length - 1], + action: slotInfo.action, + }) } -}); + clearSelection() { + clearTimeout(this._selectTimer) + this._pendingSelection = [] + } +} -MonthView.navigate = (date, action)=> { - switch (action){ +MonthView.navigate = (date, action) => { + switch (action) { case navigate.PREVIOUS: - return dates.add(date, -1, 'month'); + return dates.add(date, -1, 'month') case navigate.NEXT: return dates.add(date, 1, 'month') default: - return date; + return date } } diff --git a/src/Week.js b/src/Week.js index 0d075a96b..8d77bc399 100644 --- a/src/Week.js +++ b/src/Week.js @@ -6,15 +6,12 @@ import { navigate } from './utils/constants'; import TimeGrid from './TimeGrid'; -let Week = React.createClass({ - - propTypes: { +class Week extends React.Component { + static propTypes = { date: PropTypes.instanceOf(Date).isRequired, - }, + }; - getDefaultProps() { - return TimeGrid.defaultProps - }, + static defaultProps = TimeGrid.defaultProps; render() { let { date, ...props } = this.props @@ -24,8 +21,7 @@ let Week = React.createClass({ ); } - -}); +} Week.navigate = (date, action) => { switch (action){