@windward/games 0.17.1 → 0.19.0

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.
Files changed (34) hide show
  1. package/CHANGELOG.md +5 -13
  2. package/components/content/blocks/crosswordPuzzle/CrosswordPuzzle.vue +11 -2
  3. package/components/content/blocks/dragDrop/BucketGame.vue +7 -1
  4. package/components/content/blocks/dragDrop/SortingGame.vue +10 -1
  5. package/components/content/blocks/flashcards/CardFace.vue +15 -11
  6. package/components/content/blocks/flashcards/Flashcard.vue +0 -3
  7. package/components/content/blocks/flashcards/FlashcardSlides.vue +54 -6
  8. package/components/content/blocks/matchingGame/MatchingGame.vue +7 -1
  9. package/components/content/blocks/multipleChoice/MultipleChoice.vue +34 -19
  10. package/components/content/blocks/multipleChoice/QuestionDialog.vue +335 -112
  11. package/components/content/blocks/quizshowGame/QuizShow.vue +7 -0
  12. package/components/content/blocks/sevenStrikes/SevenStikes.vue +9 -1
  13. package/components/content/blocks/slideshow/SlideShow.vue +7 -2
  14. package/components/content/blocks/wordJumble/WordJumble.vue +9 -1
  15. package/components/settings/BucketGameSettingsManager.vue +151 -28
  16. package/components/settings/CrosswordPuzzleSettingsManager.vue +8 -29
  17. package/components/settings/FlashCardSlidesManager.vue +8 -27
  18. package/components/settings/MatchingGameManager.vue +12 -29
  19. package/components/settings/MultipleChoiceSettingsManager.vue +37 -30
  20. package/components/settings/QuizShowSettingsManager.vue +11 -28
  21. package/components/settings/SevenStrikesSettingsManager.vue +13 -29
  22. package/components/settings/SlideShowManager.vue +8 -26
  23. package/components/settings/SortingGameSettingsManager.vue +8 -29
  24. package/components/settings/WordJumbleSettingsManager.vue +8 -28
  25. package/i18n/en-US/components/content/blocks/flashcard.ts +1 -0
  26. package/i18n/en-US/components/settings/bucket_game.ts +5 -0
  27. package/i18n/en-US/components/settings/multiple_choice.ts +4 -3
  28. package/i18n/es-ES/components/settings/bucket_game.ts +5 -0
  29. package/i18n/es-ES/components/settings/multiple_choice.ts +2 -3
  30. package/i18n/sv-SE/components/settings/bucket_game.ts +5 -0
  31. package/i18n/sv-SE/components/settings/multiple_choice.ts +2 -2
  32. package/package.json +1 -1
  33. package/test/blocks/crossword/CrosswordPuzzle.spec.js +0 -21
  34. package/test/blocks/dragDrop/SortingGame.spec.js +0 -21
@@ -1,20 +1,40 @@
1
1
  <template>
2
- <v-form ref="form" v-model="formValid" :key="updateKey">
3
- <v-row>
4
- <v-col cols="12">
5
- <v-textarea
6
- v-model="question.body"
7
- :label="
2
+ <v-container>
3
+ <v-row :key="updateKey">
4
+ <v-col xl="12" lg="12" md="12" sm="12" cols="12">
5
+ <p class="font-weight-bold p-bold-text">
6
+ {{
8
7
  $t(
9
- 'windward.games.components.settings.multiple_choice.question'
8
+ 'components.content.blocks.assessment.questions.question'
10
9
  )
11
- "
12
- :autofocus="true"
13
- rows="2"
14
- :rules="$Validation.getRule('exists')"
15
- prepend-inner-icon="mdi-help"
16
- :disabled="disabled"
17
- ></v-textarea>
10
+ }}
11
+ </p>
12
+ <TextEditor
13
+ id="question-body"
14
+ :key="'question-body-' + updateKey"
15
+ v-model="question.body"
16
+ :height="200"
17
+ lazy-load
18
+ @input="onCheckValidation"
19
+ ></TextEditor>
20
+ </v-col>
21
+ <v-col xl="3" lg="3" md="4" sm="12" cols="12" v-if="false">
22
+ <GenerateAIQuestionButton
23
+ :course="course"
24
+ :content="content"
25
+ :block="block"
26
+ :question-type="questionType"
27
+ @click:generate="onQuestionGenerated"
28
+ ></GenerateAIQuestionButton>
29
+ </v-col>
30
+ <v-col cols="12">
31
+ <ImageAssetSettings
32
+ v-model="question.question_metadata.body_asset"
33
+ :assets.sync="question.assets"
34
+ hide-background
35
+ hide-decorative
36
+ hide-modal
37
+ ></ImageAssetSettings>
18
38
  <v-textarea
