Skip to content

Commit 3309d4f

Browse files
authored
Merge pull request #499 from namespace-ee/350
fix selected prop
2 parents 6e2cacf + 1f369f3 commit 3309d4f

File tree

7 files changed

+399
-507
lines changed

7 files changed

+399
-507
lines changed

README.md

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -142,7 +142,7 @@ The exact viewport of the calendar. When these are specified, scrolling in the c
142142

143143
## selected
144144

145-
An array with id's corresponding to id's in items (`item.id`). If this prop is set you have to manage the selected items yourself within the `onItemSelect` handler to update the property with new id's. This overwrites the default behaviour of selecting one item on click.
145+
An array with id's corresponding to id's in items (`item.id`). If this prop is set you have to manage the selected items yourself within the `onItemSelect` handler to update the property with new id's and use `onItemDeselect` handler to clear selection. This overwrites the default behaviour of selecting one item on click.
146146

147147
## keys
148148

@@ -280,6 +280,10 @@ Callback when an item is resized. Returns 1) the item's ID, 2) the new start or
280280

281281
Called when an item is selected. This is sent on the first click on an item. `time` is the time that corresponds to where you click/select on the item in the timeline.
282282

283+
## onItemDeselect(e)
284+
285+
Called when deselecting an item. Used to clear controlled selected prop.
286+
283287
## onItemClick(itemId, e, time)
284288

