@@ -30,7 +30,7 @@ export const coordinateInputValidator = (input: (readonly string[])[]) => {
30
30
if ( / [ 0 - 9 ] \s * [ + / ÷ \- x × ] \s * [ 0 - 9 ] / . test ( value ) ) {
31
31
containsOperator = true ;
32
32
}
33
- const foundBadChars = [ ...value . matchAll ( / [ ^ 0 - 9 + - . e E ] / g) ] ;
33
+ const foundBadChars = [ ...value . matchAll ( / [ ^ 0 - 9 + - . e E ] / g) ] ;
34
34
if ( foundBadChars . length > 0 ) {
35
35
allBadChars . push ( foundBadChars . toString ( ) ) ;
36
36
}
@@ -49,6 +49,41 @@ export const coordinateInputValidator = (input: (readonly string[])[]) => {
49
49
return errors ;
50
50
} ;
51
51
52
+ const coordItemToValue = function ( item : Immutable < CoordinateItemDTO > , index : number ) {
53
+ if ( isDefined ( item . x ) && isDefined ( item . y ) ) {
54
+ // This is an old-style choice, we need to display the x and y properties for indexes 0 and 1.
55
+ return index === 0 ? item . x : ( index === 1 ? item . y : "" ) ;
56
+ }
57
+ return isDefined ( item . coordinates ?. [ index ] ) ? item . coordinates [ index ] : "" ;
58
+ } ;
59
+
60
+ const updateCoordItem = function ( item : Immutable < CoordinateItemDTO > , newValue : string , index : number , numberOfDimensions : number ) {
61
+ let coords ;
62
+ if ( ! item ?. coordinates ?. length ) {
63
+ // Create an array, and backfill with old-style x and y if necessary:
64
+ coords = Array < string > ( numberOfDimensions ) . fill ( "" ) ;
65
+ if ( isDefined ( item . x ) ) {
66
+ coords [ 0 ] = item . x ;
67
+ }
68
+ if ( isDefined ( item . y ) ) {
69
+ coords [ 1 ] = item . y ;
70
+ }
71
+ } else {
72
+ coords = item . coordinates ;
73
+ }
74
+ coords = coords . with ( index , newValue ) ;
75
+ return { ...item , coordinates : coords } ;
76
+ } ;
77
+
78
+ const cleanItem = function ( item : Immutable < CoordinateItemDTO > ) {
79
+ const { x, y, ...cleaned } = item ;
80
+ // Remove x and y from the top-level object, but only discard if coordinates already set, otherwise use to init:
81
+ if ( isDefined ( x ) && isDefined ( y ) && ! isDefined ( cleaned . coordinates ) ) {
82
+ return { ...cleaned , coordinates : [ x , y ] } ;
83
+ }
84
+ return cleaned ;
85
+ } ;
86
+
52
87
const CoordinateInput = ( props : CoordinateInputProps ) => {
53
88
const { value, placeholderValues, numberOfDimensions, onChange, readonly, remove} = props ;
54
89
return < span className = "coordinate-input" > ({ [ ...Array ( numberOfDimensions ) ] . map ( ( _ , i ) =>
@@ -57,9 +92,8 @@ const CoordinateInput = (props: CoordinateInputProps) => {
57
92
type = "text"
58
93
className = "force-print"
59
94
placeholder = { placeholderValues [ i ] ?? "" }
60
- value = { isDefined ( value . coordinates ?. [ i ] ) ? value . coordinates [ i ] : "" }
61
- onChange = { event => onChange ( { ...value , coordinates : value . coordinates && value . coordinates . length ? value . coordinates . with ( i , event . target . value ) :
62
- ( event . target . value === "" ? undefined : Array < string > ( numberOfDimensions ) . fill ( "" ) . with ( i , event . target . value ) ) } ) }
95
+ value = { coordItemToValue ( value , i ) }
96
+ onChange = { event => onChange ( updateCoordItem ( value , event . target . value , i , numberOfDimensions ) ) }
63
97
readOnly = { readonly }
64
98
/>
65
99
{ ( i < numberOfDimensions - 1 ) && < span className = "coordinate-input-separator" > , </ span > }
@@ -81,13 +115,13 @@ const IsaacCoordinateQuestion = ({doc, questionId, readonly}: IsaacQuestionProps
81
115
} , [ dispatchSetCurrentAttempt , currentAttempt ] ) ;
82
116
83
117
const updateItem = useCallback ( ( index : number , value : Immutable < CoordinateItemDTO > ) => {
84
- const items = [ ...( currentAttempt ?. items ?? [ ] ) ] . map ( item => isDefined ( item ) ? item : { ...DEFAULT_COORDINATE_ITEM } ) ;
85
- items [ index ] = value ;
118
+ const items = [ ...( currentAttempt ?. items ?? [ ] ) ] . map ( item => isDefined ( item ) ? cleanItem ( item ) : { ...DEFAULT_COORDINATE_ITEM } ) ;
119
+ items [ index ] = cleanItem ( value ) ;
86
120
dispatchSetCurrentAttempt ( { type : "coordinateChoice" , items} ) ;
87
121
} , [ currentAttempt , dispatchSetCurrentAttempt ] ) ;
88
122
89
123
const removeItem = useCallback ( ( index : number ) => {
90
- const items = [ ...( currentAttempt ?. items ?? [ ] ) ] . map ( item => isDefined ( item ) ? item : { ...DEFAULT_COORDINATE_ITEM } ) ;
124
+ const items = [ ...( currentAttempt ?. items ?? [ ] ) ] . map ( item => isDefined ( item ) ? cleanItem ( item ) : { ...DEFAULT_COORDINATE_ITEM } ) ;
91
125
items . splice ( index , 1 ) ;
92
126
dispatchSetCurrentAttempt ( { type : "coordinateChoice" , items} ) ;
93
127
} , [ currentAttempt , dispatchSetCurrentAttempt ] ) ;
0 commit comments