19
39
  v-model="question.hint"
20
40
  :label="
@@ -22,120 +42,233 @@
22
42
  'windward.games.components.settings.multiple_choice.question_hint'
23
43
  )
24
44
  "
45
+ outlined
25
46
  rows="2"
26
- :rules="$Validation.getRule('exists')"
27
47
  prepend-inner-icon="mdi-lightbulb-on-10"
28
48
  :disabled="disabled"
29
49
  ></v-textarea>
30
- <v-textarea
31
- v-model="question.answer_description"
32
- :label="
33
- $t(
34
- 'windward.games.components.settings.multiple_choice.answer_feedback'
35
- )
36
- "
37
- rows="2"
38
- :rules="$Validation.getRule('exists')"
39
- prepend-inner-icon="mdi-comment"
40
- :disabled="disabled"
41
- ></v-textarea>
42
- <br />
50
+ </v-col>
51
+ <br />
52
+ <v-col cols="12">
43
53
  <h4 class="pb-2">
44
54
  {{
45
55
  $t(
46
- 'windward.games.components.settings.multiple_choice.answer_options'
56
+ 'windward.games.components.settings.multiple_choice.answer'
47
57
  )
48
58
  }}
49
59
  </h4>
50
- <p>
60
+ <v-radio-group
61
+ :key="updateKey"
62
+ v-model="question.correctAnswer"
63
+ :rules="validation.optionRules"
64
+ >
65
+ <draggable
66
+ :list="question.answer_options"
67
+ tag="tbody"
68
+ v-bind="dragOptions"
69
+ handle=".question-drag-handle"
70
+ @change="onDragChange"
71
+ >
72
+ <div
73
+ v-for="(answer, index) in question.answer_options"
74
+ :key="index"
75
+ >
76
+ <v-alert outlined elevation="2">
77
+ <v-row>
78
+ <v-col
79
+ cols="1"
80
+ lg="1"
81
+ md="1"
82
+ class="d-flex justify-center align-center pa-0"
83
+ >
84
+ <v-btn
85
+ elevation="0"
86
+ text
87
+ class="question-drag-handle"
88
+ >
89
+ <v-icon>mdi-drag-vertical</v-icon>
90
+ </v-btn>
91
+ </v-col>
92
+ <v-col
93
+ cols="10"
94
+ lg="10"
95
+ md="10"
96
+ class="pl-0 pr-0"
97
+ >
98
+ <v-row>
99
+ <v-col cols="12">
100
+ <v-radio
101
+ color="success"
102
+ :value="answer.id"
103
+ :disabled="disabled"
104
+ v-on:keydown.enter="
105
+ onSetAnswer(
106
+ answer,
107
+ true
108
+ )
109
+ "
110
+ @click="onSetAnswer(answer)"
111
+ >
112
+ <template #label>
113
+ <div
114
+ :class="
115
+ onContainerLabel(
116
+ answer
117
+ )
118
+ "
119
+ >
120
+ {{
121
+ answer.correctAnswer
122
+ ? $t(
123
+ 'components.content.blocks.assessment.questions.correct'
124
+ )
125
+ : $t(
126
+ 'components.content.blocks.assessment.questions.incorrect'
127
+ )
128
+ }}
129
+ </div>
130
+ </template>
131
+ </v-radio>
132
+ </v-col>
133
+ </v-row>
134
+ <v-row>
135
+ <v-col cols="12" md="6">
136
+ <TextEditor
137
+ :id="'option-' + answer.id"
138
+ :key="
139
+ 'option-' +
140
+ answer.id +
141
+ '-' +
142
+ updateKey
143
+ "
144
+ v-model="answer.value"
145
+ :label="
146
+ $t(
147
+ 'components.content.blocks.assessment.answer_option'
148
+ )
149
+ "
150
+ :height="200"
151
+ lazy-load
152
+ @input="onCheckValidation"
153
+ ></TextEditor>
154
+ </v-col>
155
+ <v-col cols="12" md="6">
156
+ <TextEditor
157
+ :id="
158
+ 'feedback-' + answer.id
159
+ "
160
+ :key="
161
+ 'feedback-' +
162
+ answer.id +
163
+ '-' +
164
+ updateKey
165
+ "
166
+ v-model="
167
+ question.answer_options[
168
+ index
169
+ ].feedback
170
+ "
171
+ :label="
172
+ $t(
173
+ 'components.content.blocks.assessment.answer_explanation'
174
+ )
175
+ "
176
+ :height="200"
177
+ lazy-load
178
+ ></TextEditor>
179
+ </v-col>
180
+ </v-row>
181
+ </v-col>
182
+ <v-col
183
+ cols="1"
184
+ lg="1"
185
+ md="1"
186
+ class="d-flex justify-center align-center pa-0"
187
+ >
188
+ <v-btn
189
+ elevation="0"
190
+ text
191
+ class="pa-0 ma-0"
192
+ color="error"
193
+ :disabled="removeDisabled"
194
+ @click="onDelete(index)"
195
+ >
196
+ <span class="sr-only">{{
197
+ $t(
198
+ 'components.content.blocks.assessment.questions.types.multi_choice_single_answer.add_option'
199
+ )
200
+ }}</span>
201
+ <v-icon>mdi-delete-outline</v-icon>
202
+ </v-btn>
203
+ </v-col>
204
+ </v-row>
205
+ </v-alert>
206
+ </div>
207
+ </draggable>
208
+ </v-radio-group>
209
+ <v-container class="d-flex justify-center" v-if="overLength">
210
+ <v-btn
211
+ :class="cursor"
212
+ color="primary"
213
+ elevation="0"
214
+ :disabled="disabled"
215
+ @click="onAddAnswer"
216
+ >
217
+ <v-icon>mdi-plus</v-icon>
218
+ {{
219
+ $t(
220
+ 'windward.games.components.settings.multiple_choice.add_answer'
221
+ )
222
+ }}
223
+ </v-btn>
224
+ </v-container>
225
+ </v-col>
226
+ <v-col cols="12">
227
+ <p class="font-weight-bold p-bold-text">
51
228
  {{
52
229
  $t(
53
- 'windward.games.components.settings.multiple_choice.correct_answer'
230
+ 'components.content.blocks.assessment.questions.question_feedback'
54
231
  )
55
232
  }}
