@windward/games 0.26.0 → 0.28.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.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,37 @@
1
1
  # Changelog
2
2
 
3
+ ## Release [0.28.0] - 2026-03-10
4
+
5
+ * Merged in feature/LE-2298-word-jumble (pull request #288)
6
+ * Merged release/0.28.0 into feature/LE-2298-word-jumble
7
+ * Merged in feature/LE-2076-matching-game-allow-rich-text-ed (pull request #287)
8
+ * Merged release/0.28.0 into feature/LE-2076-matching-game-allow-rich-text-ed
9
+ * Merged in feature/LE-2076-matching-game-allow-rich-text-ed (pull request #286)
10
+ * Merged in bugfix/LE-2232-flashcards-when-the-prev-or-next (pull request #285)
11
+ * Merged release/0.28.0 into bugfix/LE-2232-flashcards-when-the-prev-or-next
12
+ * Merged release/0.28.0 into feature/LE-2076-matching-game-allow-rich-text-ed
13
+ * Merged in feature/LE-2076-matching-game-allow-rich-text-ed (pull request #281)
14
+ * Merged in feature/LE-2298-word-jumble (pull request #282)
15
+ * Merged release/0.28.0 into bugfix/LE-2232-flashcards-when-the-prev-or-next
16
+ * Merged release/0.28.0 into feature/LE-2298-word-jumble
17
+ * Merged master into release/0.28.0
18
+ * Merged in release/0.27.0 (pull request #280)
19
+ * Merged in release/0.26.0 (pull request #269)
20
+ * Merged in release/0.25.0 (pull request #266)
21
+ * Merged in release/0.24.0 (pull request #261)
22
+ * Merged in release/0.23.0 (pull request #256)
23
+ * Merged in release/0.22.0 (pull request #248)
24
+ * Merged in release/0.21.0 (pull request #244)
25
+ * Merged in release/0.20.0 (pull request #238)
26
+ * Merged in hotfix/0.19.1 (pull request #236)
27
+ * Merged in hotfix/0.19.1 (pull request #232)
28
+
29
+
30
+ ## Release [0.27.0] - 2026-01-27
31
+
32
+ * Merged in bugfix/LE-2232-flashcards-when-the-prev-or-next (pull request #274)
33
+
34
+
3
35
  ## Release [0.26.0] - 2026-01-20
4
36
 
5
37
  * Merged in LE-2037-word-jumble-remove-feedback-se (pull request #278)
@@ -10,6 +10,7 @@
10
10
  outlined
11
11
  :class="cardClass"
12
12
  style="height: 90%"
13
+ tabindex="0"
13
14
  @keyup.enter="toggleCard"
14
15
  @click="toggleCard"
15
16
  >
@@ -27,6 +28,7 @@
27
28
  outlined
28
29
  :class="cardClass"
29
30
  style="height: 90%"
31
+ tabindex="0"
30
32
  @keyup.enter="toggleCard"
31
33
  @click="toggleCard"
32
34
  >
@@ -64,6 +66,7 @@ export default {
64
66
  options: { type: Object, default: {} },
65
67
  slide: { type: Number, required: true, default: 0 },
66
68
  assets: { type: Array, required: true },
69
+ index: { type: Number, required: true, default: 0 },
67
70
  },
68
71
  computed: {
69
72
  cardClass() {
@@ -109,8 +112,22 @@ export default {
109
112
  }
110
113
  },
111
114
  },
115
+ watch: {
116
+ slide(newValue) {
117
+ if (newValue === this.index) {
118
+ this.focusCard()
119
+ }
120
+ },
121
+ },
112
122
  mounted() {
113
123
  this.side = true
124
+ // next tick doesn't work here. Need to wait for v-carousel animations
125
+ // to finish to focus on the card even if it is mounted
126
+ if (this.slide === this.index) {
127
+ setTimeout(() => {
128
+ this.focusCard()
129
+ }, 250)
130
+ }
114
131
  },
115
132
  methods: {
116
133
  toggleCard() {
@@ -127,6 +144,17 @@ export default {
127
144
  target.$el.focus()
128
145
  })
129
146
  },
147
+ focusCard() {
148
+ // Focus the currently visible side of the card.
149
+ // Should always be front but just in case, check both refs
150
+ const ref = this.side ? this.$refs.cardFront : this.$refs.cardBack
151
+ if (!ref) return
152
+ // Vuetify components expose $el; native element does not
153
+ const el = ref.$el ? ref.$el : ref
154
+ if (el && typeof el.focus === 'function') {
155
+ el.focus()
156
+ }
157
+ },
130
158
  },
131
159
  }
132
160
  </script>
@@ -59,8 +59,10 @@
59
59
  :style="setBackgroundInCaseHighlight()"
60
60
  >
61
61
  <Flashcard
62
+ ref="flashcard"
62
63
  :options="card"
63
64
  :slide="currentSlide"
65
+ :index="index"
64
66
  :key="seed + '-' + currentSlide"
65
67
  :assets="block.assets"
66
68
  />
@@ -127,12 +129,6 @@ export default {
127
129
  }
128
130
  },
129
131
  computed: {
130
- slide() {
131
- if (this.block.metadata.config.cards.length > 0) {
132
- return this.currentSlide + 1
133
- }
134
- return 0
135
- },
136
132
  styleForText() {
137
133
  // if highlight is set, use black instead of primary
138
134
  if (
@@ -76,12 +76,10 @@
76
76
  group="people"
77
77
  tabindex="0"
78
78
  >
79
- <div
79
+ <TextViewer
80
80
  class="card_text secondary"
81
- color="secondary"
82
- >
83
- {{ mainPrompt['prompt'] }}
84
- </div>
81
+ v-model="mainPrompt['prompt']"
82
+ ></TextViewer>
85
83
  </draggable>
86
84
  </v-card-text>
87
85
  </v-card>
@@ -155,16 +153,14 @@
155
153
  group="people"
156
154
  tabindex="0"
157
155
  >
158
- <div
156
+ <TextViewer
159
157
  class="card_text secondary"
160
- color="secondary"
161
- >
162
- {{
158
+ v-model="
163
159
  block.metadata.config.prompts[
164
160
  startingIndex
165
161
  ][startingIndex].prompt
166
- }}
167
- </div>
162
+ "
163
+ ></TextViewer>
168
164
  </draggable>
169
165
  </v-card-text>
170
166
  </v-card>
@@ -321,10 +317,11 @@ import _ from 'lodash'
321
317
  import draggable from 'vuedraggable'
322
318
  import BaseContentBlock from '~/components/Content/Blocks/BaseContentBlock'
323
319
  import ImageAssetViewer from '~/components/Content/ImageAssetViewer.vue'
320
+ import TextViewer from '~/components/Text/TextViewer'
324
321
 
325
322
  export default {
326
323
  name: 'MatchingGame',
327
- components: { draggable, ImageAssetViewer },
324
+ components: { draggable, ImageAssetViewer, TextViewer },
328
325
  extends: BaseContentBlock,
329
326
  beforeMount() {
330
327
  if (_.isEmpty(this.block)) {
@@ -483,21 +480,10 @@ export default {
483
480
  if (evt.to === evt.from) {
484
481
  return
485
482
  }
486
-
487
483
  const { answerObjects, prompts } = this.block.metadata.config
488
484
  const draggedElement = answerObjects[evt.oldIndex]
489
- // flatten nested prompts array and then find target by either text or image
490
- let target = prompts
491
- .flatMap((outerElement) => outerElement)
492
- .find((element) =>
493
- this.mainPrompt.textOrImage === 'text'
494
- ? element.prompt ===
495
- evt.to.firstChild?.textContent.trim()
496
- : element.fileConfig.asset?.file_asset_id ===
497
- evt.to.firstElementChild?.id
498
- )
499
485
 
500
- this.setFeedback(target, draggedElement)
486
+ this.setFeedback(this.mainPrompt, draggedElement)
501
487
  },
502
488
  setFeedback(target, draggedElement) {
503
489
  //set feedback information here
@@ -565,6 +551,12 @@ export default {
565
551
  .dragArea {
566
552
  min-height: 100%;
567
553
  min-width: 100%;
554
+ height: auto;
555
+ }
556
+ .dragArea /deep/ .text-viewer {
557
+ overflow: visible !important;
558
+ height: auto !important;
559
+ max-height: none !important;
568
560
  }
569
561
  .card-bucket {
570
562
  line-height: 1.1em;
@@ -580,6 +572,7 @@ export default {
580
572
  }
581
573
  .card_text {
582
574
  font-size: large;
575
+ height: auto;
583
576
  }
584
577
  .card-answer-option {
585
578
  min-width: 23%;
@@ -91,7 +91,11 @@
91
91
  {{ word.value }}
92
92
  </p>
93
93
  </v-row>
94
- <v-alert v-if="showFeedback" fluid :class="feedbackStatus">
94
+ <v-alert
95
+ v-if="showFeedback"
96
+ fluid
97
+ :class="feedbackStatus"
98
+ >
95
99
  <v-row class="pa-4 d-flex justify-center">
96
100
  <p class="mb-0 p-text-override">
97
101
  {{
@@ -111,6 +115,7 @@
111
115
  "
112
116
  color="success"
113
117
  elevation="0"
118
+ :ref="'continueButton' + index"
114
119
  @click="onSlideChanged(true)"
115
120
  >{{ $t('shared.forms.continue') }}
116
121
  </v-btn>
@@ -120,7 +125,8 @@
120
125
  'container-feedback-error'
121
126
  "
122
127
  color="error"
123
- @click="onHideFeedback"
128
+ :ref="'tryAgainButton' + index"
129
+ @click="onHideFeedback(index)"
124
130
  >{{
125
131
  $t(
126
132
  'windward.games.components.content.blocks.bucket_game.try_again'
@@ -142,6 +148,7 @@
142
148
  <v-row class="d-flex mt-8">
143
149
  <v-col class="d-flex justify-end">
144
150
  <v-btn
151
+ :ref="'checkAnswer' + index"
145
152
  :disabled="disableButtons"
146
153
  color="primary"
147
154
  elevation="0"
@@ -297,15 +304,20 @@ export default {
297
304
  this.showAnswer = true
298
305
  this.disableButtons = true
299
306
  },
300
- onHideFeedback() {
307
+ onHideFeedback(index) {
301
308
  this.showFeedback = false
302
309
  this.disableButtons = false
310
+ this.$nextTick(() => {
311
+ const buttonLabel = 'checkAnswer' + index
312
+ this.$refs[buttonLabel][0].$el.focus()
313
+ })
303
314
  },
304
315
  onCheckAnswer(word) {
305
316
  this.studentResponse = this.studentResponse.toLowerCase()
306
317
  this.showFeedback = true
307
318
  this.disableButtons = true
308
319
  const answer = word.value.toLowerCase()
320
+ const wordIndex = this.block.metadata.config.words.indexOf(word)
309
321
  if (
310
322
  !_.isEmpty(this.studentResponse) &&
311
323
  this.studentResponse === answer
@@ -323,6 +335,10 @@ export default {
323
335
  'windward.games.components.content.blocks.word_jumble.correct'
324
336
  )
325
337
  }
338
+ this.$nextTick(() => {
339
+ const buttonLabel = 'continueButton' + wordIndex
340
+ this.$refs[buttonLabel][0].$el.focus()
341
+ })
326
342
  } else {
327
343
  // updates class
328
344
  this.feedbackStatus = 'container-feedback-error'
@@ -338,6 +354,10 @@ export default {
338
354
  'windward.games.components.content.blocks.word_jumble.incorrect'
339
355
  )
340
356
  }
357
+ this.$nextTick(() => {
358
+ const buttonLabel = 'tryAgainButton' + wordIndex
359
+ this.$refs[buttonLabel][0].$el.focus()
360
+ })
341
361
  }
342
362
  },
343
363
  onSlideChanged(changeSlide) {
@@ -72,9 +72,11 @@
72
72
  <span v-if="prompt.textOrImage === 'text'">
73
73
  {{
74
74
  prompt.prompt
75
- ? block.metadata.config.prompts[
76
- index
77
- ][indexPrompt].prompt
75
+ ? removeHtmlTags(
76
+ block.metadata.config.prompts[
77
+ index
78
+ ][indexPrompt].prompt
79
+ )
78
80
  : $t(
79
81
  'windward.games.components.content.blocks.matching_game.click_here'
80
82
  )
@@ -143,30 +145,24 @@
143
145
  </v-row>
144
146
  </v-container>
145
147
  <v-container class="pa-0">
146
- <v-textarea
148
+ <TextEditor
147
149
  v-if="prompt.textOrImage === 'text'"
148
150
  v-model="
149
151
  block.metadata.config.prompts[
150
152
  index
151
153
  ][indexPrompt].prompt
152
154
  "
153
- :rules="
154
- $Validation.getRule('longInput')
155
- "
156
- :counter="
157
- $Validation.getLimit(
158
- 'longInput'
159
- )
160
- "
161
155
  :label="
162
156
  $t(
163
157
  'windward.games.components.settings.matching_game.form.prompt_body'
164
158
  )
165
159
  "
166
- outlined
167
- :autofocus="true"
160
+ toolbar="styles | bold italic underline strikethrough removeformat | alignleft aligncenter alignright | mathButton a11yButton reviseText | undo redo"
161
+ menubar="view"
162
+ :default-alignment="'aligncenter'"
168
163
  :disabled="render"
169
- ></v-textarea>
164
+ class="mb-3"
165
+ ></TextEditor>
170
166
  </v-container>
171
167
  <v-container
172
168
  v-if="prompt.textOrImage === 'image'"
@@ -278,6 +274,7 @@ import BaseContentBlockSettings from '~/components/Content/Settings/BaseContentB
278
274
  import SortableExpansionPanel from '~/components/Core/SortableExpansionPanel.vue'
279
275
  import Uuid from '~/helpers/Uuid'
280
276
  import ImageAssetSettings from '~/components/Content/Settings/ImageAssetSettings.vue'
277
+ import TextEditor from '~/components/Text/TextEditor'
281
278
 
282
279
  export default {
283
280
  name: 'MatchingGameManager',
@@ -287,6 +284,7 @@ export default {
287
284
  ImageAssetSettings,
288
285
  BaseContentBlockSettings,
289
286
  PluginRef,
287
+ TextEditor,
290
288
  },
291
289
  beforeMount() {
292
290
  if (_.isEmpty(this.block)) {
@@ -357,6 +355,9 @@ export default {
357
355
  },
358
356
  mounted() {},
359
357
  methods: {
358
+ removeHtmlTags(input) {
359
+ return input.replace(/<\/?[a-z][^>]*(>|$)/gi, '')
360
+ },
360
361
  onAddPrompt(index = 0) {
361
362
  if (this.block.metadata.config.prompts[index] === undefined) {
362
363
  // forces the component to track the state by making this an observable
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@windward/games",
3
- "version": "0.26.0",
3
+ "version": "0.28.0",
4
4
  "description": "Windward UI Plugin Games",
5
5
  "main": "plugin.js",
6
6
  "scripts": {