@windward/games 0.10.0 → 0.12.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.
@@ -21,40 +21,38 @@
21
21
  {{ block.metadata.config.instructions }}
22
22
  </p>
23
23
  <v-container :class="status">
24
- <v-alert class="d-flex justify-center pa-2">{{
24
+ <v-row class="d-flex justify-center pa-2">{{
25
25
  feedback
26
26
  ? feedback
27
27
  : $t(
28
28
  'windward.games.components.content.blocks.bucket_game.form.feedback.feedback_here'
29
29
  )
30
- }}</v-alert>
31
- <v-row class="d-flex justify-center pa-2">
30
+ }}</v-row>
31
+ <v-row class="d-flex justify-center pa-2" v-if="!gameCompleted">
32
32
  <v-btn
33
- v-if="status === 'successOutline'"
34
- class="success mr-5"
33
+ v-if="status === 'container-success-outline'"
34
+ class="success"
35
35
  elevation="0"
36
36
  @click="onContinueGame"
37
37
  >{{ $t('shared.forms.continue') }}
38
38
  </v-btn>
39
- <v-container
40
- v-if="status === 'errorOutline'"
41
- class="d-flex justify-center align-center"
39
+ <v-btn
40
+ v-if="status === 'container-error-outline'"
41
+ class="error"
42
+ elevation="0"
42
43
  @click="onExitFeedback"
43
- ><v-btn class="error" elevation="0">{{
44
- $t('shared.forms.try_again')
45
- }}</v-btn>
46
- </v-container>
44
+ >{{ $t('shared.forms.try_again') }}
45
+ </v-btn>
47
46
  </v-row>
48
47
  </v-container>
49
48
  <v-container class="pl-0 pr-0 mt-2">
50
- <v-row
51
- v-if="render"
52
- class="d-flex flex-wrap flex-row justify-center flex-fill"
53
- >
54
- <v-row align="center" class="col-md-12">
49
+ <v-row v-if="render" class="d-flex justify-center">
50
+ <v-row
51
+ v-if="mainPrompt['textOrImage'] === 'text'"
52
+ class="col-md-12"
53
+ >
55
54
  <v-card
56
- v-if="mainPrompt['textOrImage'] === 'text'"
57
- class="flex-fill bucket"
55
+ class="flex-fill card-bucket"
58
56
  min-height="5em"
59
57
  outlined
60
58
  tile
@@ -62,7 +60,7 @@
62
60
  >
63
61
  <v-card-text class="text-center">
64
62
  <draggable
65
- class="dragArea list-group"
63
+ class="dragArea no-select"
66
64
  :key="mainPrompt['prompt']"
67
65
  v-bind="dragOptions"
68
66
  disabled
@@ -79,17 +77,24 @@
79
77
  </v-card-text>
80
78
  </v-card>
81
79
  </v-row>
82
- <v-row align="center" class="col-md-10">
80
+ <v-row
81
+ v-if="mainPrompt['textOrImage'] === 'image'"
82
+ class="d-flex justify-center ma-2"
83
+ >
83
84
  <v-card
84
- v-if="mainPrompt['textOrImage'] === 'image'"
85
- class="pa-2 flex-fill bucket"
85
+ class="flex-fill card-bucket"
86
86
  min-height="5em"
87
+ max-height="400"
88
+ :max-width="maxWidth"
87
89
  outlined
88
90
  tile
89
91
  >
90
92
  <v-card-text>
91
93
  <draggable
92
- v-if="mainPrompt.file"
94
+ v-if="
95
+ mainPrompt.fileConfig.asset &&
96
+ mainPrompt.fileConfig.asset.file_asset_id
97
+ "
93
98
  class="dragArea list-group"
94
99
  :key="mainPrompt['prompt']"
95
100
  v-bind="dragOptions"
@@ -97,28 +102,18 @@
97
102
  group="people"
98
103
  tabindex="0"
99
104
  >