56
233
  </p>
57
- </v-col>
58
- <v-container
59
- v-for="(answer, index) in question.answer_options"
60
- :key="index"
61
- >
62
- <v-row>
63
- <v-col
64
- cols="12"
65
- md="1"
66
- class="d-flex justify-center"
67
- @mouseover="onHover"
68
- @mouseleave="onHoverLeave"
69
- >
70
- <v-radio-group v-model="question.correctAnswer">
71
- <v-radio
72
- :ref="'checkbox' + index"
73
- :value="answer.id"
74
- :disabled="disabled"
75
- v-on:keydown.enter="onSetAnswer(answer, true)"
76
- @click="onSetAnswer(answer)"
77
- ></v-radio>
78
- </v-radio-group>
79
- </v-col>
80
- <v-col cols="12" md="10">
81
- <v-textarea
82
- v-model="answer.value"
83
- flat
84
- solo
85
- :autofocus="answer.focus"
86
- :outlined="setOutline(answer)"
87
- hide-details
88
- :class="getCorrectAnswer(answer)"
89
- :label="
90
- $t(
91
- 'windward.games.components.settings.multiple_choice.answer_option'
92
- )
93
- "
94
- rows="2"
95
- :rules="$Validation.getRule('exists')"
96
- :disabled="disabled"
97
- ></v-textarea>
98
- </v-col>
99
- <v-col cols="12" md="1" class="d-flex justify-center">
100
- <v-btn
101
- text
102
- elevation="0"
103
- :disabled="disabled"
104
- @click="onDelete(index)"
105
- >
106
- <v-icon color="error">mdi-delete-outline</v-icon>
107
- <span class="d-sr-only">{{
108
- $t('shared.forms.delete')
109
- }}</span>
110
- </v-btn>
111
- </v-col>
112
- </v-row>
113
- </v-container>
114
- <v-container class="d-flex justify-center" v-if="overLength">
115
- <v-btn
116
- :class="cursor"
117
- color="primary"
118
- elevation="0"
119
- :disabled="disabled"
120
- @click="onAddAnswer"
121
- >
122
- <v-icon>mdi-plus</v-icon>
123
- {{
234
+ <v-switch
235
+ v-model="question.metadata.config.single_feedback"
236
+ :label="
124
237
  $t(
125
- 'windward.games.components.settings.multiple_choice.add_answer'
238
+ 'components.content.blocks.assessment.single_feedback_label'
126
239
  )
127
- }}
128
- </v-btn>
129
- </v-container>
240
+ "
241
+ ></v-switch>
242
+ <TextEditor
243
+ v-if="question.metadata.config.single_feedback"
244
+ id="question-feedback"
245
+ :key="'question-feedback-' + updateKey"
246
+ v-model="question.answer_description"
247
+ :height="200"
248
+ lazy-load
249
+ ></TextEditor>
250
+ </v-col>
251
+ <v-col cols="12" md="12" v-if="false">
252
+ <QuestionSkills></QuestionSkills>
253
+ </v-col>
130
254
  </v-row>