285289
Called when an item is clicked. Note: the item must be selected before it's clicked... except if it's a touch event and `itemTouchSendsClick` is enabled. `time` is the time that corresponds to where you click on the item in the timeline.
Lines changed: 214 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,214 @@
1+
/* eslint-disable no-console */
2+
import React, { Component } from 'react'
3+
import moment from 'moment'
4+
5+
import Timeline, {
6+
TimelineMarkers,
7+
TimelineHeaders,
8+
TodayMarker,
9+
CustomMarker,
10+
CursorMarker,
11+
CustomHeader,
12+
SidebarHeader,
13+
DateHeader
14+
} from 'react-calendar-timeline'
15+
16+
import generateFakeData from '../generate-fake-data'
17+
18+
var minTime = moment()
19+
.add(-6, 'months')
20+
.valueOf()
21+
var maxTime = moment()
22+
.add(6, 'months')
23+
.valueOf()
24+
25+
var keys = {
26+
groupIdKey: 'id',
27+
groupTitleKey: 'title',
28+
groupRightTitleKey: 'rightTitle',
29+
itemIdKey: 'id',
30+
itemTitleKey: 'title',
31+
itemDivTitleKey: 'title',
32+
itemGroupKey: 'group',
33+
itemTimeStartKey: 'start',
34+
itemTimeEndKey: 'end'
35+
}
36+
37+
export default class App extends Component {
38+
constructor(props) {
39+
super(props)
40+
41+
const { groups, items } = generateFakeData()
42+
const defaultTimeStart = moment()
43+
.startOf('day')
44+
.toDate()
45+
const defaultTimeEnd = moment()
46+
.startOf('day')
47+
.add(1, 'day')
48+
.toDate()
49+
50+
this.state = {
51+
groups,
52+
items,
53+
defaultTimeStart,
54+
defaultTimeEnd,
55+
selected: undefined,
56+
}
57+
}
58+
59+
handleCanvasClick = (groupId, time) => {
60+
console.log('Canvas clicked', groupId, moment(time).format())
61+
}
62+
63+
handleCanvasDoubleClick = (groupId, time) => {
64+
console.log('Canvas double clicked', groupId, moment(time).format())
65+
}
66+
67+
handleCanvasContextMenu = (group, time) => {
68+
console.log('Canvas context menu', group, moment(time).format())
69+
}
70+
71+
handleItemClick = (itemId, _, time) => {
72+
console.log('Clicked: ' + itemId, moment(time).format())
73+
}
74+
75+
handleItemSelect = (itemId, _, time) => {
76+
this.setState({
77+
selected: [itemId]
78+
})
79+
console.log('Selected: ' + itemId, moment(time).format())
80+
}
81+
82+
handleItemDeselect = () => {
83+
this.setState({selected: undefined})
84+
}
85+
86+
handleItemDoubleClick = (itemId, _, time) => {
87+
console.log('Double Click: ' + itemId, moment(time).format())
88+
}
89+
90+
handleItemContextMenu = (itemId, _, time) => {
91+
console.log('Context Menu: ' + itemId, moment(time).format())
92+
}
93+
94+
handleItemMove = (itemId, dragTime, newGroupOrder) => {
95+
const { items, groups } = this.state
96+
97+
const group = groups[newGroupOrder]
98+
99+
this.setState({
100+
items: items.map(
101+
item =>
102+
item.id === itemId
103+
? Object.assign({}, item, {
104+
start: dragTime,
105+
end: dragTime + (item.end - item.start),
106+
group: group.id
107+
})
108+
: item
109+
)
110+
})
111+
112+
console.log('Moved', itemId, dragTime, newGroupOrder)
113+
}
114+
115+
handleItemResize = (itemId, time, edge) => {
116+
const { items } = this.state
117+
118+
this.setState({
119+
items: items.map(
120+
item =>
121+
item.id === itemId
122+
? Object.assign({}, item, {
123+
start: edge === 'left' ? time : item.start,
124+
end: edge === 'left' ? item.end : time
125+
})
126+
: item
127+
)
128+
})
129+
130+
console.log('Resized', itemId, time, edge)
131+
}
132+
133+
// this limits the timeline to -6 months ... +6 months
134+
handleTimeChange = (visibleTimeStart, visibleTimeEnd, updateScrollCanvas) => {
135+
if (visibleTimeStart < minTime && visibleTimeEnd > maxTime) {
136+
updateScrollCanvas(minTime, maxTime)
137+
} else if (visibleTimeStart < minTime) {
138+
updateScrollCanvas(minTime, minTime + (visibleTimeEnd - visibleTimeStart))
139+
} else if (visibleTimeEnd > maxTime) {
140+
updateScrollCanvas(maxTime - (visibleTimeEnd - visibleTimeStart), maxTime)
141+
} else {
142+
updateScrollCanvas(visibleTimeStart, visibleTimeEnd)
143+
}
144+
}
145+
146+
moveResizeValidator = (action, item, time) => {
147+
if (time < new Date().getTime()) {
148+
var newTime =
149+
Math.ceil(new Date().getTime() / (15 * 60 * 1000)) * (15 * 60 * 1000)
150+
return newTime
151+
}
152+
153+
return time
154+
}
155+
156+
render() {
157+
const { groups, items, defaultTimeStart, defaultTimeEnd } = this.state
158+
159+
return (
160+
<Timeline
161+
groups={groups}
162+
items={items}
163+
keys={keys}
164+
sidebarWidth={150}
165+
sidebarContent={<div>Above The Left</div>}
166+
canMove
167+
canResize="right"
168+
canSelect
169+
itemsSorted
170+
itemTouchSendsClick={false}
171+
stackItems
172+
itemHeightRatio={0.75}
173+
defaultTimeStart={defaultTimeStart}
174+
defaultTimeEnd={defaultTimeEnd}
175+
onCanvasClick={this.handleCanvasClick}
176+
onCanvasDoubleClick={this.handleCanvasDoubleClick}
177+
onCanvasContextMenu={this.handleCanvasContextMenu}
178+
onItemClick={this.handleItemClick}
179+
onItemSelect={this.handleItemSelect}
180+
onItemContextMenu={this.handleItemContextMenu}
181+
onItemMove={this.handleItemMove}
182+
onItemResize={this.handleItemResize}
183+
onItemDoubleClick={this.handleItemDoubleClick}
184+
onTimeChange={this.handleTimeChange}
185+
moveResizeValidator={this.moveResizeValidator}
186+
selected={this.state.selected}
187+
onItemDeselect={this.handleItemDeselect}
188+
>
189+
<TimelineMarkers>
190+
<TodayMarker />
191+
<CustomMarker
192+
date={
193+
moment()
194+
.startOf('day')
195+
.valueOf() +
196+
1000 * 60 * 60 * 2
197+
}
198+
/>
199+
<CustomMarker
200+
date={moment()
201+
.add(3, 'day')
202+
.valueOf()}
203+
>
204+
{({ styles }) => {
205+
const newStyles = { ...styles, backgroundColor: 'blue' }
206+
return <div style={newStyles} />
207+
}}
208+
</CustomMarker>
209+
<CursorMarker />
210+
</TimelineMarkers>
211+
</Timeline>
212+
)
213+
}
214+
}

demo/app/index.js

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,8 @@ const demos = {
1515
verticalClasses: require('./demo-vertical-classes').default,
1616
customItems: require('./demo-custom-items').default,
1717
customHeaders: require('./demo-headers').default,
18-
customInfoLabel: require('./demo-custom-info-label').default
18+
customInfoLabel: require('./demo-custom-info-label').default,
19+
controledSelect: require('./demo-controlled-select').default
1920
}
2021

2122
// A simple component that shows the pathname of the current location

package.json

