1
- import React , { type Dispatch , type SetStateAction , useMemo } from 'react' ;
1
+ import React , { useCallback , useEffect , useMemo , useState } from 'react' ;
2
+ import type { Dispatch , SetStateAction } from 'react' ;
2
3
4
+ import { DataSheetGrid , keyColumn , intColumn , floatColumn } from 'react-datasheet-grid' ;
5
+ import 'react-datasheet-grid/dist/style.css' ;
3
6
import { useTranslation } from 'react-i18next' ;
4
- import Spreadsheet , { createEmptyMatrix } from 'react-spreadsheet' ;
5
- import type { CellBase , Matrix } from 'react-spreadsheet' ;
6
7
7
- import type { ConditionalEffortCurveForm , EffortCurveForms } from 'modules/rollingStock/types' ;
8
+ import type {
9
+ ConditionalEffortCurveForm ,
10
+ DataSheetCurve ,
11
+ EffortCurveForms ,
12
+ } from 'modules/rollingStock/types' ;
8
13
import { replaceElementAtIndex } from 'utils/array' ;
9
14
import { msToKmh } from 'utils/physics' ;
10
15
@@ -14,9 +19,9 @@ type CurveSpreadsheetProps = {
14
19
selectedCurve : ConditionalEffortCurveForm ;
15
20
selectedCurveIndex : number ;
16
21
selectedTractionModeCurves : ConditionalEffortCurveForm [ ] ;
17
- effortCurves : EffortCurveForms | null ;
22
+ effortCurves : EffortCurveForms ;
18
23
setEffortCurves : Dispatch < SetStateAction < EffortCurveForms | null > > ;
19
- selectedTractionMode : string | null ;
24
+ selectedTractionMode : string ;
20
25
isDefaultCurve : boolean ;
21
26
} ;
22
27
@@ -30,45 +35,38 @@ const CurveSpreadsheet = ({
30
35
isDefaultCurve,
31
36
} : CurveSpreadsheetProps ) => {
32
37
const { t } = useTranslation ( 'rollingstock' ) ;
38
+ const columns = useMemo (
39
+ ( ) => [
40
+ { ...keyColumn ( 'speed' , intColumn ) , title : t ( 'speed' ) } ,
41
+ { ...keyColumn ( 'effort' , floatColumn ) , title : t ( 'effort' ) } ,
42
+ ] ,
43
+ [ t ]
44
+ ) ;
33
45
34
- const spreadsheetCurve = useMemo ( ( ) => {
35
- const { speeds, max_efforts } = selectedCurve . curve ;
36
- const filledMatrix : (
37
- | {
38
- value : string ;
39
- }
40
- | undefined
41
- ) [ ] [ ] =
42
- speeds && max_efforts
43
- ? max_efforts . map ( ( effort , index ) => [
44
- {
45
- value :
46
- speeds [ index ] !== undefined ? Math . round ( msToKmh ( speeds [ index ] ! ) ) . toString ( ) : '' ,
47
- } ,
48
- // Effort needs to be displayed in kN
49
- { value : effort !== undefined ? Math . round ( effort / 1000 ) . toString ( ) : '' } ,
50
- ] )
51
- : [ ] ;
52
- const numberOfRows = filledMatrix . length < 8 ? 8 - filledMatrix . length : 1 ;
53
- return filledMatrix . concat ( createEmptyMatrix < CellBase < string > > ( numberOfRows , 2 ) ) ;
54
- } , [ selectedCurve ] ) ;
46
+ const [ needsSort , setNeedsSort ] = useState < boolean > ( false ) ;
55
47
56
- const updateRollingStockCurve = ( e : Matrix < { value : string } > ) => {
57
- if ( ! selectedTractionMode || ! effortCurves ) return ;
58
- const formattedCurve = formatCurve ( e ) ;
48
+ const handleBlur = useCallback ( ( ) => {
49
+ setNeedsSort ( true ) ;
50
+ } , [ ] ) ;
59
51
52
+ const updateRollingStockCurve = ( newCurve : DataSheetCurve [ ] ) => {
53
+ // Format the new curve
54
+ const formattedCurve = formatCurve ( newCurve ) ;
55
+
56
+ // Create the updated selected curve
60
57
const updatedSelectedCurve = {
61
58
...selectedCurve ,
62
59
curve : formattedCurve ,
63
60
} ;
64
61
65
- // replace the updated curve
62
+ // Replace the updated curve in the selected traction mode curves
66
63
const updatedCurves = replaceElementAtIndex (
67
64
selectedTractionModeCurves ,
68
65
selectedCurveIndex ,
69
66
updatedSelectedCurve
70
67
) ;
71
68
69
+ // Update the effort curves
72
70
const updatedEffortCurve = {
73
71
...effortCurves ,
74
72
[ selectedTractionMode ] : {
@@ -77,30 +75,56 @@ const CurveSpreadsheet = ({
77
75
...( isDefaultCurve ? { default_curve : formattedCurve } : { } ) ,
78
76
} ,
79
77
} ;
78
+
80
79
setEffortCurves ( updatedEffortCurve ) ;
81
80
} ;
82
81
83
- const orderSpreadsheetValues = ( ) => {
84
- const orderedValuesByVelocity = spreadsheetCurve . sort ( ( a , b ) => {
85
- // if a row has a max_effort, but no speed, it should appear at the top of the table
86
- if ( b [ 0 ] && b [ 0 ] . value === '' ) return 1 ;
87
- return Number ( a [ 0 ] ?. value ) - Number ( b [ 0 ] ?. value ) ;
88
- } ) ;
89
- updateRollingStockCurve ( orderedValuesByVelocity ) ;
90
- } ;
82
+ const spreadsheetCurve = useMemo ( ( ) => {
83
+ const { speeds, max_efforts } = selectedCurve . curve ;
84
+
85
+ const filledDataSheet : DataSheetCurve [ ] = max_efforts . map ( ( effort , index ) => ( {
86
+ speed : speeds [ index ] !== null ? Math . round ( msToKmh ( speeds [ index ] ! ) ) : null ,
87
+ // Effort needs to be displayed in kN
88
+ effort : effort && effort !== null ? effort / 1000 : null ,
89
+ } ) ) ;
90
+
91
+ // Add an empty line for input only if last line is not already empty
92
+ if (
93
+ filledDataSheet . length === 0 ||
94
+ filledDataSheet [ filledDataSheet . length - 1 ] . speed !== null ||
95
+ filledDataSheet [ filledDataSheet . length - 1 ] . effort !== null
96
+ ) {
97
+ filledDataSheet . push ( { speed : null , effort : null } ) ;
98
+ }
99
+
100
+ return filledDataSheet ;
101
+ } , [ selectedCurve ] ) ;
102
+
103
+ useEffect ( ( ) => {
104
+ if ( needsSort ) {
105
+ const sortedSpreadsheetValues = spreadsheetCurve
106
+ . filter ( ( item ) => item . speed !== null || item . effort !== null )
107
+ . sort ( ( a , b ) => {
108
+ if ( a . speed === null ) return - 1 ;
109
+ if ( b . speed === null ) return 1 ;
110
+ return Number ( a . speed ) - Number ( b . speed ) ;
111
+ } ) ;
112
+
113
+ updateRollingStockCurve ( sortedSpreadsheetValues ) ;
114
+ setNeedsSort ( false ) ;
115
+ }
116
+ } , [ needsSort ] ) ;
91
117
92
118
return (
93
119
< div className = "rollingstock-editor-spreadsheet" >
94
- < Spreadsheet
95
- data = { spreadsheetCurve }
96
- onChange = { ( e ) => {
97
- updateRollingStockCurve ( e ) ;
98
- } }
99
- onBlur = { orderSpreadsheetValues }
100
- onKeyDown = { ( e ) => {
101
- if ( e . key === 'Enter' ) orderSpreadsheetValues ( ) ;
102
- } }
103
- columnLabels = { [ t ( 'speed' ) , t ( 'effort' ) ] }
120
+ < DataSheetGrid
121
+ value = { spreadsheetCurve }
122
+ columns = { columns }
123
+ onChange = { ( e ) => updateRollingStockCurve ( e as DataSheetCurve [ ] ) }
124
+ rowHeight = { 30 }
125
+ addRowsComponent = { false }
126
+ onBlur = { handleBlur }
127
+ onSelectionChange = { handleBlur }
104
128
/>
105
129
</ div >
106
130
) ;
0 commit comments