@windward/games 0.0.3 → 0.0.5

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/components/content/DatableEditor.vue +0 -3
  2. package/components/content/blocks/crosswordPuzzle/Crossword.ts +553 -0
  3. package/components/content/blocks/crosswordPuzzle/CrosswordClues.vue +91 -0
  4. package/components/content/blocks/crosswordPuzzle/CrosswordElements.ts +38 -0
  5. package/components/content/blocks/crosswordPuzzle/CrosswordPuzzle.vue +673 -0
  6. package/components/content/blocks/multipleChoice/MultipleChoice.vue +187 -127
  7. package/components/content/blocks/multipleChoice/QuestionDialog.vue +37 -13
  8. package/components/content/blocks/sevenStrikes/SevenStikes.vue +368 -0
  9. package/components/content/blocks/sevenStrikes/keyboard.vue +71 -0
  10. package/components/content/blocks/wordJumble/Jumble.vue +2 -10
  11. package/components/content/blocks/wordJumble/WordJumble.vue +22 -10
  12. package/components/settings/CrosswordPuzzleSettingsManager.vue +271 -0
  13. package/components/settings/MultipleChoiceSettingsManager.vue +21 -7
  14. package/components/settings/SevenStrikesSettingsManager.vue +288 -0
  15. package/components/settings/WordJumbleSettingsManager.vue +4 -1
  16. package/i18n/en-US/components/content/blocks/crossword.ts +22 -0
  17. package/i18n/en-US/components/content/blocks/index.ts +4 -0
  18. package/i18n/en-US/components/content/blocks/multiple_choice.ts +2 -4
  19. package/i18n/en-US/components/content/blocks/seven_strikes.ts +6 -0
  20. package/i18n/en-US/components/settings/crossword.ts +7 -0
  21. package/i18n/en-US/components/settings/index.ts +4 -0
  22. package/i18n/en-US/components/settings/multiple_choice.ts +1 -1
  23. package/i18n/en-US/components/settings/seven_strikes.ts +8 -0
  24. package/i18n/en-US/components/settings/word_jumble.ts +1 -1
  25. package/i18n/en-US/shared/content_blocks.ts +2 -0
  26. package/i18n/en-US/shared/settings.ts +2 -0
  27. package/package.json +2 -1
  28. package/plugin.js +43 -1
  29. package/test/blocks/crossword/CrosswordPuzzle.spec.js +49 -0
  30. package/test/blocks/sevenStrikes/sevenStrikes.spec.js +24 -0
  31. package/test/settings/BucketGameManager.spec.js +1 -1
  32. package/test/settings/CrosswordPuzzleManager.spec.js +103 -0
  33. package/test/settings/SevenStrikesManager.spec.js +53 -0
  34. package/test/settings/WordJumbleManager.spec.js +2 -2
