Skip to content

Commit fe29528

Browse files
committed
better handle pasted rows in timetables
1 parent 0f132f4 commit fe29528

File tree

1 file changed

+45
-18
lines changed

1 file changed

+45
-18
lines changed

src/main/client/editor/components/TimetableEditor.js

Lines changed: 45 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,8 @@ import objectPath from 'object-path'
1111
import EditableCell from '../../common/components/EditableCell'
1212
import HourMinuteInput from './HourMinuteInput'
1313

14+
const DT_FORMATS = ['YYYY-MM-DDTHH:mm:ss', 'YYYY-MM-DDTh:mm:ss a', 'YYYY-MM-DDTh:mm a']
15+
1416
export default class TimetableEditor extends Component {
1517
static propTypes = {
1618
route: PropTypes.object,
@@ -61,15 +63,12 @@ export default class TimetableEditor extends Component {
6163
let stateUpdate = { edited: { $push: [rowIndex] }, data: {$set: newRows} }
6264
this.setState(update(this.state, stateUpdate))
6365
}
64-
addNewRow (columns, blank = false) {
66+
constructNewRow (columns, toClone = null) {
6567
const activePattern = this.props.route && this.props.route.tripPatterns ? this.props.route.tripPatterns.find(p => p.id === this.props.activePatternId) : null
66-
67-
// set blank to true if there are no rows to clone
68-
blank = blank || this.state.data.length === 0
69-
let newRow = blank ? {} : clone(this.state.data[this.state.data.length - 1]) || {}
68+
let newRow = toClone ? clone(toClone) || {} : {}
7069

7170
// set starting time for first arrival
72-
let cumulativeTravelTime = blank ? 0 : objectPath.get(newRow, `stopTimes.0.arrivalTime`)
71+
let cumulativeTravelTime = !toClone ? 0 : objectPath.get(newRow, `stopTimes.0.arrivalTime`)
7372
cumulativeTravelTime += this.state.offsetSeconds
7473

7574
for (let i = 0; i < activePattern.patternStops.length; i++) {
@@ -78,7 +77,6 @@ export default class TimetableEditor extends Component {
7877
if (!objectPath.get(newRow, `stopTimes.${i}`)) {
7978
objectPath.set(newRow, `stopTimes.${i}`, {})
8079
}
81-
console.log(objectPath.get(newRow, `stopTimes.${i}`))
8280
objectPath.set(newRow, `stopTimes.${i}.stopId`, stop.stopId)
8381
cumulativeTravelTime += +stop.defaultTravelTime
8482
objectPath.set(newRow, `stopTimes.${i}.arrivalTime`, cumulativeTravelTime)
@@ -87,7 +85,7 @@ export default class TimetableEditor extends Component {
8785
}
8886
for (let i = 0; i < columns.length; i++) {
8987
let col = columns[i]
90-
if (/TIME/.test(col.type)) {
88+
if (this.isTimeFormat(col.type)) {
9189
// TODO: add default travel/dwell times to new rows
9290
// objectPath.ensureExists(newRow, col.key, 0)
9391
} else {
@@ -101,6 +99,16 @@ export default class TimetableEditor extends Component {
10199
objectPath.set(newRow, 'feedId', this.props.feedSource.id)
102100
objectPath.set(newRow, 'patternId', activePattern.id)
103101
objectPath.set(newRow, 'calendarId', this.props.activeScheduleId)
102+
103+
return newRow
104+
}
105+
addNewRow (columns, blank = false) {
106+
// set blank to true if there are no rows to clone
107+
blank = blank || this.state.data.length === 0
108+
109+
let clone = blank ? null : this.state.data[this.state.data.length - 1]
110+
let newRow = this.constructNewRow(columns, clone)
111+
104112
let newRows = [...this.state.data, newRow]
105113
let stateUpdate = {
106114
data: {$set: newRows},
@@ -229,7 +237,7 @@ export default class TimetableEditor extends Component {
229237
for (var j = 0; j < columns.length; j++) {
230238
let col = columns[j]
231239
let path = `${rowIndexes[i]}.${col.key}`
232-
if (/TIME/.test(col.type)) {
240+
if (this.isTimeFormat(col.type)) {
233241
let currentVal = objectPath.get(newRows, path)
234242
let newVal = currentVal + offsetAmount % 86399 // ensure seconds does not exceed 24 hours
235243
objectPath.set(newRows, path, newVal)
@@ -242,18 +250,33 @@ export default class TimetableEditor extends Component {
242250
}
243251
this.setState(update(this.state, stateUpdate))
244252
}
253+
parseTime (timeString) {
254+
const date = moment().startOf('day').format('YYYY-MM-DD')
255+
return moment(date + 'T' + timeString, DT_FORMATS).diff(date, 'seconds')
256+
}
245257
handlePastedRows (pastedRows, rowIndex, colIndex, columns) {
246258
let newRows = [...this.state.data]
247-
let date = moment().startOf('day').format('YYYY-MM-DD')
248259
let editedRows = []
260+
261+
// iterate over rows in pasted selection
249262
for (var i = 0; i < pastedRows.length; i++) {
250263
editedRows.push(i)
251-
// TODO: fix handlePaste to accommodate new pastedRows objects
264+
265+
// iterate over number of columns in pasted selection
252266
for (var j = 0; j < pastedRows[0].length; j++) {
253267
let path = `${rowIndex + i}.${columns[colIndex + j].key}`
254-
if (typeof newRows[i + rowIndex] !== 'undefined' && typeof objectPath.get(newRows, path) !== 'undefined') {
255-
let newValue = moment(date + 'T' + pastedRows[i][j], ['YYYY-MM-DDTHH:mm:ss', 'YYYY-MM-DDTh:mm:ss a', 'YYYY-MM-DDTh:mm a']).diff(date, 'seconds')
256-
objectPath.set(newRows, path, newValue)
268+
269+
// construct new row if it doesn't exist
270+
if (typeof newRows[i + rowIndex] === 'undefined' || typeof objectPath.get(newRows, path) === 'undefined') {
271+
newRows.push(this.constructNewRow(columns))
272+
}
273+
let newValue = this.parseTime(pastedRows[i][j])
274+
objectPath.set(newRows, path, newValue)
275+
276+
// if departure times are hidden, paste into adjacent time column
277+
let adjacentPath = `${rowIndex + i}.${columns[colIndex + j + 2].key}`
278+
if (this.state.hideDepartureTimes && this.isTimeFormat(columns[colIndex + j].type) && typeof objectPath.get(newRows, adjacentPath) !== 'undefined') {
279+
objectPath.set(newRows, adjacentPath, newValue)
257280
}
258281
}
259282
}
@@ -287,16 +310,19 @@ export default class TimetableEditor extends Component {
287310
this.setState(update(this.state, stateUpdate))
288311
})
289312
}
313+
isTimeFormat (type) {
314+
return /TIME/.test(type)
315+
}
290316
isDataValid (col, value, previousValue) {
291-
if (/TIME/.test(col.type)) {
317+
if (this.isTimeFormat(col.type)) {
292318
return value && value >= 0 && value < previousValue
293319
}
294320
else {
295321
return true
296322
}
297323
}
298324
getCellRenderer (col, value) {
299-
if (!/TIME/.test(col.type)) {
325+
if (!this.isTimeFormat(col.type)) {
300326
return value
301327
}
302328
else {
@@ -642,6 +668,7 @@ export default class TimetableEditor extends Component {
642668
ref={`cell-${rowIndex}-${colIndex}`}
643669
onChange={(value) => {
644670
this.setCellValue(value, rowIndex, `${rowIndex}.${col.key}`)
671+
645672
// set departure time value if departure times are hidden
646673
if (this.state.hideDepartureTimes && columns[colIndex + 1] && columns[colIndex + 1].type === 'DEPARTURE_TIME') {
647674
this.setCellValue(value, rowIndex, `${rowIndex}.${columns[colIndex + 1].key}`)
@@ -655,11 +682,11 @@ export default class TimetableEditor extends Component {
655682
onDown={(evt) => this._onDown(evt, rowIndex, colIndex)}
656683
duplicateLeft={(evt) => this.setCellValue(previousValue, rowIndex, `${rowIndex}.${col.key}`)}
657684
handlePastedRows={(rows) => this.handlePastedRows(rows, rowIndex, colIndex, columns)}
658-
invalidData={/TIME/.test(col.type) && val >= 0 && val < previousValue}
685+
invalidData={this.isTimeFormat(col.type) && val >= 0 && val < previousValue}
659686
isEditing={this.state.activeCell === `${rowIndex}-${colIndex}` }
660687
isFocused={false}
661688
placeholder={col.placeholder}
662-
renderTime={/TIME/.test(col.type)}
689+
renderTime={this.isTimeFormat(col.type)}
663690
cellRenderer={(value) => this.getCellRenderer(col, value)}
664691
data={val}
665692
style={cellStyle}

0 commit comments

Comments
 (0)