131
- </v-form>
255
+ </v-container>
132
256
  </template>
133
257
  <script>
134
258
  import Form from '~/components/Core/Form'
135
259
  import _ from 'lodash'
260
+ import draggable from 'vuedraggable'
136
261
  import Crypto from '~/helpers/Crypto'
262
+ import ImageAssetSettings from '~/components/Content/Settings/ImageAssetSettings.vue'
263
+ import TextEditor from '~/components/Text/TextEditor.vue'
264
+
137
265
  export default {
138
266
  name: 'QuestionDialog',
267
+ components: {
268
+ TextEditor,
269
+ ImageAssetSettings,
270
+ draggable,
271
+ },
139
272
  extends: Form,
140
273
  layout: 'authenticated',
141
274
  middleware: ['auth', 'course'],
@@ -159,6 +292,7 @@ export default {
159
292
  disabled: false,
160
293
  chosen: false,
161
294
  focus: false,
295
+ feedback: '',
162
296
  },
163
297
  {
164
298
  id: Crypto.id(),
@@ -167,8 +301,17 @@ export default {
167
301
  disabled: false,
168
302
  chosen: false,
169
303
  focus: false,
304
+ feedback: '',
170
305
  },
171
306
  ],
307
+ question_metadata: {
308
+ body_asset: null,
309
+ },
310
+ metadata: {
311
+ config: {
312
+ single_feedback: false,
313
+ },
314
+ },
172
315
  // needed to add this bc radio group needs the id of the answer to set correct
173
316
  correctAnswer: 1,
174
317
  body: '',
@@ -177,8 +320,9 @@ export default {
177
320
  id: Crypto.id(),
178
321
  }
179
322
  }
323
+ // resets form if brand new question was created
180
324
  if (newValue && newValue.body === '') {
181
- this.$refs.form.resetValidation()
325
+ this.updateKey = Crypto.id()
182
326
  }
183
327
  },
184
328
  },
@@ -195,6 +339,14 @@ export default {
195
339
  return false
196
340
  }
197
341
  },
342
+ dragOptions() {
343
+ return {
344
+ animation: 200,
345
+ }
346
+ },
347
+ removeDisabled() {
348
+ return this.question.answer_options.length <= 2
349
+ },
198
350
  },