@@ -0,0 +1,368 @@
1
+ <template>
2
+ <div>
3
+ <v-col class="pa-0">
4
+ <h3>
5
+ {{
6
+ block.metadata.config.title
7
+ ? block.metadata.config.title
8
+ : $t(
9
+ 'plugin.games.components.content.blocks.seven_strikes.title'
10
+ )
11
+ }}
12
+ </h3>
13
+ <p>{{ block.metadata.config.instructions }}</p>
14
+ </v-col>
15
+ <v-col class="pa-0">
16
+ <template>
17
+ <v-carousel
18
+ v-model="carouselIndex"
19
+ @change="onSlideChanged($event)"
20
+ >
21
+ <v-carousel-item
22
+ v-for="(word, index) in block.metadata.config.words"
23
+ :key="index"
24
+ >
25
+ <v-row class="d-flex justify-center ma-2">
26
+ <p class="pa-3 mb-0 clueAndJumble">
27
+ {{ word.hint }}
28
+ </p>
29
+ </v-row>
30
+ <v-container
31
+ v-if="showFeedback"
32
+ :key="'feedback'"
33
+ :class="feedbackStatus"
34
+ class="mb-8"
35
+ >
36
+ <v-row class="d-flex" align="center">
37
+ <v-col cols="4"></v-col>
38
+ <v-col cols="4" class="d-flex justify-center">
39
+ <p class="">{{ feedback }}</p>
40
+ </v-col>
41
+ <v-col cols="4" class="d-flex justify-end">
42
+ <v-icon @click="onHideFeedback">
43
+ mdi-alpha-x-circle-outline</v-icon
44
+ >
45
+ </v-col>
46
+ </v-row>
47
+ </v-container>
48
+ <v-row class="justify-center">
49
+ <div
50
+ v-for="(
51
+ strike, strikeIndex
52
+ ) in sevenStrikesCounter"
53
+ :key="strikeIndex"
54
+ >
55
+ <div
56
+ v-if="strike.strike === false"
57
+ :ref="'strike' + strikeIndex"
58
+ class="strikeArea ml-1 mr-1 d-flex justify-center align-center"
59
+ maxlength="1"
60
+ ></div>
61
+ <div
62
+ class="strike"
63
+ v-if="strike.strike === true"
64
+ >
65
+ X
66
+ </div>
67
+ </div>
68
+ </v-row>
69
+ <v-row
70
+ class="justify-center mt-12"
71
+ v-if="word.splitWord"
72
+ >
73
+ <div
74
+ v-for="(letter, splitIndex) in word.splitWord"
75
+ :key="splitIndex"
76
+ >
77
+ <div
78
+ :ref="'input' + splitIndex"
79
+ class="textArea ml-1 mr-1"
80
+ maxlength="1"
81
+ >
82
+ <div v-if="letter.show === true">
83
+ {{ letter.letter }}
84
+ </div>
85
+ </div>
86
+ </div>
87
+ </v-row>
88
+ <div>
89
+ <keyboard
90
+ v-if="!showFeedback"
91
+ :key="index"
92
+ :keyboardClass="keyBoardClass + index"
93
+ @onChange="onChange"
94
+ @onKeyPress="onKeyPress"
95
+ :input="input"
96
+ />
97
+ </div>
98
+ <v-row class="justify-center mt-12" v-if="showFeedback">
99
+ <v-btn
100
+ color="primary ml-4"
101
+ @click="onRevealAnswer"
102
+ >{{
103
+ $t(
104
+ 'plugin.games.components.content.blocks.seven_strikes.reveal'
105
+ )
106
+ }}</v-btn
107
+ >
108
+ <v-btn
109
+ color="primary ml-4"
110
+ @click="onSlideChanged(index)"
111
+ >{{
112
+ $t(
113
+ 'plugin.games.components.content.blocks.seven_strikes.again'
114
+ )
115
+ }}</v-btn
116
+ >
117
+ <v-btn
118
+ @click="onChangeSlide(index + 1)"
119
+ color="primary ml-4"
120
+ >{{
121
+ $t(
122
+ 'plugin.games.components.content.blocks.seven_strikes.next'
123
+ )
124
+ }}</v-btn
125
+ >
126
+ </v-row>
127
+ </v-carousel-item>
128
+ </v-carousel>
129
+ </template>
130
+ </v-col>
131
+ </div>
132
+ </template>
133
+
134
+ <script>
135
+ import _ from 'lodash'
136
+ import draggable from 'vuedraggable'
137
+ import keyboard from './keyboard.vue'
138
+ import CrudTable from '../../CrudTable'
139
+ import BaseContentBlock from '~/components/Content/Blocks/BaseContentBlock'
140
+
141
+ export default {
142
+ name: 'SevenStrikesGame',
143
+ components: { CrudTable, draggable, keyboard },
144
+ extends: BaseContentBlock,
145
+ beforeMount() {
146
+ if (_.isEmpty(this.block)) {
147
+ this.block = {}
148
+ }
149
+ if (_.isEmpty(this.block.metadata)) {
150
+ this.block.metadata = {}
151
+ }
152
+ if (_.isEmpty(this.block.metadata.config)) {
153
+ this.block.metadata.config = {}
154
+ }
155
+ if (_.isEmpty(this.block.metadata.config.words)) {
156
+ this.block.metadata.config.words = []
157
+ }
158
+ },
159
+ data() {
160
+ return {
161
+ feedback: '',
162
+ feedbackStatus: '',
163
+ showFeedback: false,
164
+ input: '',
165
+ sevenStrikesCounter: [
166
+ { strike: false },
167
+ { strike: false },
168
+ { strike: false },
169
+ { strike: false },
170
+ { strike: false },
171
+ { strike: false },
172
+ { strike: false },
173
+ ],
174
+ keyBoardClass: 'simple-keyboard',
175
+ onSlide: 0,
176
+ onStrike: 0,
177
+ carouselIndex: 0,
178
+ }
179
+ },
180
+ mounted() {},
181
+ methods: {
182
+ onChangeSlide(newIndex) {
183
+ if (newIndex >= this.block.metadata.config.words.length) {
184
+ newIndex = 0
185
+ }
186
+ this.carouselIndex = newIndex
187
+ this.onSlideChanged(newIndex)
188
+ },
189
+ onChange(input) {
190
+ this.input = input
191
+ },
192
+ onKeyPress(letter) {
193
+ // can't play when editing
194
+ if (this.render) {
195
+ // to lowercase
196
+ letter = letter.toLowerCase()
197
+ // check if word value contains input letter
198
+ const doesContain =
199
+ this.block.metadata.config.words[
200
+ this.onSlide
201
+ ].value.includes(letter)
202
+ if (doesContain) {
203
+ // loop over to show letters in case there are multiple of the same
204
+ this.block.metadata.config.words[
205
+ this.onSlide
206
+ ].splitWord.forEach((character) => {
207
+ if (character.letter === letter) {
208
+ character.show = true
209
+ }
210
+ })
211
+ } else {
212
+ // checks if the user is already out of strikes
213
+ if (this.onStrike <= 6) {
214
+ this.sevenStrikesCounter[this.onStrike].strike = true
215
+ this.onStrike = this.onStrike + 1
216
+ }
217
+ }
218
+ const isComplete = this.checkIfComplete()
219
+ const checkStrikes = this.checkStrikes()
220
+ if (isComplete) {
221
+ this.thatIsCorrect()
222
+ }
223
+ if (checkStrikes) {
224
+ this.thatIsIncorrect()
225
+ }
226
+ }
227
+ },
228
+ checkIfComplete() {
229
+ // checks to see if the user guessed all the letters correctly
230
+ let finished = true
231
+ this.block.metadata.config.words[this.onSlide].splitWord.forEach(
232
+ (element) => {
233
+ if (element.show === false) {
234
+ finished = false
235
+ }
236
+ }
237
+ )
238
+ return finished
239
+ },
240
+ checkStrikes() {
241
+ // checks to see if user has exhausted strikes
242
+ let outOfStrikes = true
243
+ this.sevenStrikesCounter.forEach((element) => {
244
+ if (element.strike === false) {
245
+ outOfStrikes = false
246
+ }
247
+ })
248
+ return outOfStrikes
249
+ },
250
+ thatIsIncorrect() {
251
+ this.showFeedback = true
252
+ // updates class
253
+ this.feedbackStatus = 'error'
254
+ // gets custom or standard feedback
255
+ if (
256
+ !_.isEmpty(this.block.metadata.config.feedback_incorrect) &&
257
+ this.block.metadata.config.feedback_incorrect !== ''
258
+ ) {
259
+ this.feedback = this.block.metadata.config.feedback_incorrect
260
+ } else {
261
+ this.feedback = this.$t(
262
+ 'plugin.games.components.content.blocks.word_jumble.incorrect'
263
+ )
264
+ }
265
+ },
266
+ thatIsCorrect() {
267
+ this.showFeedback = true
268
+ this.feedbackStatus = 'success'
269
+ if (
270
+ !_.isEmpty(this.block.metadata.config.feedback_correct) &&
271
+ this.block.metadata.config.feedback_correct !== ''
272
+ ) {
273
+ this.feedback = this.block.metadata.config.feedback_correct
274
+ } else {
275
+ this.feedback = this.$t(
276
+ 'plugin.games.components.content.blocks.word_jumble.correct'
277
+ )
278
+ }
279
+ },
280
+ onHideFeedback() {
281
+ // fired from feedback x
282
+ this.showFeedback = false
283
+ this.onSlideChanged(this.onSlide)
284
+ },
285
+ onInputChange(input) {
286
+ // catches target input
287
+ this.input = input.target.value
288
+ },
289
+ onSlideChanged(event) {
290
+ // this function is called when the slide is changed
291
+ // manually resetting is necessary due to fact that components do not remount
292
+ // event hold slide index
293
+ this.onSlide = event
294
+ // hides feedback if showing
295
+ this.showFeedback = false
296
+ this.feedbackStatus = ''
297
+ this.onStrike = 0
298
+ this.feedback = this.$t(
299
+ 'plugin.games.components.content.blocks.word_jumble.feedback'
300
+ )
301
+ // resets strikes
302
+ this.sevenStrikesCounter.forEach((element) => {
303
+ element.strike = false
304
+ })
305
+ if (
306
+ this.block.metadata.config.words[this.onSlide].splitWord &&
307
+ this.block.metadata.config.words[this.onSlide].splitWord
308
+ .length > 0
309
+ ) {
310
+ // rehides the displayed correct letters
311
+ this.block.metadata.config.words[
312
+ this.onSlide
313
+ ].splitWord.forEach((word) => {
314
+ if (word.show) {
315
+ word.show = false
316
+ }
317
+ })
318
+ }
319
+ },
320
+ onRevealAnswer() {
321
+ this.block.metadata.config.words[this.onSlide].splitWord.forEach(
322
+ (word) => {
323
+ word.show = true
324
+ }
325
+ )
326
+ },
327
+ },
328
+ }
329
+ </script>
330
+
331
+ <style scoped>
332
+ .outline {
333
+ border: 2px solid black;
334
+ border-radius: 10px;
335
+ }
336
+ .clueAndJumble {
337
+ font-size: 20px;
338
+ }
339
+ .spanBold {
340
+ font-weight: 900;
341
+ }
342
+ .error {
343
+ border: dashed 2px #dc3d1a;
344
+ background-color: #fff1f1;
345
+ }
346
+ .success {
347
+ border: dashed 2px #76b778;
348
+ background-color: #f1fff3;
349
+ }
350
+ .textArea {
351
+ width: 20px;
352
+ height: 20px;
353
+ border-bottom: 2px solid black;
354
+ display: flex;
355
+ justify-content: center;
356
+ align-items: center;
357
+ }
358
+ .strikeArea {
359
+ width: 40px;
360
+ height: 40px;
361
+ border: 2px solid black;
362
+ }
363
+ .strike {
364
+ color: #dc3d1a;
365
+ font-size: 40px;
366
+ margin: 10px;
367
+ }
368
+ </style>
@@ -0,0 +1,71 @@
1
+ <template>
2
+ <div :class="keyboardClass"></div>
3
+ </template>
4
+
5
+ <script>
6
+ import Keyboard from 'simple-keyboard'
7
+ import 'simple-keyboard/build/css/index.css'
8
+
9
+ export default {
10
+ name: 'SimpleKeyboard',
11
+ props: {
12
+ keyboardClass: {
13
+ default: 'simple-keyboard',
14
+ type: String,
15
+ },
16
+ input: {
17
+ type: String,
18
+ },
19
+ },
20
+ data: () => ({
21
+ keyboard: null,
22
+ }),
23
+ mounted() {
24
+ this.keyboard = new Keyboard(this.keyboardClass, {
25
+ layout: {
26
+ default: [
27
+ 'q w e r t y u i o p',
28
+ 'a s d f g h j k l',
29
+ 'z x c v b n m',
30
+ ],
31
+ },
32
+ onChange: this.onChange,
33
+ onKeyPress: this.onKeyPress,
34
+ })
35
+ },
36
+ methods: {
37
+ onChange(input) {
38
+ this.$emit('onChange', input)
39
+ },
40
+ onKeyPress(button) {
41
+ this.$emit('onKeyPress', button)
42
+
43
+ /**
44
+ * If you want to handle the shift and caps lock buttons
45
+ */
46
+ if (button === '{shift}' || button === '{lock}') this.handleShift()
47
+ },
48
+ handleShift() {
49
+ let currentLayout = this.keyboard.options.layoutName
50
+ let shiftToggle = currentLayout === 'default' ? 'shift' : 'default'
51
+
52
+ this.keyboard.setOptions({
53
+ layoutName: shiftToggle,
54
+ })
55
+ },
56
+ },
57
+ watch: {
58
+ input(input) {
59
+ this.keyboard.setInput(input)
60
+ },
61
+ },
62
+ }
63
+ </script>
64
+
65
+ <!-- Add "scoped" attribute to limit CSS to this component only -->
66
+ <style scoped>
67
+ .hg-theme-default {
68
+ padding: 55px;
69
+ margin-top: 25px;
70
+ }
71
+ </style>
@@ -9,7 +9,6 @@
9
9
  class="textArea ml-1 mr-1"
