Skip to content

Commit fd8b29a

Browse files
authored
Merge pull request #1251 from isaacphysics/feature/nd-coordinate-qs
Support n-dimensional coord Qs
2 parents 4089610 + 65af241 commit fd8b29a

File tree

2 files changed

+30
-39
lines changed

2 files changed

+30
-39
lines changed

src/IsaacApiTypes.tsx

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -219,8 +219,8 @@ export interface IsaacSymbolicQuestionDTO extends QuestionDTO {
219219

220220
export interface IsaacCoordinateQuestionDTO extends QuestionDTO {
221221
numberOfCoordinates?: number;
222-
placeholderXValue?: string;
223-
placeholderYValue?: string;
222+
numberOfDimensions?: number;
223+
placeholderValues?: string[];
224224
}
225225

226226
export interface IsaacTopicSummaryPageDTO extends SeguePageDTO {
@@ -513,8 +513,7 @@ export interface ParsonsItemDTO extends ItemDTO {
513513
}
514514

515515
export interface CoordinateItemDTO extends ItemDTO {
516-
x?: string;
517-
y?: string;
516+
coordinates?: string[];
518517
}
519518

520519
export interface QuantityDTO extends ChoiceDTO {

src/app/components/content/IsaacCoordinateQuestion.tsx

Lines changed: 27 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -7,18 +7,17 @@ import {IsaacQuestionProps} from "../../../IsaacAppTypes";
77
import {Immutable} from "immer";
88
import QuestionInputValidation from "../elements/inputs/QuestionInputValidation";
99

10-
// Custom input component for coordinates - a pair of inputs, one for x and one for y, formatted with brackets
11-
// and a comma in between.
10+
// Custom input component for coordinates
1211
interface CoordinateInputProps {
1312
value: Immutable<CoordinateItemDTO>;
14-
placeholderXValue?: string;
15-
placeholderYValue?: string;
13+
placeholderValues: string[];
14+
numberOfDimensions: number;
1615
onChange: (value: Immutable<CoordinateItemDTO>) => void;
1716
readonly?: boolean;
1817
remove?: () => void;
1918
}
2019

21-
export const coordinateInputValidator = (input: string[][]) => {
20+
export const coordinateInputValidator = (input: (readonly string[])[]) => {
2221
const errors: string[] = [];
2322
const allBadChars: string[] = [];
2423
let containsComma = false;
@@ -51,32 +50,25 @@ export const coordinateInputValidator = (input: string[][]) => {
5150
};
5251

5352
const CoordinateInput = (props: CoordinateInputProps) => {
54-
const {value, placeholderXValue, placeholderYValue, onChange, readonly, remove} = props;
55-
return <span className="coordinate-input">
56-
(
57-
<Input
58-
type="text"
59-
className="force-print"
60-
placeholder={placeholderXValue ?? "x"}
61-
value={value.x ?? ""}
62-
onChange={event => onChange({...value, x: event.target.value === "" ? undefined : event.target.value})}
63-
readOnly={readonly}
64-
/>
65-
<span className="coordinate-input-separator">,&nbsp;</span>
66-
<Input
67-
type="text"
68-
className="force-print"
69-
placeholder={placeholderYValue ?? "y"}
70-
value={value.y ?? ""}
71-
onChange={event => onChange({...value, y: event.target.value === "" ? undefined : event.target.value})}
72-
readOnly={readonly}
73-
/>
74-
)
75-
{remove && <Button className="ms-3" size="sm" onClick={remove}>Delete</Button>}
53+
const {value, placeholderValues, numberOfDimensions, onChange, readonly, remove} = props;
54+
return <span className="coordinate-input">({[...Array(numberOfDimensions)].map((_, i) =>
55+
<span key={i}>
56+
<Input
57+
type="text"
58+
className="force-print"
59+
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))})}
63+
readOnly={readonly}
64+
/>
65+
{(i < numberOfDimensions - 1) && <span className="coordinate-input-separator">,&nbsp;</span>}
66+
</span>)})
67+
{remove && <Button className="ms-3" size="sm" onClick={remove}>Delete</Button>}
7668
</span>;
7769
};
7870

79-
const DEFAULT_COORDINATE_ITEM = {type: "coordinateItem", x: undefined, y: undefined};
71+
const DEFAULT_COORDINATE_ITEM = {type: "coordinateItem", coordinates: []};
8072

8173
const IsaacCoordinateQuestion = ({doc, questionId, readonly}: IsaacQuestionProps<IsaacCoordinateQuestionDTO>) => {
8274

@@ -110,8 +102,8 @@ const IsaacCoordinateQuestion = ({doc, questionId, readonly}: IsaacQuestionProps
110102
? Array.from({length: doc.numberOfCoordinates}).map((_, index) =>
111103
<CoordinateInput
112104
key={index}
113-
placeholderXValue={doc.placeholderXValue}
114-
placeholderYValue={doc.placeholderYValue}
105+
placeholderValues={doc.placeholderValues ?? []}
106+
numberOfDimensions={doc.numberOfDimensions ?? 1}
115107
value={currentAttempt?.items?.[index] ?? {...DEFAULT_COORDINATE_ITEM}}
116108
readonly={readonly}
117109
onChange={value => updateItem(index, value)}
@@ -121,8 +113,8 @@ const IsaacCoordinateQuestion = ({doc, questionId, readonly}: IsaacQuestionProps
121113
{currentAttempt?.items?.map((item, index) =>
122114
<CoordinateInput
123115
key={index}
124-
placeholderXValue={doc.placeholderXValue}
125-
placeholderYValue={doc.placeholderYValue}
116+
placeholderValues={doc.placeholderValues ?? []}
117+
numberOfDimensions={doc.numberOfDimensions ?? 1}
126118
value={item}
127119
readonly={readonly}
128120
onChange={value => updateItem(index, value)}
@@ -132,14 +124,14 @@ const IsaacCoordinateQuestion = ({doc, questionId, readonly}: IsaacQuestionProps
132124
</>
133125
: <CoordinateInput
134126
key={0}
135-
placeholderXValue={doc.placeholderXValue}
136-
placeholderYValue={doc.placeholderYValue}
127+
placeholderValues={doc.placeholderValues ?? []}
128+
numberOfDimensions={doc.numberOfDimensions ?? 1}
137129
value={{...DEFAULT_COORDINATE_ITEM}}
138130
readonly={readonly}
139131
onChange={value => updateItem(0, value)}
140132
/>
141133
}
142-
<QuestionInputValidation userInput={currentAttempt?.items?.map(answer => [answer.x ?? "", answer.y ?? ""]) ?? []} validator={coordinateInputValidator}/>
134+
<QuestionInputValidation userInput={currentAttempt?.items?.map(answer => answer.coordinates ?? []) ?? []} validator={coordinateInputValidator}/>
143135
{!doc.numberOfCoordinates && <Button color="secondary" size="sm" className="mt-3" onClick={() => updateItem(currentAttempt?.items?.length ?? 1, {...DEFAULT_COORDINATE_ITEM})}>Add coordinate</Button>}
144136
</div>;
145137
};

0 commit comments

Comments
 (0)