100
- <div :id="mainPrompt.file.file_asset_id">
101
- <v-img
102
- :aria-describedby="
103
- getFileAriaDescribedBy(
104
- mainPrompt.file,
105
- mainPrompt[
106
- 'ariaDescribedBy'
107
- ]
108
- )
109
- "
110
- :alt="
111
- getFileAlt(
112
- mainPrompt.file,
113
- mainPrompt['altText']
114
- )
115
- "
116
- :src="
117
- getFilePublicUrl(
118
- mainPrompt.file
119
- )
120
- "
121
- ></v-img>
105
+ <div
106
+ :id="
107
+ mainPrompt.fileConfig.asset
108
+ .file_asset_id
109
+ "
110
+ >
111
+ <ImageAssetViewer
112
+ v-model="mainPrompt['fileConfig']"
113
+ :assets="block.assets"
114
+ max-height="400"
115
+ :max-width="maxWidth"
116
+ ></ImageAssetViewer>
122
117
  </div>
123
118
  </draggable>
124
119
  </v-card-text>
@@ -136,7 +131,7 @@
136
131
  startingIndex
137
132
  ].textOrImage === 'text'
138
133
  "
139
- class="pa-2 flex-fill bucket"
134
+ class="pa-2 flex-fill card-bucket"
140
135
  min-height="5em"
141
136
  outlined
142
137
  tile
@@ -164,18 +159,25 @@
164
159
  </v-card-text>
165
160
  </v-card>
166
161
  </v-row>
167
- <v-row align="center" class="col-md-10">
162
+ <v-row
163
+ class="d-flex justify-center ma-2"
164
+ v-if="
165
+ block.metadata.config.prompts[startingIndex][
166
+ startingIndex
167
+ ].textOrImage === 'image' &&
168
+ block.metadata.config.prompts[startingIndex][
169
+ startingIndex
170
+ ].fileConfig.asset &&
171
+ block.metadata.config.prompts[startingIndex][
172
+ startingIndex
173
+ ].fileConfig.asset.file_asset_id
174
+ "
175
+ >
168
176
  <v-card
169
- v-if="
170
- block.metadata.config.prompts[startingIndex][
171
- startingIndex
172
- ].textOrImage === 'image' &&
173
- block.metadata.config.prompts[startingIndex][
174
- startingIndex
175
- ].file
176
- "
177
- class="pa-2 flex-fill bucket"
177
+ class="pa-2 flex-fill card-bucket"
178
178
  min-height="5em"
179
+ max-height="400"
180
+ :max-width="maxWidth"
179
181
  outlined
180
182
  tile
181
183
  >
@@ -196,43 +198,20 @@
196
198
  :id="
197
199
  block.metadata.config.prompts[
198
200
  startingIndex
199
- ][startingIndex].file.file_asset_id
201
+ ][startingIndex].fileConfig.asset
202
+ .file_asset_id
200
203
  "
201
204
  >
202
- <v-img
203
- :aria-describedby="
204
- getFileAriaDescribedBy(
205
- block.metadata.config
206
- .prompts[startingIndex][
207
- startingIndex
208
- ].file,
209
- block.metadata.config
210
- .prompts[startingIndex][
211
- startingIndex
212
- ]['ariaDescribedBy']
213
- )
214
- "
215
- :alt="
216
- getFileAlt(
217
- block.metadata.config
218
- .prompts[startingIndex][
219
- startingIndex
220
- ].file,
221
- block.metadata.config
222
- .prompts[startingIndex][
223
- startingIndex
224
- ]['altText']
225
- )
205
+ <ImageAssetViewer
206
+ v-model="
207
+ block.metadata.config.prompts[
208
+ startingIndex
209
+ ][startingIndex].fileConfig
226
210
  "
227
- :src="
228
- getFilePublicUrl(
229
- block.metadata.config
230
- .prompts[startingIndex][
231
- startingIndex
232
- ].file
233
- )
234
- "
235
- ></v-img>
211
+ :assets.sync="block.assets"
212
+ max-height="400"
213
+ :max-width="maxWidth"
214
+ ></ImageAssetViewer>
236
215
  </div>
237
216
  </draggable>
238
217
  </v-card-text>
@@ -242,7 +221,7 @@
242
221
  <v-row>
243
222
  <draggable
244
223
  v-bind="dragOptions"
245
- class="d-flex justify-space-between flex-wrap col-md-12"
224
+ class="d-flex justify-space-between flex-wrap col-md-12 no-select"
246
225
  :list="block.metadata.config.answerObjects"
247
226
  :disabled="!allowDrag"
248
227
  :group="{
@@ -250,6 +229,8 @@
250
229
  pull: 'clone',
251
230
  put: false,
252
231
  }"