10
10
  maxlength="1"
11
11
  @input="onInput($event, index)"
12
- v-on:keydown="onSpaceHandler($event)"
13
12
  ></v-text-field>
14
13
  </div>
15
14
  </v-row>
@@ -43,8 +42,8 @@ export default {
43
42
  handler(newValue) {
44
43
  // removes students answer from input if they got it correct
45
44
  this.splitUpWord()
46
- }
47
- }
45
+ },
46
+ },
48
47
  },
49
48
  data() {
50
49
  return {
@@ -71,13 +70,6 @@ export default {
71
70
  }
72
71
  }
73
72
  },
74
- onSpaceHandler(event) {
75
- // prevents empty space from being entered
76
- if (event.keyCode === 32) {
77
- event.preventDefault()
78
- return false
79
- }
80
- },
81
73
  onInput(event, index) {
82
74
  // handles focusing on next element after student enters a letter
83
75
  if (!_.isEmpty(event) && this.$refs['input' + (index + 1)]) {
@@ -19,7 +19,7 @@
19
19
  v-for="(word, index) in block.metadata.config.words"
20
20
  :key="index"
21
21
  >
22
- <v-row class="d-flex justify-center outline ma-2">
22
+ <v-row class="d-flex justify-center ma-2">
23
23
  <p class="pa-3 mb-0 clueAndJumble">
24
24
  <span class="spanBold"
25
25
  >{{
@@ -36,15 +36,22 @@
36
36
  {{ shuffle(word.value) }}
37
37
  </p>
38
38
  </v-row>
39
- <v-container :key="'feedback'" :class="feedbackStatus">
40
- <br />
41
- <v-row
42
- class="d-flex justify-space-around"
43
- align="center"
44
- justify="center"
45
- >{{ feedback }}
39
+ <v-container
40
+ v-if="showFeedback"
41
+ :key="'feedback'"
42
+ :class="feedbackStatus"
43
+ >
44
+ <v-row class="d-flex" align="center">
45
+ <v-col cols="4"></v-col>
46
+ <v-col cols="4" class="d-flex justify-center">
47
+ <p class="">{{ feedback }}</p>
48
+ </v-col>
49
+ <v-col cols="4" class="d-flex justify-end">
50
+ <v-icon @click="onHideFeedback">
51
+ mdi-alpha-x-circle-outline</v-icon
52
+ >
53
+ </v-col>
46
54
  </v-row>
47
- <br />
48
55
  </v-container>
49
56
  <v-row class="justify-center mt-4">
50
57
  <Jumble
@@ -124,6 +131,7 @@ export default {
124
131
  'plugin.games.components.content.blocks.word_jumble.feedback'
125
132
  ),
126
133
  showAnswer: false,
134
+ showFeedback: false,
127
135
  resetValue: false,
128
136
  studentResponse: '',
129
137
  feedbackStatus: '',
@@ -166,8 +174,12 @@ export default {
166
174
  // reveal prop changed to true to show answer
167
175
  this.showAnswer = true
168
176
  },
177
+ onHideFeedback() {
178
+ this.showFeedback = false
179
+ },
169
180
  onCheckAnswer(word) {
170
181
  this.studentResponse = this.studentResponse.toLowerCase()
182
+ this.showFeedback = true
171
183
  const answer = word.value.toLowerCase()
172
184
  if (this.studentResponse === answer) {
173
185
  // updates class
@@ -202,7 +214,7 @@ export default {
202
214
  },
203
215
  onSlideChanged() {
204
216
  // this function is called when the slide is changed
205
- // reset the game each time this occurs via props due to fact
217
+ // reset the game each time this occurs via props due to fact
206
218
  // that components do not remount on slides each time the slide is revisited
207
219
  // updates class
208
220
  this.feedbackStatus = ''