199
351
  data() {
200
352
  return {
@@ -213,19 +365,81 @@ export default {
213
365
  },
214
366
  cursor: null,
215
367
  updateKey: 0,
368
+ formValid: false,
216
369
  }
217
370
  },
218
- mounted() {
371
+ beforeMount() {
219
372
  if (this.value.body === '') {
220
- this.$refs.form.resetValidation()
373
+ // this.$refs.form.resetValidation()
221
374
  this.question = _.cloneDeep(this.value)
222
375
  } else if (!_.isEmpty(this.value)) {
223
376
  this.question = _.cloneDeep(this.value)
377
+ if (_.isEmpty(this.question.question_metadata)) {
378
+ this.question.question_metadata = {
379
+ body_asset: null,
380
+ }
381
+ }
382
+ if (_.isEmpty(this.question.metadata)) {
383
+ // if legacy has no feedback than set toggle to false
384
+ if (_.isEmpty(this.question.answer_description)) {
385
+ this.question.metadata = {
386
+ config: {
387
+ single_feedback: false,
388
+ },
389
+ }
390
+ } else {
391
+ this.question.metadata = {
392
+ config: {
393
+ single_feedback: true,
394
+ },
395
+ }
396
+ }
397
+ }
224
398
  }
225
- // refreshes data for modal on mount
226
- this.updateKey = Crypto.id()
227
399
  },
228
400
  methods: {
401
+ onContainerLabel(answer) {
402
+ if (answer.correctAnswer) {
403
+ return 'container-label-correct'
404
+ } else {
405
+ return ''
406
+ }
407
+ },
408
+ onDragChange() {
409
+ this.updateKey = Crypto.id()
410
+ },
411
+ onCheckValidation() {
412
+ let questionValid = true
413
+ const condition1 = '<div>&nbsp;</div><div>&nbsp;</div>'
414
+ const condition2 = '<div>&nbsp;</div>'
415
+ const condition3 = '<div>&nbsp;</div>\n<div>&nbsp;</div>'
416
+ if (
417
+ _.isEmpty(this.question.body) ||
418
+ this.question.body === condition1 ||
419
+ this.question.body === condition2 ||
420
+ this.question.body === condition3
421
+ ) {
422
+ questionValid = false
423
+ }
424
+ let correctAnswerChosen = false
425
+ this.question.answer_options.forEach((answer) => {
426
+ if (
427
+ _.isEmpty(answer.value) ||
428
+ answer.value === condition1 ||
429
+ answer.value === condition2 ||
430
+ answer.value === condition3
431
+ ) {
432
+ questionValid = false
433
+ }
434
+ if (answer.correctAnswer) {
435
+ correctAnswerChosen = true
436
+ }
437
+ })
438
+ if (!correctAnswerChosen) {
439
+ questionValid = false
440
+ }
441
+ this.$emit('change:valid', questionValid)
442
+ },
229
443
  onSave() {
230
444
  // clones questions so that input areas aren't linked when reset
231
445
  const emittedQuestion = _.cloneDeep(this.question)
@@ -253,9 +467,11 @@ export default {
253
467
  element.correctAnswer = false
254
468
  }
255
469
  })
470
+ this.onCheckValidation()
256
471
  },
257
472
  onDelete(index) {
258
473
  this.question.answer_options.splice(index, 1)
474
+ this.onCheckValidation()
259
475
  },
260
476
  onAddAnswer() {
261
477
  // pushes new answer object into answer options
@@ -268,11 +484,12 @@ export default {
268
484
  focus: true,
269
485
  }
270
486
  this.question.answer_options.push(answerObject)
487
+ this.onCheckValidation()
271
488
  },
272
489
  getCorrectAnswer(answer) {
273
490
  // if input area is the correct answer adds a green border
274
491
  if (answer.correctAnswer === true) {
275
- return 'successOutline'
492
+ return 'container-success-outline'
276
493
  } else {
277
494
  return ''
278
495
  }
@@ -285,7 +502,7 @@ export default {
285
502
  }
286
503
  },
287
504
  onHover() {
288
- this.cursor = 'changePointer'
505
+ this.cursor = 'cursor-pointer'
289
506
  },