232
+ :forceFallback="true"
233
+ :sort="false"
253
234
  @end="onEnd"
254
235
  >
255
236
  <v-card
@@ -257,8 +238,8 @@
257
238
  .answerObjects"
258
239
  :key="aindex"
259
240
  :class="
260
- 'pa-2 ma-2 answerCard container-outline ' +
261
- answerColor(answer)
241
+ 'pa-2 ma-2 card-answer-option container-outline ' +
242
+ getAnswerStyles(answer)
262
243
  "
263
244
  outlined
264
245
  >
@@ -274,7 +255,6 @@
274
255
  align="end"
275
256
  >
276
257
  <v-row>
277
- <!-- <v-flex xs8></v-flex> -->
278
258
  <v-col cols="12" lg="6" md="7" sm="6"></v-col>
279
259
  <v-col cols="12" lg="6" md="5" sm="6">
280
260
  <v-row
@@ -311,11 +291,8 @@
311
291
  outlined
312
292
  elevation="0"
313
293
  @click="onReset"
314
- >{{
315
- $t(
316
- 'windward.games.components.content.blocks.matching_game.reset'
317
- )
318
- }}
294
+ >
295
+ {{ $t('shared.forms.reset') }}
319
296
  </v-btn>
320
297
  </v-col>
321
298
  </v-row>
@@ -331,10 +308,11 @@
331
308
  import _ from 'lodash'
332
309
  import draggable from 'vuedraggable'
333
310
  import BaseContentBlock from '~/components/Content/Blocks/BaseContentBlock'
311
+ import ImageAssetViewer from '~/components/Content/ImageAssetViewer.vue'
334
312
 
