From 0d5df794bcbcf4c0c31218ab47cdbb49cbd1981f Mon Sep 17 00:00:00 2001 From: jquense Date: Mon, 26 Oct 2015 19:29:45 +0700 Subject: [PATCH] [changed] "show more" behavior is cleaner --- src/EventEndingRow.jsx | 94 +++++++++++++++++++++++++++++++ src/EventRow.jsx | 72 +++--------------------- src/EventRowMixin.js | 71 +++++++++++++++++++++++ src/Month.jsx | 125 ++++++++++++++++++++--------------------- src/utils/messages.js | 2 +- 5 files changed, 235 insertions(+), 129 deletions(-) create mode 100644 src/EventEndingRow.jsx create mode 100644 src/EventRowMixin.js diff --git a/src/EventEndingRow.jsx b/src/EventEndingRow.jsx new file mode 100644 index 000000000..6dde90020 --- /dev/null +++ b/src/EventEndingRow.jsx @@ -0,0 +1,94 @@ +import React from 'react'; +import EventRowMixin from './EventRowMixin'; +import { eventLevels } from './utils/eventLevels'; +import message from './utils/messages'; +import range from 'lodash/utility/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: { + segments: React.PropTypes.array, + slots: React.PropTypes.number + }, + + mixins: [ EventRowMixin ], + + render(){ + let { segments, slots: slotCount } = this.props; + let rowSegments = eventLevels(segments).levels[0]; + + let current = 1 + , lastEnd = 1 + , row = []; + + while (current <= slotCount){ + let key = '_lvl_' + current; + + let { event, left, right, span } = rowSegments + .filter(seg => isSegmentInSlot(seg, current))[0] || {} //eslint-disable-line + + if (!event) { + current++ + continue; + } + + if (this.canRenderSlotEvent(left, span)) { + let gap = left - lastEnd; + let content = this.renderEvent(event) + + if (gap) + row.push(this.renderSpan(gap, key + '_gap')) + + row.push( + this.renderSpan(span, key, content) + ) + + lastEnd = current = (right + 1); + } + else { + row.push(this.renderSpan(1, key, this.renderShowMore(segments, current))) + current++; + } + } + + return ( +
+ { row } +
+ ) + }, + + 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 + ? ( + + {messages.showMore(count)} + + ) : false + } +}); + +export default EventRow diff --git a/src/EventRow.jsx b/src/EventRow.jsx index 90793ce32..ddf56c6e8 100644 --- a/src/EventRow.jsx +++ b/src/EventRow.jsx @@ -1,41 +1,19 @@ import React from 'react'; -import { findDOMNode } from 'react-dom'; -import EventCell from './EventCell'; -import getHeight from 'dom-helpers/query/height'; -import { accessor } from './utils/propTypes'; -import { segStyle } from './utils/eventLevels'; -import { isSelected } from './utils/selection'; - -let propTypes = { - segments: React.PropTypes.array, - end: React.PropTypes.instanceOf(Date), - start: React.PropTypes.instanceOf(Date), - - titleAccessor: accessor, - allDayAccessor: accessor, - startAccessor: accessor, - endAccessor: accessor, - - onEventClick: React.PropTypes.func -}; +import EventRowMixin from './EventRowMixin'; let EventRow = React.createClass({ displayName: 'EventRow', - propTypes, - - getDefaultProps() { - return { - segments: [], - selected: [], - slots: 7 - }; + propTypes: { + segments: React.PropTypes.array }, + mixins: [EventRowMixin], + render(){ - let { segments, start, end } = this.props; + let { segments } = this.props; let lastEnd = 1; @@ -46,7 +24,7 @@ let EventRow = React.createClass({ let key = '_lvl_' + li; let gap = left - lastEnd; - let content = this.renderEvent(event, start, end) + let content = this.renderEvent(event) if (gap) row.push(this.renderSpan(gap, key + '_gap')) @@ -62,42 +40,6 @@ let EventRow = React.createClass({ } ) - }, - - renderEvent(event, start, end, props = {}){ - let { - eventPropGetter, selected, startAccessor, endAccessor - , titleAccessor, eventComponent, onSelect } = this.props; - - - return ( - - ) - }, - - renderSpan(len, key, content = ' '){ - let { slots } = this.props; - - return ( -
- {content} -
- ) - }, - - getRowHeight(){ - getHeight(findDOMNode(this)) } }); diff --git a/src/EventRowMixin.js b/src/EventRowMixin.js new file mode 100644 index 000000000..3c1b64a52 --- /dev/null +++ b/src/EventRowMixin.js @@ -0,0 +1,71 @@ +import React, { PropTypes } from 'react'; +import { findDOMNode } from 'react-dom'; +import EventCell from './EventCell'; +import getHeight from 'dom-helpers/query/height'; +import { accessor, elementType } from './utils/propTypes'; +import { segStyle } from './utils/eventLevels'; +import { isSelected } from './utils/selection'; + + +export default { + propType: { + slots: PropTypes.number.isRequired, + end: PropTypes.instanceOf(Date), + start: PropTypes.instanceOf(Date), + + selected: PropTypes.array, + eventPropGetter: PropTypes.func, + titleAccessor: accessor, + allDayAccessor: accessor, + startAccessor: accessor, + endAccessor: accessor, + + eventComponent: elementType, + onSelect: React.PropTypes.func + }, + + getDefaultProps() { + return { + segments: [], + selected: [], + slots: 7 + } + }, + + renderEvent(event){ + let { + eventPropGetter, selected, start, end + , startAccessor, endAccessor, titleAccessor + , allDayAccessor, eventComponent, onSelect } = this.props; + + return ( + + ) + }, + + renderSpan(len, key, content = ' '){ + let { slots } = this.props; + + return ( +
+ {content} +
+ ) + }, + + getRowHeight(){ + getHeight(findDOMNode(this)) + } +} diff --git a/src/Month.jsx b/src/Month.jsx index 50110a04a..9e3395675 100644 --- a/src/Month.jsx +++ b/src/Month.jsx @@ -13,6 +13,7 @@ import getPosition from 'dom-helpers/query/position'; import raf from 'dom-helpers/util/requestAnimationFrame'; import EventRow from './EventRow'; +import EventEndingRow from './EventEndingRow'; import Popup from './Popup'; import Overlay from 'react-overlays/lib/Overlay'; import BackgroundCells from './BackgroundCells'; @@ -20,11 +21,11 @@ import BackgroundCells from './BackgroundCells'; import { dateFormat } from './utils/propTypes'; import { segStyle, inRange, eventSegments, eventLevels, sortEvents } from './utils/eventLevels'; -let isSegmentInSlot = (seg, slot) => seg.left <= slot && seg.right >= slot; let eventsForWeek = (evts, start, end, props) => evts.filter(e => inRange(e, start, end, props)); +let isSegmentInSlot = (seg, slot) => seg.left <= slot && seg.right >= slot; let propTypes = { ...EventRow.PropTypes, @@ -97,11 +98,11 @@ let MonthView = React.createClass({ render(){ var { date, culture, weekdayFormat } = this.props , month = dates.visibleDays(date, culture) - , rows = chunk(month, 7); + , weeks = chunk(month, 7); let measure = this.state.needLimitMeasure - this._rowCount = rows.length; + this._weekCount = weeks.length; var elementProps = omit(this.props, Object.keys(propTypes)); @@ -111,53 +112,54 @@ let MonthView = React.createClass({ className={cn('rbc-month-view', elementProps.className)} >
- {this._headers(rows[0], weekdayFormat, culture)} + {this._headers(weeks[0], weekdayFormat, culture)}
- { rows.map((row, idx) => - this._row(row, idx, measure && this._renderMeasureRows)) + { weeks.map((week, idx) => + this.renderWeek(week, idx, measure && this._renderMeasureRows)) } ) }, - _row(row, rowIdx, content) { - let first = row[0] - let last = row[row.length - 1] - let evts = eventsForWeek(this.props.events, row[0], row[row.length - 1], this.props) + renderWeek(week, weekIdx, content) { + let first = week[0] + let last = week[week.length - 1] + let evts = eventsForWeek(this.props.events, week[0], week[week.length - 1], this.props) evts.sort((a, b) => sortEvents(a, b, this.props)) let segments = evts = evts.map(evt => eventSegments(evt, first, last, this.props)) - let limit = this.state.rowLimit; + let limit = (this.state.rowLimit - 1) || 1; let { levels, extra } = eventLevels(segments, limit) + content = content || ((lvls, wk) => lvls.map((lvl, idx) => this.renderRowLevel(lvl, wk, idx))) + return ( -
this._firstRow = r)} + ref={!weekIdx && (r => this._firstRow = r)} > { - this.renderBackground(row, rowIdx) + this.renderBackground(week, weekIdx) }
this._firstDateRow = r)} + ref={!weekIdx && (r => this._firstDateRow = r)} > - { this._dates(row) } + { this._dates(week) }
{ - content - ? content(levels, row, rowIdx) - : levels.map((l, idx) => this.renderRowLevel(l, row, idx)) + content(levels, week, weekIdx) + } + { + !!extra.length && + this.renderShowMore(segments, extra, week, weekIdx, levels.length) }
- { - this.renderShowMore(segments, extra, row, rowIdx) - } { this.props.popup && this._renderOverlay() } @@ -165,23 +167,6 @@ let MonthView = React.createClass({ ) }, - renderRowLevel(segments, week, idx){ - let first = week[0] - let last = week[week.length - 1] - - return ( - - ) - }, - renderBackground(row, idx){ let self = this; @@ -204,31 +189,41 @@ let MonthView = React.createClass({ ) }, - renderShowMore(segments, extraSegments, row, rowIdx) { - return row.map((date, idx) => { - let slot = idx + 1; + renderRowLevel(segments, week, idx){ + let first = week[0] + let last = week[week.length - 1] - let count = extraSegments - .filter(seg => isSegmentInSlot(seg, slot)).length + return ( + + ) + }, - let events = segments - .filter(seg => isSegmentInSlot(seg, slot)) - .map(seg => seg.event) + renderShowMore(segments, extraSegments, week, weekIdx) { + let first = week[0] + let last = week[week.length - 1] - let onClick = ()=> this._showMore(events, date, rowIdx, idx) + let onClick = slot => this._showMore(segments, week[slot - 1], weekIdx, slot) - return count - ? ( - - {'show ' + count + ' more'} - - ) : false - }) + return ( + + ) }, _dates(row){ @@ -348,12 +343,16 @@ let MonthView = React.createClass({ }) }, - _showMore(events, date, row, slot){ - let cell = findDOMNode(this._bgRows[row]).children[slot]; + _showMore(segments, date, weekIdx, slot){ + let cell = findDOMNode(this._bgRows[weekIdx]).children[slot - 1]; + + let events = segments + .filter(seg => isSegmentInSlot(seg, slot)) + .map(seg => seg.event) //cancel any pending selections so only the event click goes through. this.clearSelection() - + if (this.props.popup) { let position = getPosition(cell, findDOMNode(this)); diff --git a/src/utils/messages.js b/src/utils/messages.js index 8f6d0194d..d9f54269a 100644 --- a/src/utils/messages.js +++ b/src/utils/messages.js @@ -15,7 +15,7 @@ import invariant from 'invariant'; today: 'today', agenda: 'agenda', - hiddenEvents: total => `show ${total} more` + showMore: total => `+${total} more` } export function set(key, msg){