@@ -11,6 +11,8 @@ import objectPath from 'object-path'
1111import EditableCell from '../../common/components/EditableCell'
1212import 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+
1416export 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 ( / T I M E / . 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 ( / T I M E / . 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 / T I M E / . test ( type )
315+ }
290316 isDataValid ( col , value , previousValue ) {
291- if ( / T I M E / . 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 ( ! / T I M E / . 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 = { / T I M E / . 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 = { / T I M E / . 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