335
313
  export default {
336
314
  name: 'MatchingGame',
337
- components: { draggable },
315
+ components: { draggable, ImageAssetViewer },
338
316
  extends: BaseContentBlock,
339
317
  beforeMount() {
340
318
  if (_.isEmpty(this.block)) {
@@ -393,6 +371,15 @@ export default {
393
371
  totalAmountQuestions() {
394
372
  return _.flatten(this.block.metadata.config.prompts).length
395
373
  },
374
+ maxWidth() {
375
+ if (window.innerWidth <= 430 && window.innerWidth >= 378) {
376
+ return '250'
377
+ } else if (window.innerWidth <= 378) {
378
+ return '200'
379
+ } else {
380
+ return '300'
381
+ }
382
+ },
396
383
  },
397
384
  data() {
398
385
  return {
@@ -405,9 +392,10 @@ export default {
405
392
  status: 'default',
406
393
  allowDrag: true,
407
394
  mainPrompt: {
408
- altText: '',
409
395
  answer: {},
410
- file: null,
396
+ fileConfig: {
397
+ hideBackground: true,
398
+ },
411
399
  prompt: '',
412
400
  textOrImage: 'text',
413
401
  },
@@ -420,6 +408,8 @@ export default {
420
408
  render(newVal) {
421
409
  if (newVal === true) {
422
410
  this.onReset()
411
+ } else if (newVal === false) {
412
+ this.allowDrag = false
423
413
  }
424
414
  },
425
415
  value(newValue) {
@@ -432,14 +422,20 @@ export default {
432
422
  this.setMainPrompt()
433
423
  },
434
424
  methods: {
435
- answerColor(element) {
425
+ getAnswerStyles(element) {
426
+ let classValue = ' '
436
427
  if (
437
428
  !_.isEmpty(this.droppedElement) &&
438
429
  element.display === this.droppedElement.display
439
430
  ) {
440
- return this.status
431
+ classValue += this.status + ' '
432
+ }
433
+ if (this.allowDrag) {
434
+ classValue += 'card-answer-option-grab '
435
+ } else {
436
+ classValue += 'card-answer-option-default '
441
437
  }
442
- return ''
438
+ return classValue
443
439
  },
444
440
  shuffle(array) {
445
441
  let currentIndex = array.length,
@@ -466,75 +462,58 @@ export default {
466
462
  }
467
463
  },
468
464
  onEnd(evt, originalEvent) {
469
- // if dragged to nowhere do not set target
470
- const draggedToNowhere = evt.to === evt.from
471
465
  if (!this.render) {
472
466
  this.mainPrompt =
473
467
  this.block.metadata.config.prompts[this.startingIndex]
474
468
  }
475
- let draggedElement = ''
476
- let target = ''
477
- if (!draggedToNowhere) {
478
- if (this.mainPrompt.textOrImage === 'text') {
479
- draggedElement =
480
- this.block.metadata.config.answerObjects[evt.oldIndex]
481
- this.block.metadata.config.prompts.forEach(
482
- (outerElement) => {
483
- const getMain = outerElement.find((element) => {
484
- return (
485
- element.prompt ==
486
- evt.to.firstChild.textContent.trim()
487
- )
488
- })
489
- if (getMain) {
490
- target = getMain
491
- }
492
- }
493
- )
494
- } else {
495
- draggedElement =
496
- this.block.metadata.config.answerObjects[evt.oldIndex]
497
- this.block.metadata.config.prompts.forEach(
498
- (outerElement) => {
499
- const mainElement = outerElement.find((element) => {
500
- return (
501
- element.file?.file_asset_id ==
502
- evt.to.firstElementChild.id
503
- )
504
- })
505
- if (mainElement) {
506
- target = mainElement
507
- }
508
- }
509
- )
510
- }
469
+ // If dragged to the same container, do nothing
470
+ if (evt.to === evt.from) {
471
+ return
511
472
  }
473
+
474
+ const { answerObjects, prompts } = this.block.metadata.config
475
+ const draggedElement = answerObjects[evt.oldIndex]
476
+ // flatten nested prompts array and then find target by either text or image
477
+ let target = prompts
478
+ .flatMap((outerElement) => outerElement)
479
+ .find((element) =>
480
+ this.mainPrompt.textOrImage === 'text'
481
+ ? element.prompt ===
482
+ evt.to.firstChild?.textContent.trim()
483
+ : element.fileConfig.asset?.file_asset_id ===
484
+ evt.to.firstElementChild?.id
485
+ )
486
+
487
+ this.setFeedback(target, draggedElement)
488
+ },
489
+ setFeedback(target, draggedElement) {
512
490
  //set feedback information here
513
491
  if (_.isUndefined(target) || _.isEmpty(target)) {
514
492
  this.droppedElement = null
515
- } else if (draggedElement.id !== target.answer.id) {
493
+ // avoids unnecessary computing below
494
+ return
495
+ }
496
+
497
+ const isCorrect = draggedElement.id === target.answer.id
498
+
499
+ if (!isCorrect) {
516
500
  this.feedback = this.block.metadata.config.feedback_incorrect
517
- this.status = 'errorOutline'
518
- this.allowDrag = false
519
- this.droppedElement = draggedElement
520
- } else if (draggedElement.id === target.answer.id) {
501
+ this.status = 'container-error-outline'
502
+ } else if (isCorrect) {
521
503
  this.feedback = !_.isEmpty(target.matchExplanation)
522
504
  ? target.matchExplanation
523
505
  : this.block.metadata.config.feedback_correct
524
506
  this.solvedQuestions.push(target)
525
- this.status = 'successOutline'
526
- this.allowDrag = false
527
- this.droppedElement = draggedElement
507
+ this.status = 'container-success-outline'
508
+ // checks if game is completed
528
509
  if (
529
510
  this.solvedQuestions.length === this.flattenedPrompts.length
530
511
  ) {
531
- this.feedback = this.$t(
532
- 'windward.games.components.content.blocks.matching_game.congratulations_feedback'
533
- )
534
512
  this.gameCompleted = true
535
- this.status = 'default'
536
513
  }
537
514
  }
515
+ this.allowDrag = false
516
+ this.droppedElement = draggedElement
538
517
  },
539
518
  onExitFeedback() {
540
519
  this.status = 'default'
@@ -546,7 +525,8 @@ export default {
546
525
  },
547
526
  onContinueGame() {
548
527
  if (!this.render) {
549
- this.startingIndex = this.startingIndex + 1
528
+ // do nothing cannot play in editing mode
529
+ return
550
530
  }
551
531
  this.status = 'default'
552
532
  this.mainPrompt = this.shufflePrompts.shift()
@@ -568,66 +548,56 @@ export default {
568
548
  )
569
549
  this.setMainPrompt()
570
550
  },
571
- getFileAlt(file, defaultText = '') {
572
- // If a default / override was defined
573
- if (defaultText) {
574
- return defaultText
575
- }
576
-
577
- file = this.resolveAsset(file)
578
- return _.get(file, 'asset.metadata.props.alt', '')
579
- },
580
- getFileAriaDescribedBy(file, defaultText = '') {
581
- // If a default / override was defined
582
- if (defaultText) {
583
- return defaultText
584
- }
585
-
586
- file = this.resolveAsset(file)
587
- return _.get(file, 'asset.metadata.props.aria_describedby', '')
588
- },
589
- getFilePublicUrl(file) {
590
- file = this.resolveAsset(file)
591
- return _.get(file, 'asset.public_url', '')
592
- },
593
551
  },
594
552
  }
595
553
  </script>
596
554
  <style scoped>
597
- .bucket {
555
+ .no-select {
556
+ user-select: none;
557
+ }
558
+ .dragArea {
559
+ min-height: 100%;
560
+ min-width: 100%;
561
+ }
562
+ .card-bucket {
598
563
  line-height: 1.1em;
599
564
  padding: 0.5em;
600
565
  box-shadow: 0px 2px 3px #0000004a;
601
566
  transition: 0.5s box-shadow;
602
567
  }
603
- .successOutline {
568
+ .container-success-outline {
604
569
  border: 4px solid var(--v-success-base) !important;
605
570
  }
606
- .errorOutline {
571
+ .container-error-outline {
607
572
  border: 4px solid var(--v-error-base) !important;
608
573
  }
609
574
  .card_text {
610
575
  font-size: large;
611
576
  }
612
- .answerCard {
577
+ .card-answer-option {
613
578
  min-width: 23%;
579
+ }
580
+ .card-answer-option-grab {
614
581
  cursor: grab;
615
582
  }
583
+ .card-answer-option-default {
584
+ cursor: default;
585
+ }
616
586
  .icon--error {
617
587
  color: var(--v-error-base);
618
588
  }
619
589
  @media (max-width: 1270px) {
620
- .answerCard {
590
+ .card-answer-option {
621
591
  min-width: 30%;
622
592
  }
623
593
  }
624
594
  @media (max-width: 900px) {
625
- .answerCard {
595
+ .card-answer-option {
626
596
  min-width: 40%;
627
597
  }
628
598
  }
629
599
  @media (max-width: 610px) {
630
- .answerCard {
600
+ .card-answer-option {
631
601
  min-width: 95%;
632
602
  }
633
603
  }
@@ -118,6 +118,7 @@
118
118
  <div
119
119
  :ref="'input' + splitIndex"
120
120
  class="container-text-area ml-1 mr-1"
121
+ :class="getContainerStatus(letter)"
121
122
  maxlength="1"
122
123
  >
123
124
  <div v-if="letter.show === true">
@@ -302,6 +303,17 @@ export default {
302
303
  this.block.metadata.config.currentWord = newIndex
303
304
  this.onSlideChanged(newIndex)
304
305
  },
306
+ getContainerStatus(letter) {
307
+ if (
308
+ !_.isEmpty(letter.letter) &&
309
+ letter.letter !== ' ' &&
310
+ letter.letter !== '-'
311
+ ) {
312
+ return 'letter-underline '
313
+ } else if (letter.letter === '-') {
314
+ letter.show = true
315
+ }
316
+ },
305
317
  onChange(input) {
306
318
  this.input = input
307
319
  },
@@ -427,7 +439,11 @@ export default {
427
439
  this.block.metadata.config.words[
428
440
  this.onSlide
429
441
  ].splitWord.forEach((letter) => {
430
- if (letter.letter && letter.letter !== ' ') {
442
+ if (
443
+ letter.letter &&
444
+ letter.letter !== ' ' &&
445
+ letter.letter !== '-'
446
+ ) {
431
447
  letter.show = false
432
448
  }
433
449
  })
@@ -459,11 +475,13 @@ export default {
459
475
  .container-text-area {
460
476
  width: 20px;
461
477
  height: 20px;
462
- border-bottom: 2px solid var(--v-primary-base);
463
478
  display: flex;
464
479
  justify-content: center;
465
480
  align-items: center;
466
481
  }
482
+ .letter-underline {
483
+ border-bottom: 2px solid var(--v-primary-base);
484
+ }
467
485
  .container-strike-area {
468
486
  color: gray;
469
487
  font-size: 40px;