290
507
  onHoverLeave() {
291
508
  this.cursor = ''
@@ -294,11 +511,17 @@ export default {
294
511
  }
295
512
  </script>
296
513
  <style lang="scss" scoped>
297
- .successOutline {
514
+ .container-success-outline {
298
515
  border: 4px solid var(--v-success-base);
299
516
  color: var(--v-success-base);
300
517
  }
301
- .changePointer {
518
+ .cursor-pointer {
302
519
  cursor: pointer !important;
303
520
  }
521
+ .p-bold-text {
522
+ color: var(--v-primary-base);
523
+ }
524
+ .container-label-correct {
525
+ color: var(--v-success-base) !important;
526
+ }
304
527
  </style>
@@ -13,6 +13,10 @@
13
13
  <div v-if="!tableMode">
14
14
  <div>
15
15
  <h2
16
+ v-if="
17
+ block.metadata.config.title &&
18
+ block.metadata.config.display_title
19
+ "
16
20
  :aria-label="
17
21
  $t(
18
22
  'windward.games.components.content.blocks.quizshow_game.title'
@@ -231,6 +235,9 @@ export default {
231
235
  if (this.answerStates.length === 0) {
232
236
  this.initAnswersStates()
233
237
  }
238
+ if (!_.isBoolean(this.block.metadata.config.display_title)) {
239
+ this.$set(this.block.metadata.config, 'display_title', true)
240
+ }
234
241
  },
235
242
  mounted() {},
236
243
  updated() {},
@@ -1,7 +1,12 @@
1
1
  <template>
2
2
  <div>
3
3
  <v-col class="pa-0">
4
- <h2>
4
+ <h2
5
+ v-if="
6
+ block.metadata.config.title &&
7
+ block.metadata.config.display_title
8
+ "
9
+ >
5
10
  {{
6
11
  block.metadata.config.title
7
12
  ? block.metadata.config.title
@@ -253,6 +258,9 @@ export default {
253
258
  'windward.games.components.settings.seven_strikes.title'
254
259
  )
255
260
  }
261
+ if (!_.isBoolean(this.block.metadata.config.display_title)) {
262
+ this.$set(this.block.metadata.config, 'display_title', true)
263
+ }
256
264
  if (_.isEmpty(this.block.metadata.config.instructions)) {
257
265
  this.block.metadata.config.instructions = this.$t(
258
266
  'windward.games.components.settings.seven_strikes.instructions'
@@ -2,7 +2,10 @@
2
2
  <div>
3
3
  <div>
4
4
  <h2
5
- v-if="block.metadata.config.title"
5
+ v-if="
6
+ block.metadata.config.title &&
7
+ block.metadata.config.display_title
8
+ "
6
9
  :aria-label="
7
10
  $t(
8
11
  'windward.games.components.content.blocks.slideshow.slideshow_title'
@@ -128,7 +131,9 @@ export default {
128
131
  'windward.games.components.settings.slideshow.form.slideshow_title'
129
132
  )
130
133
  }
131
-
134
+ if (!_.isBoolean(this.block.metadata.config.display_title)) {
135
+ this.$set(this.block.metadata.config, 'display_title', true)
136
+ }
132
137
  if (_.isEmpty(this.block.metadata.config.instructions)) {
133
138
  this.block.metadata.config.instructions = ''
134
139
  }
@@ -1,7 +1,12 @@
1
1
  <template>
2
2
  <v-container class="pa-0">
3
3
  <v-col class="pa-0">
4
- <h2>
4
+ <h2
5
+ v-if="
6
+ block.metadata.config.title &&
7
+ block.metadata.config.display_title
8
+ "
9
+ >
5
10
  {{
6
11
  block.metadata.config.title
7
12
  ? block.metadata.config.title
@@ -211,6 +216,9 @@ export default {
211
216
  'windward.games.components.settings.word_jumble.title'
212
217
  )
213
218
  }
219
+ if (!_.isBoolean(this.block.metadata.config.display_title)) {
220
+ this.$set(this.block.metadata.config, 'display_title', true)
221
+ }
214
222
  if (
215
223
  _.isEmpty(this.block.metadata.config.instructions) &&
216
224
  this.block.id &&