Lines changed: 13 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -5,18 +5,19 @@
55
"main": "lib/index.js",
66
"scripts": {
77
"build": "npm run build:lib",
8-
"build:demo":
9-
"echo '!!! Building Demo' && cross-env NODE_ENV=production webpack --progress",
10-
"build:lib":
11-
"echo '!!! Building Library' && rimraf lib && cross-env NODE_ENV=production babel src --out-dir lib && node-sass src/lib/Timeline.scss lib/Timeline.css && sed -i'.bak' 's/Timeline\\.scss/Timeline\\.css/g' lib/lib/Timeline.js && rm lib/lib/Timeline.js.bak",
8+
"build:demo": "echo '!!! Building Demo' && cross-env NODE_ENV=production webpack --progress",
9+
"build:lib": "echo '!!! Building Library' && rimraf lib && cross-env NODE_ENV=production babel src --out-dir lib && node-sass src/lib/Timeline.scss lib/Timeline.css && sed -i'.bak' 's/Timeline\\.scss/Timeline\\.css/g' lib/lib/Timeline.js && rm lib/lib/Timeline.js.bak",
1210
"lint": "eslint --ext .js --ext .jsx ./src",
1311
"lint:fix": "prettier-eslint --parser babylon --write \"src/**/*.js\"",
1412
"prepublish": "npm run build:lib",
1513
"start": "webpack-dev-server --hot --host 0.0.0.0 --display-modules",
1614
"test": "jest",
1715
"test:watch": "jest --watch"
1816
},
19-
"files": ["lib", "src"],
17+
"files": [
18+
"lib",
19+
"src"
20+
],
2021
"homepage": "https://github.com/namespace-ee/react-calendar-timeline",
2122
"repository": {
2223
"type": "git",
@@ -57,7 +58,12 @@
5758
}
5859
],
5960
"license": "MIT",
60-
"keywords": ["react", "reactjs", "react-component", "timeline"],
61+
"keywords": [
62+
"react",
63+
"reactjs",
64+
"react-component",
65+
"timeline"
66+
],
6167
"standard": {
6268
"parser": "babel-eslint"
6369
},
@@ -128,7 +134,7 @@
128134
"jest-watch-typeahead": "^0.3.1",
129135
"jsdom": "^11.5.1",
130136
"moment": "^2.11.1",
131-
"node-sass": "^4.9.2",
137+
"node-sass": "^4.12.0",
132138
"prettier": "^1.10.2",
133139
"prettier-eslint-cli": "^4.7.0",
134140
"prop-types": "^15.6.2",

src/lib/Timeline.js

Lines changed: 22 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -268,6 +268,10 @@ export default class ReactCalendarTimeline extends Component {
268268
constructor(props) {
269269
super(props)
270270

271+
this.getSelected = this.getSelected.bind(this)
272+
this.hasSelectedItem = this.hasSelectedItem.bind(this)
273+
this.isItemSelected= this.isItemSelected.bind(this)
274+
271275
let visibleTimeStart = null
272276
let visibleTimeEnd = null
273277

@@ -581,7 +585,7 @@ export default class ReactCalendarTimeline extends Component {
581585

582586
selectItem = (item, clickType, e) => {
583587
if (
584-
this.state.selectedItem === item ||
588+
this.isItemSelected(item) ||
585589
(this.props.itemTouchSendsClick && clickType === 'touch')
586590
) {
587591
if (item && this.props.onItemClick) {
@@ -732,7 +736,7 @@ export default class ReactCalendarTimeline extends Component {
732736

733737
handleRowClick = (e, rowIndex) => {
734738
// shouldnt this be handled by the user, as far as when to deselect an item?
735-
if (this.state.selectedItem) {
739+
if (this.hasSelectedItem()) {
736740
this.selectItem(null)
737741
}
738742

@@ -907,10 +911,7 @@ export default class ReactCalendarTimeline extends Component {
907911
keys: this.props.keys,
908912
groupHeights: groupHeights,
909913
groupTops: groupTops,
910-
selected:
911-
this.state.selectedItem && !this.props.selected
912-
? [this.state.selectedItem]
913-
: this.props.selected || [],
914+
selected: this.getSelected(),
914915
height: height,
915916
minUnit: minUnit,
916917
timeSteps: timeSteps
@@ -945,6 +946,21 @@ export default class ReactCalendarTimeline extends Component {
945946
)
946947
}
947948

949+
getSelected() {
950+
return this.state.selectedItem && !this.props.selected
951+
? [this.state.selectedItem]
952+
: this.props.selected || [];
953+
}
954+
955+
hasSelectedItem(){
956+
if(!Array.isArray(this.props.selected)) return !!this.state.selectedItem
957+
return this.props.selected.length > 0
958+
}
959+
960+
isItemSelected(itemId){
961+
const selectedItems = this.getSelected()
962+
return selectedItems.some(i => i === itemId)
963+
}
948964
getScrollElementRef = el => {
949965
this.props.scrollRef(el)
950966
this.scrollComponent = el

0 commit comments

Comments
 (0)