@windward/games 0.0.1 → 0.0.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/components/content/blocks/dragDrop/BucketGame.vue +4 -4
- package/components/content/blocks/dragDrop/SortingGame.vue +1 -1
- package/components/content/blocks/flashcards/FlashcardSlides.vue +2 -2
- package/components/content/blocks/matchingGame/MatchingGame.vue +2 -2
- package/components/content/blocks/multipleChoice/MultipleChoice.vue +565 -0
- package/components/content/blocks/multipleChoice/QuestionDialog.vue +276 -0
- package/components/content/blocks/quizshowGame/QuizShow.vue +2 -2
- package/components/content/blocks/slideshow/SlideShow.vue +2 -2
- package/components/content/blocks/wordJumble/Jumble.vue +97 -0
- package/components/content/blocks/wordJumble/WordJumble.vue +239 -0
- package/components/settings/MultipleChoiceSettingsManager.vue +290 -0
- package/components/settings/SortingGameSettingsManager.vue +10 -5
- package/components/settings/WordJumbleSettingsManager.vue +293 -0
- package/i18n/en-US/components/content/blocks/index.ts +4 -0
- package/i18n/en-US/components/content/blocks/matching_game.ts +1 -1
- package/i18n/en-US/components/content/blocks/multiple_choice.ts +11 -0
- package/i18n/en-US/components/content/blocks/word_jumble.ts +9 -0
- package/i18n/en-US/components/settings/index.ts +6 -0
- package/i18n/en-US/components/settings/multiple_choice.ts +16 -0
- package/i18n/en-US/components/settings/sorting_game.ts +3 -0
- package/i18n/en-US/components/settings/word_jumble.ts +11 -0
- package/i18n/en-US/shared/content_blocks.ts +2 -0
- package/i18n/en-US/shared/settings.ts +2 -0
- package/package.json +1 -1
- package/plugin.js +42 -0
- package/test/blocks/multipleChoice/MultipleChoice.spec.js +26 -0
- package/test/blocks/multipleChoice/QuestionDialog.spec.js +26 -0
- package/test/blocks/wordJumble/Jumble.spec.js +27 -0
- package/test/blocks/wordJumble/WordJumble.spec.js +24 -0
- package/test/settings/MultipleChoiceGameManager.spec.js +30 -0
- package/test/settings/WordJumbleManager.spec.js +87 -0
|
@@ -1,20 +1,20 @@
|
|
|
1
1
|
<template>
|
|
2
2
|
<div>
|
|
3
3
|
<div>
|
|
4
|
-
<
|
|
4
|
+
<h3 class="title" aria-label="bucket game title" tabindex="0">
|
|
5
5
|
{{ block.metadata.config.title }}
|
|
6
|
-
</
|
|
6
|
+
</h3>
|
|
7
7
|
|
|
8
8
|
<p tabindex="0">
|
|
9
9
|
{{ block.metadata.config.instructions }}
|
|
10
10
|
</p>
|
|
11
|
-
<
|
|
11
|
+
<h4 v-if="!render" class="d-flex justify-center align-center">
|
|
12
12
|
{{
|
|
13
13
|
$t(
|
|
14
14
|
'plugin.games.components.content.blocks.bucket_game.cannot'
|
|
15
15
|
)
|
|
16
16
|
}}
|
|
17
|
-
</
|
|
17
|
+
</h4>
|
|
18
18
|
<v-container fluid :class="status" class="pa-0">
|
|
19
19
|
<br />
|
|
20
20
|
<v-row>
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
<template>
|
|
2
2
|
<div class="background">
|
|
3
3
|
<div>
|
|
4
|
-
<
|
|
4
|
+
<h3
|
|
5
5
|
:aria-label="
|
|
6
6
|
$t(
|
|
7
7
|
'plugin.games.components.content.blocks.matching_game.match_game_title'
|
|
@@ -10,7 +10,7 @@
|
|
|
10
10
|
tabindex="0"
|
|
11
11
|
>
|
|
12
12
|
{{ block.metadata.config.title }}
|
|
13
|
-
</
|
|
13
|
+
</h3>
|
|
14
14
|
|
|
15
15
|
<p tabindex="0">
|
|
16
16
|
{{ block.metadata.config.instructions }}
|
|
@@ -0,0 +1,565 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<v-container>
|
|
3
|
+
<v-row>
|
|
4
|
+
<v-col>
|
|
5
|
+
<h3>{{ block.metadata.config.title }}</h3>
|
|
6
|
+
<p>{{ block.metadata.config.instructions }}</p>
|
|
7
|
+
</v-col>
|
|
8
|
+
</v-row>
|
|
9
|
+
<v-row>
|
|
10
|
+
<v-col class="pa-0">
|
|
11
|
+
<v-carousel height="600">
|
|
12
|
+
<v-container>
|
|
13
|
+
<v-carousel-item
|
|
14
|
+
v-for="(question, index) in block.metadata.config
|
|
15
|
+
.questions"
|
|
16
|
+
:key="index"
|
|
17
|
+
>
|
|
18
|
+
<v-col class="d-flex justify-center">
|
|
19
|
+
<p class="questionBody">{{ question.body }}</p>
|
|
20
|
+
</v-col>
|
|
21
|
+
<v-container :key="updateKey">
|
|
22
|
+
<v-row
|
|
23
|
+
v-for="(
|
|
24
|
+
answer, answerIndex
|
|
25
|
+
) in question.answer_options"
|
|
26
|
+
:key="answerIndex"
|
|
27
|
+
class="mb-2"
|
|
28
|
+
>
|
|
29
|
+
<v-col
|
|
30
|
+
cols="12"
|
|
31
|
+
md="2"
|
|
32
|
+
class="d-flex justify-end"
|
|
33
|
+
>
|
|
34
|
+
<v-icon
|
|
35
|
+
class="isCorrect"
|
|
36
|
+
v-if="
|
|
37
|
+
studentResponses[index] &&
|
|
38
|
+
answer.correctAnswer === true &&
|
|
39
|
+
studentResponses[index]
|
|
40
|
+
.answer_options[answerIndex]
|
|
41
|
+
.chosen === true
|
|
42
|
+
"
|
|
43
|
+
>mdi-check-circle</v-icon
|
|
44
|
+
>
|
|
45
|
+
<v-icon
|
|
46
|
+
class="isCorrect"
|
|
47
|
+
v-if="
|
|
48
|
+
studentResponses[index] &&
|
|
49
|
+
answer.correctAnswer === true &&
|
|
50
|
+
studentResponses[index]
|
|
51
|
+
.answer_options[answerIndex]
|
|
52
|
+
.chosen !== true
|
|
53
|
+
"
|
|
54
|
+
>mdi-check-circle-outline</v-icon
|
|
55
|
+
>
|
|
56
|
+
<v-icon
|
|
57
|
+
class="isIncorrect"
|
|
58
|
+
v-if="
|
|
59
|
+
studentResponses[index] &&
|
|
60
|
+
answer.correctAnswer !== true &&
|
|
61
|
+
studentResponses[index]
|
|
62
|
+
.answer_options[answerIndex]
|
|
63
|
+
.chosen === true
|
|
64
|
+
"
|
|
65
|
+
>mdi-alpha-x-circle</v-icon
|
|
66
|
+
>
|
|
67
|
+
</v-col>
|
|
68
|
+
<v-col cols="12" md="8" class="pa-0">
|
|
69
|
+
<v-card
|
|
70
|
+
class="optionOutline pa-2"
|
|
71
|
+
:disabled="answer.disabled"
|
|
72
|
+
:class="
|
|
73
|
+
onIsThisCorrect(answer, index)
|
|
74
|
+
"
|
|
75
|
+
outlined
|
|
76
|
+
tile
|
|
77
|
+
@click="
|
|
78
|
+
onChooseAnswer(answer, index)
|
|
79
|
+
"
|
|
80
|
+
>
|
|
81
|
+
<div
|
|
82
|
+
class="d-flex justify-space-between"
|
|
83
|
+
>
|
|
84
|
+
<p class="mb-0">
|
|
85
|
+
{{
|
|
86
|
+
getCharacter(
|
|
87
|
+
question,
|
|
88
|
+
answer
|
|
89
|
+
) +
|
|
90
|
+
': ' +
|
|
91
|
+
answer.value
|
|
92
|
+
}}
|
|
93
|
+
</p>
|
|
94
|
+
<div>
|
|
95
|
+
<v-icon
|
|
96
|
+
class="isCorrect"
|
|
97
|
+
v-if="
|
|
98
|
+
studentResponses[
|
|
99
|
+
index
|
|
100
|
+
] &&
|
|
101
|
+
answer.correctAnswer ===
|
|
102
|
+
true &&
|
|
103
|
+
studentResponses[
|
|
104
|
+
index
|
|
105
|
+
].answer_options[
|
|
106
|
+
answerIndex
|
|
107
|
+
].chosen !== true
|
|
108
|
+
"
|
|
109
|
+
@click="
|
|
110
|
+
onAnswerDescription(
|
|
111
|
+
question
|
|
112
|
+
)
|
|
113
|
+
"
|
|
114
|
+
>mdi-information</v-icon
|
|
115
|
+
>
|
|
116
|
+
<v-icon
|
|
117
|
+
right
|
|
118
|
+
dark
|
|
119
|
+
nontranslate
|
|
120
|
+
v-if="
|
|
121
|
+
studentResponses[
|
|
122
|
+
index
|
|
123
|
+
] &&
|
|
124
|
+
answer.correctAnswer ==
|
|
125
|
+
true &&
|
|
126
|
+
studentResponses[
|
|
127
|
+
index
|
|
128
|
+
].answer_options[
|
|
129
|
+
answerIndex
|
|
130
|
+
].chosen === true
|
|
131
|
+
"
|
|
132
|
+
@click="
|
|
133
|
+
onAnswerDescription(
|
|
134
|
+
question
|
|
135
|
+
)
|
|
136
|
+
"
|
|
137
|
+
>
|
|
138
|
+
mdi-information-outline
|
|
139
|
+
</v-icon>
|
|
140
|
+
</div>
|
|
141
|
+
</div>
|
|
142
|
+
</v-card>
|
|
143
|
+
</v-col>
|
|
144
|
+
<v-col cols="12" md="2"></v-col>
|
|
145
|
+
</v-row>
|
|
146
|
+
</v-container>
|
|
147
|
+
<v-col cols="12" md="12">
|
|
148
|
+
<v-row class="d-flex justify-center">
|
|
149
|
+
<v-btn
|
|
150
|
+
color="primary"
|
|
151
|
+
outlined
|
|
152
|
+
class="mr-4 hintButton"
|
|
153
|
+
@click="onHint(question)"
|
|
154
|
+
>{{
|
|
155
|
+
$t(
|
|
156
|
+
'plugin.games.components.content.blocks.multiple_choice.hint'
|
|
157
|
+
)
|
|
158
|
+
}}</v-btn
|
|
159
|
+
>
|
|
160
|
+
<v-btn
|
|
161
|
+
color="primary fiftyButton"
|
|
162
|
+
outlined
|
|
163
|
+
:disabled="
|
|
164
|
+
question.answer_options.length !==
|
|
165
|
+
4 || question.fiftyFifty
|
|
166
|
+
"
|
|
167
|
+
class="ml-4 fiftyButton"
|
|
168
|
+
@click="onFifty(question)"
|
|
169
|
+
>{{
|
|
170
|
+
$t(
|
|
171
|
+
'plugin.games.components.content.blocks.multiple_choice.fifty'
|
|
172
|
+
)
|
|
173
|
+
}}</v-btn
|
|
174
|
+
>
|
|
175
|
+
</v-row>
|
|
176
|
+
</v-col>
|
|
177
|
+
<v-layout class="mt-2">
|
|
178
|
+
<v-flex xs4></v-flex>
|
|
179
|
+
<v-flex xs4>
|
|
180
|
+
<v-col align="center" tabindex="0">
|
|
181
|
+
{{
|
|
182
|
+
$t(
|
|
183
|
+
'plugin.games.components.content.blocks.matching_game.of_complete_text_area',
|
|
184
|
+
[
|
|
185
|
+
completedAmount,
|
|
186
|
+
totalAmountQuestions,
|
|
187
|
+
]
|
|
188
|
+
)
|
|
189
|
+
}}
|
|
190
|
+
<v-progress-linear
|
|
191
|
+
color="primary"
|
|
192
|
+
outlined
|
|
193
|
+
v-model="completedPercent"
|
|
194
|
+
rounded
|
|
195
|
+
height="15"
|
|
196
|
+
></v-progress-linear>
|
|
197
|
+
</v-col>
|
|
198
|
+
</v-flex>
|
|
199
|
+
<v-flex xs4></v-flex>
|
|
200
|
+
</v-layout>
|
|
201
|
+
<v-layout class="mt-2" v-if="updateTotals !== 1">
|
|
202
|
+
<v-flex xs8></v-flex>
|
|
203
|
+
<v-flex xs4>
|
|
204
|
+
<v-col :key="updateTotals">
|
|
205
|
+
<p>
|
|
206
|
+
{{
|
|
207
|
+
$t(
|
|
208
|
+
'plugin.games.components.content.blocks.multiple_choice.total_points'
|
|
209
|
+
)
|
|
210
|
+
}}
|
|
211
|
+
</p>
|
|
212
|
+
<p>
|
|
213
|
+
{{ studentAmountCorrect }}
|
|
214
|
+
{{
|
|
215
|
+
$t(
|
|
216
|
+
'plugin.games.components.content.blocks.multiple_choice.out_of'
|
|
217
|
+
)
|
|
218
|
+
}}
|
|
219
|
+
{{ totalQuestionsMultipleChoice }}
|
|
220
|
+
</p>
|
|
221
|
+
</v-col>
|
|
222
|
+
</v-flex>
|
|
223
|
+
</v-layout>
|
|
224
|
+
</v-carousel-item>
|
|
225
|
+
<Dialog
|
|
226
|
+
v-model="dialog"
|
|
227
|
+
:trigger="false"
|
|
228
|
+
@click:outside="close"
|
|
229
|
+
@click:close="close"
|
|
230
|
+
@keydown.esc="close"
|
|
231
|
+
>
|
|
232
|
+
<template #title>
|
|
233
|
+
<div v-if="hint">
|
|
234
|
+
{{
|
|
235
|
+
$t(
|
|
236
|
+
'plugin.games.components.content.blocks.multiple_choice.hint_title'
|
|
237
|
+
)
|
|
238
|
+
}}
|
|
239
|
+
</div>
|
|
240
|
+
<div v-if="answerDescriptionModal">
|
|
241
|
+
{{
|
|
242
|
+
$t(
|
|
243
|
+
'plugin.games.components.content.blocks.multiple_choice.answer_description'
|
|
244
|
+
)
|
|
245
|
+
}}
|
|
246
|
+
</div>
|
|
247
|
+
</template>
|
|
248
|
+
<template #form="{ on, attrs }">
|
|
249
|
+
<div v-bind="attrs" v-on="on">
|
|
250
|
+
<div v-if="hint">
|
|
251
|
+
{{ hintText }}
|
|
252
|
+
</div>
|
|
253
|
+
<div v-if="answerDescriptionModal">
|
|
254
|
+
{{ answerDescription }}
|
|
255
|
+
</div>
|
|
256
|
+
</div>
|
|
257
|
+
</template>
|
|
258
|
+
</Dialog>
|
|
259
|
+
</v-container>
|
|
260
|
+
</v-carousel>
|
|
261
|
+
</v-col>
|
|
262
|
+
</v-row>
|
|
263
|
+
</v-container>
|
|
264
|
+
</template>
|
|
265
|
+
<script>
|
|
266
|
+
import BaseContentBlock from '~/components/Content/Blocks/BaseContentBlock'
|
|
267
|
+
import Dialog from '~/components/Dialog.vue'
|
|
268
|
+
import _ from 'lodash'
|
|
269
|
+
import Crypto from '~/helpers/Crypto'
|
|
270
|
+
import { mapGetters } from 'vuex'
|
|
271
|
+
import UserContentBlockState from '~/models/UserContentBlockState'
|
|
272
|
+
import ContentBlock from '~/models/ContentBlock'
|
|
273
|
+
import Course from '~/models/Course'
|
|
274
|
+
|
|
275
|
+
export default {
|
|
276
|
+
name: 'MultipleChoice',
|
|
277
|
+
extends: BaseContentBlock,
|
|
278
|
+
components: { Dialog },
|
|
279
|
+
beforeMount() {
|
|
280
|
+
if (_.isEmpty(this.block)) {
|
|
281
|
+
this.block = {}
|
|
282
|
+
}
|
|
283
|
+
if (_.isEmpty(this.block.metadata)) {
|
|
284
|
+
this.block.metadata = {}
|
|
285
|
+
}
|
|
286
|
+
if (_.isEmpty(this.block.metadata.config)) {
|
|
287
|
+
this.block.metadata.config = {}
|
|
288
|
+
}
|
|
289
|
+
if (_.isEmpty(this.block.metadata.config.title)) {
|
|
290
|
+
this.block.metadata.config.title = ''
|
|
291
|
+
}
|
|
292
|
+
if (_.isEmpty(this.block.metadata.config.instructions)) {
|
|
293
|
+
this.block.metadata.config.instructions = ''
|
|
294
|
+
}
|
|
295
|
+
if (_.isEmpty(this.block.metadata.config.questions)) {
|
|
296
|
+
this.block.metadata.config.questions = []
|
|
297
|
+
}
|
|
298
|
+
this.updateTotals = 1
|
|
299
|
+
},
|
|
300
|
+
data() {
|
|
301
|
+
return {
|
|
302
|
+
updateKey: 0,
|
|
303
|
+
completedItems: [],
|
|
304
|
+
dialog: false,
|
|
305
|
+
hint: false,
|
|
306
|
+
hintText: '',
|
|
307
|
+
answerDescriptionModal: false,
|
|
308
|
+
answerDescription: '',
|
|
309
|
+
studentAmountCorrect: null,
|
|
310
|
+
totalQuestionsMultipleChoice: null,
|
|
311
|
+
updateTotals: 1,
|
|
312
|
+
studentResponses: [],
|
|
313
|
+
}
|
|
314
|
+
},
|
|
315
|
+
computed: {
|
|
316
|
+
...mapGetters({
|
|
317
|
+
organization: 'organization/get',
|
|
318
|
+
course: 'course/get',
|
|
319
|
+
}),
|
|
320
|
+
completedAmount() {
|
|
321
|
+
if (this.completedItems) {
|
|
322
|
+
return _.flatten(this.completedItems).length
|
|
323
|
+
}
|
|
324
|
+
},
|
|
325
|
+
totalAmountQuestions() {
|
|
326
|
+
if (this.block.metadata.config.questions) {
|
|
327
|
+
return _.flatten(this.block.metadata.config.questions).length
|
|
328
|
+
}
|
|
329
|
+
},
|
|
330
|
+
completedPercent() {
|
|
331
|
+
if (
|
|
332
|
+
this.block.metadata.config.questions.length > 0 &&
|
|
333
|
+
this.completedItems.length > 0
|
|
334
|
+
) {
|
|
335
|
+
return (this.completedAmount / this.totalAmountQuestions) * 100
|
|
336
|
+
}
|
|
337
|
+
return 0
|
|
338
|
+
},
|
|
339
|
+
},
|
|
340
|
+
mounted() {
|
|
341
|
+
this.studentAmountCorrect = 0
|
|
342
|
+
this.onAmountCorrect()
|
|
343
|
+
this.onTotalAmountOfQuestions()
|
|
344
|
+
},
|
|
345
|
+
methods: {
|
|
346
|
+
async onAmountCorrect() {
|
|
347
|
+
let correct = 0
|
|
348
|
+
// grabs user state to get the total amount of questions a studentanswered correctly
|
|
349
|
+
let userState = await UserContentBlockState.where({
|
|
350
|
+
'metadata->block->tag': 'plugin-games-multiple-choice',
|
|
351
|
+
}).get()
|
|
352
|
+
userState.forEach((state) => {
|
|
353
|
+
state.metadata.studentResponses.forEach((element) => {
|
|
354
|
+
if (element.isStudentCorrect === true) {
|
|
355
|
+
correct = correct + 1
|
|
356
|
+
}
|
|
357
|
+
})
|
|
358
|
+
})
|
|
359
|
+
|
|
360
|
+
// sets total
|
|
361
|
+
this.studentAmountCorrect = correct
|
|
362
|
+
// updates display area of totals
|
|
363
|
+
this.updateTotals = Crypto.id()
|
|
364
|
+
this.updateKey = Crypto.id()
|
|
365
|
+
},
|
|
366
|
+
async onTotalAmountOfQuestions() {
|
|
367
|
+
let multipleChoiceTotalQuestions = 0
|
|
368
|
+
// get total amount of questions via content block route
|
|
369
|
+
let multipleChoiceBlocks = await ContentBlock.where(
|
|
370
|
+
'tag',
|
|
371
|
+
'plugin-games-multiple-choice'
|
|
372
|
+
)
|
|
373
|
+
.for(new Course({ id: this.course.id }))
|
|
374
|
+
.get()
|
|
375
|
+
|
|
376
|
+
multipleChoiceBlocks.forEach((element) => {
|
|
377
|
+
element.block.metadata.config.questions.forEach((question) => {
|
|
378
|
+
multipleChoiceTotalQuestions =
|
|
379
|
+
multipleChoiceTotalQuestions + 1
|
|
380
|
+
})
|
|
381
|
+
})
|
|
382
|
+
|
|
383
|
+
this.totalQuestionsMultipleChoice = multipleChoiceTotalQuestions
|
|
384
|
+
},
|
|
385
|
+
onAnswerDescription(question) {
|
|
386
|
+
//launches modal and displays anwer description
|
|
387
|
+
this.dialog = true
|
|
388
|
+
this.answerDescriptionModal = true
|
|
389
|
+
this.answerDescription = question.answer_description
|
|
390
|
+
},
|
|
391
|
+
onChooseAnswer(answer, questionIndex) {
|
|
392
|
+
const answerIndex =
|
|
393
|
+
this.block.metadata.config.questions[
|
|
394
|
+
questionIndex
|
|
395
|
+
].answer_options.indexOf(answer)
|
|
396
|
+
// set answer,chosen and student_response on block state
|
|
397
|
+
if (_.isEmpty(this.studentResponses[questionIndex])) {
|
|
398
|
+
// clones question block into the correct index in the studentResponses araay
|
|
399
|
+
this.studentResponses[questionIndex] = _.cloneDeep(
|
|
400
|
+
this.block.metadata.config.questions[questionIndex]
|
|
401
|
+
)
|
|
402
|
+
// lets html side know whish answer was chosen, this is important in determine css
|
|
403
|
+
// used on the displayed side
|
|
404
|
+
this.studentResponses[questionIndex].answer_options[
|
|
405
|
+
answerIndex
|
|
406
|
+
].chosen = true
|
|
407
|
+
this.studentResponses[questionIndex].student_response = answer
|
|
408
|
+
|
|
409
|
+
// get question by index
|
|
410
|
+
const question =
|
|
411
|
+
this.block.metadata.config.questions[questionIndex]
|
|
412
|
+
// ensure the object is not already in the completed items array if not push into the array
|
|
413
|
+
const questionDone = this.completedItems.find(
|
|
414
|
+
(item) => item === question
|
|
415
|
+
)
|
|
416
|
+
|
|
417
|
+
if (!questionDone) {
|
|
418
|
+
this.completedItems.push(question)
|
|
419
|
+
}
|
|
420
|
+
if (answer.correctAnswer === true) {
|
|
421
|
+
this.studentAmountCorrect = this.studentAmountCorrect + 1
|
|
422
|
+
this.studentResponses[questionIndex].isStudentCorrect = true
|
|
423
|
+
}
|
|
424
|
+
|
|
425
|
+
this.updateKey = Crypto.id()
|
|
426
|
+
}
|
|
427
|
+
},
|
|
428
|
+
onIsThisCorrect(answer, questionIndex) {
|
|
429
|
+
// check if student already responded
|
|
430
|
+
if (
|
|
431
|
+
!_.isEmpty(
|
|
432
|
+
this.studentResponses[questionIndex] &&
|
|
433
|
+
this.studentResponses[questionIndex].student_response
|
|
434
|
+
)
|
|
435
|
+
) {
|
|
436
|
+
// checks if answer is correct and applies class
|
|
437
|
+
if (
|
|
438
|
+
this.studentResponses[questionIndex].student_response
|
|
439
|
+
.correctAnswer === true
|
|
440
|
+
) {
|
|
441
|
+
if (answer.correctAnswer === true) {
|
|
442
|
+
return 'studentAnswerCorrect'
|
|
443
|
+
} else {
|
|
444
|
+
return ''
|
|
445
|
+
}
|
|
446
|
+
} else if (
|
|
447
|
+
this.studentResponses[questionIndex].student_response
|
|
448
|
+
.correctAnswer !== true
|
|
449
|
+
) {
|
|
450
|
+
if (answer.correctAnswer === true) {
|
|
451
|
+
return 'correctBorder'
|
|
452
|
+
} else if (
|
|
453
|
+
this.studentResponses[questionIndex].student_response
|
|
454
|
+
.correctAnswer !== true
|
|
455
|
+
) {
|
|
456
|
+
return 'incorrectBorder'
|
|
457
|
+
}
|
|
458
|
+
}
|
|
459
|
+
}
|
|
460
|
+
},
|
|
461
|
+
getCharacter(question, answer) {
|
|
462
|
+
//gets a letter to preface each answer option
|
|
463
|
+
const letters = ['A', 'B', 'C', 'D']
|
|
464
|
+
const questionIndex =
|
|
465
|
+
this.block.metadata.config.questions.indexOf(question)
|
|
466
|
+
const answerIndex =
|
|
467
|
+
this.block.metadata.config.questions[
|
|
468
|
+
questionIndex
|
|
469
|
+
].answer_options.indexOf(answer)
|
|
470
|
+
return letters[answerIndex]
|
|
471
|
+
},
|
|
472
|
+
onHint(question) {
|
|
473
|
+
// launches dialog modal
|
|
474
|
+
this.dialog = true
|
|
475
|
+
this.hint = true
|
|
476
|
+
// sets text for modal, cannot just reference question.hint in html because
|
|
477
|
+
// the carousel loads all elements regardless of if they are on a different slide or not
|
|
478
|
+
// when mounted so it always shows the last question if referenced question.hint
|
|
479
|
+
this.hintText = question.hint
|
|
480
|
+
},
|
|
481
|
+
onFifty(question) {
|
|
482
|
+
// disables button so user can't click twice
|
|
483
|
+
question.fiftyFifty = true
|
|
484
|
+
// shuffles array so that the first too answers that are incorrect are not the ones disabled every time
|
|
485
|
+
const shuffledArray = this.shuffle(question.answer_options)
|
|
486
|
+
let counter = 0
|
|
487
|
+
shuffledArray.forEach((element) => {
|
|
488
|
+
if (element.correctAnswer !== true && counter < 2) {
|
|
489
|
+
counter = counter + 1
|
|
490
|
+
element.disabled = true
|
|
491
|
+
}
|
|
492
|
+
})
|
|
493
|
+
// resort array back in order based on id
|
|
494
|
+
shuffledArray.sort((a, b) => a.id - b.id)
|
|
495
|
+
this.updateKey = Crypto.id()
|
|
496
|
+
},
|
|
497
|
+
shuffle(array) {
|
|
498
|
+
let currentIndex = array.length,
|
|
499
|
+
randomIndex
|
|
500
|
+
|
|
501
|
+
// While there remain elements to shuffle.
|
|
502
|
+
while (currentIndex != 0) {
|
|
503
|
+
// Pick a remaining element.
|
|
504
|
+
randomIndex = Math.floor(Math.random() * currentIndex)
|
|
505
|
+
currentIndex--
|
|
506
|
+
|
|
507
|
+
// And swap it with the current element.
|
|
508
|
+
;[array[currentIndex], array[randomIndex]] = [
|
|
509
|
+
array[randomIndex],
|
|
510
|
+
array[currentIndex],
|
|
511
|
+
]
|
|
512
|
+
}
|
|
513
|
+
|
|
514
|
+
return array
|
|
515
|
+
},
|
|
516
|
+
close() {
|
|
517
|
+
this.dialog = false
|
|
518
|
+
this.hint = false
|
|
519
|
+
this.answerDescriptionModal = false
|
|
520
|
+
this.hintText = ''
|
|
521
|
+
this.answerDescription = ''
|
|
522
|
+
},
|
|
523
|
+
},
|
|
524
|
+
}
|
|
525
|
+
</script>
|
|
526
|
+
<style scoped>
|
|
527
|
+
.questionBody {
|
|
528
|
+
font-size: 18px;
|
|
529
|
+
}
|
|
530
|
+
.optionOutline {
|
|
531
|
+
border: 2px solid var(--v-primary-base);
|
|
532
|
+
border-radius: 3px;
|
|
533
|
+
}
|
|
534
|
+
.correctBorder {
|
|
535
|
+
border: 3px solid var(--v-success-base);
|
|
536
|
+
}
|
|
537
|
+
.incorrectBorder {
|
|
538
|
+
background-color: var(--v-error-base);
|
|
539
|
+
border: 2px solid var(--v-error-base);
|
|
540
|
+
color: white;
|
|
541
|
+
}
|
|
542
|
+
.studentAnswerCorrect {
|
|
543
|
+
background-color: var(--v-success-base);
|
|
544
|
+
color: white;
|
|
545
|
+
}
|
|
546
|
+
.isCorrect {
|
|
547
|
+
color: var(--v-success-base);
|
|
548
|
+
}
|
|
549
|
+
.isIncorrect {
|
|
550
|
+
color: var(--v-error-base);
|
|
551
|
+
}
|
|
552
|
+
.iconStudentCorrect {
|
|
553
|
+
color: var(--v-success-base);
|
|
554
|
+
border-radius: 25px;
|
|
555
|
+
}
|
|
556
|
+
/* need important below to override vuetify preset width for buttons */
|
|
557
|
+
.hintButton {
|
|
558
|
+
min-width: 20% !important;
|
|
559
|
+
margin-right: 16px;
|
|
560
|
+
}
|
|
561
|
+
.fiftyButton {
|
|
562
|
+
min-width: 20% !important;
|
|
563
|
+
margin-right: 16px;
|
|
564
|
+
}
|
|
565
|
+
</style>
|