Skip to content

Commit 36871bf

Browse files
authored
feat(allDayMaxRows): Allow for more granular control
Allows for more granular control over the number of events display in the all day row at the top of the TimeGrid Co-authored-by: Arturo Fornes <[email protected]> Closes jquense#2386
1 parent 0b27f58 commit 36871bf

13 files changed

+369
-16
lines changed

src/BackgroundCells.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ import clsx from 'clsx'
44

55
import { notify } from './utils/helpers'
66
import { dateCellSelection, getSlotAtX, pointInBox } from './utils/selection'
7-
import Selection, { getBoundsForNode, isEvent } from './Selection'
7+
import Selection, { getBoundsForNode, isEvent, isShowMore } from './Selection'
88

99
class BackgroundCells extends React.Component {
1010
constructor(props, context) {
@@ -77,7 +77,7 @@ class BackgroundCells extends React.Component {
7777
}))
7878

7979
let selectorClicksHandler = (point, actionType) => {
80-
if (!isEvent(node, point)) {
80+
if (!isEvent(node, point) && !isShowMore(node, point)) {
8181
let rowBox = getBoundsForNode(node)
8282
let { range, rtl } = this.props
8383

src/Calendar.js

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -603,6 +603,15 @@ class Calendar extends React.Component {
603603
*/
604604
showMultiDayTimes: PropTypes.bool,
605605

606+
/**
607+
* Determines a maximum amount of rows of events to display in the all day
608+
* section for Week and Day views, will display `showMore` button if
609+
* events excede this number.
610+
*
611+
* Defaults to `Infinity`
612+
*/
613+
allDayMaxRows: PropTypes.number,
614+
606615
/**
607616
* Constrains the minimum _time_ of the Day and Week views.
608617
*/
@@ -865,6 +874,7 @@ class Calendar extends React.Component {
865874
views: [views.MONTH, views.WEEK, views.DAY, views.AGENDA],
866875
step: 30,
867876
length: 30,
877+
allDayMaxRows: Infinity,
868878

869879
doShowMoreDrillDown: true,
870880
drilldownView: views.DAY,

src/Day.js

Lines changed: 55 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@ import PropTypes from 'prop-types'
22
import React from 'react'
33

44
import { navigate } from './utils/constants'
5+
import { DayLayoutAlgorithmPropType } from './utils/propTypes'
6+
57
import TimeGrid from './TimeGrid'
68

79
class Day extends React.Component {
@@ -39,11 +41,63 @@ class Day extends React.Component {
3941

4042
Day.propTypes = {
4143
date: PropTypes.instanceOf(Date).isRequired,
42-
localizer: PropTypes.any,
44+
45+
events: PropTypes.array.isRequired,
46+
backgroundEvents: PropTypes.array.isRequired,
47+
resources: PropTypes.array,
48+
49+
step: PropTypes.number,
50+
timeslots: PropTypes.number,
51+
range: PropTypes.arrayOf(PropTypes.instanceOf(Date)),
4352
min: PropTypes.instanceOf(Date),
4453
max: PropTypes.instanceOf(Date),
54+
getNow: PropTypes.func.isRequired,
55+
4556
scrollToTime: PropTypes.instanceOf(Date),
4657
enableAutoScroll: PropTypes.bool,
58+
showMultiDayTimes: PropTypes.bool,
59+
60+
rtl: PropTypes.bool,
61+
resizable: PropTypes.bool,
62+
width: PropTypes.number,
63+
64+
accessors: PropTypes.object.isRequired,
65+
components: PropTypes.object.isRequired,
66+
getters: PropTypes.object.isRequired,
67+
localizer: PropTypes.object.isRequired,
68+
69+
allDayMaxRows: PropTypes.number,
70+
71+
selected: PropTypes.object,
72+
selectable: PropTypes.oneOf([true, false, 'ignoreEvents']),
73+
longPressThreshold: PropTypes.number,
74+
75+
onNavigate: PropTypes.func,
76+
onSelectSlot: PropTypes.func,
77+
onSelectEnd: PropTypes.func,
78+
onSelectStart: PropTypes.func,
79+
onSelectEvent: PropTypes.func,
80+
onDoubleClickEvent: PropTypes.func,
81+
onKeyPressEvent: PropTypes.func,
82+
onShowMore: PropTypes.func,
83+
onDrillDown: PropTypes.func,
84+
getDrilldownView: PropTypes.func.isRequired,
85+
86+
dayLayoutAlgorithm: DayLayoutAlgorithmPropType,
87+
88+
showAllEvents: PropTypes.bool,
89+
doShowMoreDrillDown: PropTypes.bool,
90+
91+
popup: PropTypes.bool,
92+
handleDragStart: PropTypes.func,
93+
94+
popupOffset: PropTypes.oneOfType([
95+
PropTypes.number,
96+
PropTypes.shape({
97+
x: PropTypes.number,
98+
y: PropTypes.number,
99+
}),
100+
]),
47101
}
48102

49103
Day.range = (date, { localizer }) => {

src/EventEndingRow.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -83,7 +83,7 @@ class EventEndingRow extends React.Component {
8383
type="button"
8484
key={'sm_' + slot}
8585
className={clsx('rbc-button-link', 'rbc-show-more')}
86-
onClick={e => this.showMore(slot, e)}
86+
onClick={(e) => this.showMore(slot, e)}
8787
>
8888
{localizer.messages.showMore(count)}
8989
</button>

src/Selection.js

Lines changed: 17 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -15,10 +15,19 @@ export function getEventNodeFromPoint(node, { clientX, clientY }) {
1515
return closest(target, '.rbc-event', node)
1616
}
1717

18+
export function getShowMoreNodeFromPoint(node, { clientX, clientY }) {
19+
let target = document.elementFromPoint(clientX, clientY)
20+
return closest(target, '.rbc-show-more', node)
21+
}
22+
1823
export function isEvent(node, bounds) {
1924
return !!getEventNodeFromPoint(node, bounds)
2025
}
2126

27+
export function isShowMore(node, bounds) {
28+
return !!getShowMoreNodeFromPoint(node, bounds)
29+
}
30+
2231
function getEventCoordinates(e) {
2332
let target = e
2433

@@ -38,7 +47,10 @@ const clickTolerance = 5
3847
const clickInterval = 250
3948

4049
class Selection {
41-
constructor(node, { global = false, longPressThreshold = 250, validContainers = [] } = {}) {
50+
constructor(
51+
node,
52+
{ global = false, longPressThreshold = 250, validContainers = [] } = {}
53+
) {
4254
this.isDetached = false
4355
this.container = node
4456
this.globalMouse = !node || global
@@ -307,15 +319,14 @@ class Selection {
307319
// Check whether provided event target element
308320
// - is contained within a valid container
309321
_isWithinValidContainer(e) {
310-
const eventTarget = e.target;
311-
const containers = this.validContainers;
322+
const eventTarget = e.target
323+
const containers = this.validContainers
312324

313325
if (!containers || !containers.length || !eventTarget) {
314-
return true;
326+
return true
315327
}
316328

317-
return containers.some(
318-
(target) => !!eventTarget.closest(target));
329+
return containers.some((target) => !!eventTarget.closest(target))
319330
}
320331

321332
_handleTerminatingEvent(e) {

src/TimeGrid.js

Lines changed: 112 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6,11 +6,14 @@ import memoize from 'memoize-one'
66

77
import DayColumn from './DayColumn'
88
import TimeGutter from './TimeGutter'
9+
import TimeGridHeader from './TimeGridHeader'
10+
import PopOverlay from './PopOverlay'
911

1012
import getWidth from 'dom-helpers/width'
11-
import TimeGridHeader from './TimeGridHeader'
12-
import { notify } from './utils/helpers'
13+
import getPosition from 'dom-helpers/position'
14+
import { views } from './utils/constants'
1315
import { inRange, sortEvents } from './utils/eventLevels'
16+
import { notify } from './utils/helpers'
1417
import Resources from './utils/Resources'
1518
import { DayLayoutAlgorithmPropType } from './utils/propTypes'
1619

@@ -22,6 +25,7 @@ export default class TimeGrid extends Component {
2225

2326
this.scrollRef = React.createRef()
2427
this.contentRef = React.createRef()
28+
this.containerRef = React.createRef()
2529
this._scrollRatio = null
2630
this.gutterRef = createRef()
2731
}
@@ -67,12 +71,50 @@ export default class TimeGrid extends Component {
6771
this.applyScroll()
6872
}
6973

70-
handleSelectAlldayEvent = (...args) => {
74+
handleKeyPressEvent = (...args) => {
75+
this.clearSelection()
76+
notify(this.props.onKeyPressEvent, args)
77+
}
78+
79+
handleSelectEvent = (...args) => {
7180
//cancel any pending selections so only the event click goes through.
7281
this.clearSelection()
7382
notify(this.props.onSelectEvent, args)
7483
}
7584

85+
handleDoubleClickEvent = (...args) => {
86+
this.clearSelection()
87+
notify(this.props.onDoubleClickEvent, args)
88+
}
89+
90+
handleShowMore = (events, date, cell, slot, target) => {
91+
const {
92+
popup,
93+
onDrillDown,
94+
onShowMore,
95+
getDrilldownView,
96+
doShowMoreDrillDown,
97+
} = this.props
98+
this.clearSelection()
99+
100+
if (popup) {
101+
let position = getPosition(cell, this.containerRef.current)
102+
103+
this.setState({
104+
overlay: {
105+
date,
106+
events,
107+
position: { ...position, width: '200px' },
108+
target,
109+
},
110+
})
111+
} else if (doShowMoreDrillDown) {
112+
notify(onDrillDown, [date, getDrilldownView(date) || views.DAY])
113+
}
114+
115+
notify(onShowMore, [events, date, slot])
116+
}
117+
76118
handleSelectAllDaySlot = (slots, slotInfo) => {
77119
const { onSelectSlot } = this.props
78120

@@ -202,6 +244,7 @@ export default class TimeGrid extends Component {
202244
'rbc-time-view',
203245
resources && 'rbc-time-view-resources'
204246
)}
247+
ref={this.containerRef}
205248
>
206249
<TimeGridHeader
207250
range={range}
@@ -211,6 +254,11 @@ export default class TimeGrid extends Component {
211254
getNow={getNow}
212255
localizer={localizer}
213256
selected={selected}
257+
allDayMaxRows={
258+
this.props.showAllEvents
259+
? Infinity
260+
: this.props.allDayMaxRows ?? Infinity
261+
}
214262
resources={this.memoizedResources(resources, accessors)}
215263
selectable={this.props.selectable}
216264
accessors={accessors}
@@ -220,13 +268,15 @@ export default class TimeGrid extends Component {
220268
isOverflowing={this.state.isOverflowing}
221269
longPressThreshold={longPressThreshold}
222270
onSelectSlot={this.handleSelectAllDaySlot}
223-
onSelectEvent={this.handleSelectAlldayEvent}
271+
onSelectEvent={this.handleSelectEvent}
272+
onShowMore={this.handleShowMore}
224273
onDoubleClickEvent={this.props.onDoubleClickEvent}
225274
onKeyPressEvent={this.props.onKeyPressEvent}
226275
onDrillDown={this.props.onDrillDown}
227276
getDrilldownView={this.props.getDrilldownView}
228277
resizable={resizable}
229278
/>
279+
{this.props.popup && this.renderOverlay()}
230280
<div
231281
ref={this.contentRef}
232282
className="rbc-time-content"
@@ -256,6 +306,47 @@ export default class TimeGrid extends Component {
256306
)
257307
}
258308

309+
renderOverlay() {
310+
let overlay = this.state?.overlay ?? {}
311+
let {
312+
accessors,
313+
localizer,
314+
components,
315+
getters,
316+
selected,
317+
popupOffset,
318+
handleDragStart,
319+
} = this.props
320+
321+
const onHide = () => this.setState({ overlay: null })
322+
323+
return (
324+
<PopOverlay
325+
overlay={overlay}
326+
accessors={accessors}
327+
localizer={localizer}
328+
components={components}
329+
getters={getters}
330+
selected={selected}
331+
popupOffset={popupOffset}
332+
ref={this.containerRef}
333+
handleKeyPressEvent={this.handleKeyPressEvent}
334+
handleSelectEvent={this.handleSelectEvent}
335+
handleDoubleClickEvent={this.handleDoubleClickEvent}
336+
handleDragStart={handleDragStart}
337+
show={!!overlay.position}
338+
overlayDisplay={this.overlayDisplay}
339+
onHide={onHide}
340+
/>
341+
)
342+
}
343+
344+
overlayDisplay = () => {
345+
this.setState({
346+
overlay: null,
347+
})
348+
}
349+
259350
clearSelection() {
260351
clearTimeout(this._selectTimer)
261352
this._pendingSelection = []
@@ -341,6 +432,8 @@ TimeGrid.propTypes = {
341432
getters: PropTypes.object.isRequired,
342433
localizer: PropTypes.object.isRequired,
343434

435+
allDayMaxRows: PropTypes.number,
436+
344437
selected: PropTypes.object,
345438
selectable: PropTypes.oneOf([true, false, 'ignoreEvents']),
346439
longPressThreshold: PropTypes.number,
@@ -350,12 +443,27 @@ TimeGrid.propTypes = {
350443
onSelectEnd: PropTypes.func,
351444
onSelectStart: PropTypes.func,
352445
onSelectEvent: PropTypes.func,
446+
onShowMore: PropTypes.func,
353447
onDoubleClickEvent: PropTypes.func,
354448
onKeyPressEvent: PropTypes.func,
355449
onDrillDown: PropTypes.func,
356450
getDrilldownView: PropTypes.func.isRequired,
357451

358452
dayLayoutAlgorithm: DayLayoutAlgorithmPropType,
453+
454+
showAllEvents: PropTypes.bool,
455+
doShowMoreDrillDown: PropTypes.bool,
456+
457+
popup: PropTypes.bool,
458+
handleDragStart: PropTypes.func,
459+
460+
popupOffset: PropTypes.oneOfType([
461+
PropTypes.number,
462+
PropTypes.shape({
463+
x: PropTypes.number,
464+
y: PropTypes.number,
465+
}),
466+
]),
359467
}
360468

361469
TimeGrid.defaultProps = {

0 commit comments

Comments
 (0)