fcad-core-dragon 2.1.1 → 2.1.2

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 (160) hide show
  1. package/.editorconfig +7 -7
  2. package/.gitlab-ci.yml +124 -0
  3. package/.prettierrc +11 -11
  4. package/.vscode/extensions.json +8 -8
  5. package/.vscode/settings.json +46 -16
  6. package/CHANGELOG +520 -520
  7. package/README.md +57 -57
  8. package/documentation/.vitepress/config.js +114 -114
  9. package/documentation/api-examples.md +49 -49
  10. package/documentation/composants/app-base-button.md +58 -58
  11. package/documentation/composants/app-base-error-display.md +59 -59
  12. package/documentation/composants/app-base-popover.md +68 -68
  13. package/documentation/composants/app-comp-audio.md +75 -75
  14. package/documentation/composants/app-comp-branch-buttons.md +111 -111
  15. package/documentation/composants/app-comp-button-progress.md +53 -53
  16. package/documentation/composants/app-comp-carousel.md +53 -53
  17. package/documentation/composants/app-comp-container.md +53 -53
  18. package/documentation/composants/app-comp-input-checkbox-next.md +42 -42
  19. package/documentation/composants/app-comp-input-dropdown-next.md +34 -34
  20. package/documentation/composants/app-comp-input-radio-next.md +39 -39
  21. package/documentation/composants/app-comp-input-text-next.md +35 -35
  22. package/documentation/composants/app-comp-input-text-table-next.md +34 -34
  23. package/documentation/composants/app-comp-input-text-to-fill-dropdown-next.md +53 -53
  24. package/documentation/composants/app-comp-input-text-to-fill-next.md +31 -31
  25. package/documentation/composants/app-comp-jauge.md +31 -31
  26. package/documentation/composants/app-comp-menu-item.md +55 -55
  27. package/documentation/composants/app-comp-menu.md +29 -29
  28. package/documentation/composants/app-comp-navigation.md +41 -41
  29. package/documentation/composants/app-comp-note-call.md +53 -53
  30. package/documentation/composants/app-comp-note-credit.md +53 -53
  31. package/documentation/composants/app-comp-play-bar-next.md +53 -53
  32. package/documentation/composants/app-comp-pop-up-next.md +93 -93
  33. package/documentation/composants/app-comp-quiz-next.md +235 -235
  34. package/documentation/composants/app-comp-quiz-recall.md +53 -53
  35. package/documentation/composants/app-comp-svg-next.md +53 -53
  36. package/documentation/composants/app-comp-table-of-content.md +50 -50
  37. package/documentation/composants/app-comp-video-player.md +82 -82
  38. package/documentation/composants.md +46 -46
  39. package/documentation/composants_critiques/ModelPageComposant.md +53 -53
  40. package/documentation/composants_critiques/app-base-module.md +43 -43
  41. package/documentation/composants_critiques/app-base-page.md +48 -48
  42. package/documentation/composants_critiques/app-base.md +311 -311
  43. package/documentation/composants_critiques/main.md +15 -15
  44. package/documentation/demarrage.md +50 -50
  45. package/documentation/deploiement.md +57 -57
  46. package/documentation/index.md +33 -33
  47. package/documentation/markdown-examples.md +85 -85
  48. package/documentation/public/vite.svg +14 -14
  49. package/documentation/public/vuejs.svg +1 -1
  50. package/documentation/public/vuetify.svg +5 -5
  51. package/eslint.config.js +60 -60
  52. package/junit-report.xml +182 -0
  53. package/package.json +66 -59
  54. package/playwright/index.html +12 -0
  55. package/playwright/index.js +21 -0
  56. package/playwright-ct.config.js +95 -0
  57. package/src/$locales/en.json +157 -157
  58. package/src/$locales/fr.json +120 -120
  59. package/src/assets/data/onboardingMessages.json +47 -47
  60. package/src/components/AppBase.vue +1171 -1169
  61. package/src/components/AppBaseButton.vue +90 -95
  62. package/src/components/AppBaseErrorDisplay.vue +438 -438
  63. package/src/components/AppBaseFlipCard.vue +84 -84
  64. package/src/components/AppBaseModule.vue +1639 -1634
  65. package/src/components/AppBasePage.vue +867 -866
  66. package/src/components/AppBasePopover.vue +41 -41
  67. package/src/components/AppBaseSkeleton.vue +66 -66
  68. package/src/components/AppCompAudio.vue +261 -256
  69. package/src/components/AppCompBranchButtons.vue +508 -508
  70. package/src/components/AppCompButtonProgress.vue +137 -132
  71. package/src/components/AppCompCarousel.vue +342 -336
  72. package/src/components/AppCompContainer.vue +29 -29
  73. package/src/components/AppCompInputCheckBoxNx.vue +325 -323
  74. package/src/components/AppCompInputDropdownNx.vue +302 -299
  75. package/src/components/AppCompInputRadioNx.vue +287 -284
  76. package/src/components/AppCompInputTextNx.vue +156 -153
  77. package/src/components/AppCompInputTextTableNx.vue +205 -202
  78. package/src/components/AppCompInputTextToFillDropdownNx.vue +343 -340
  79. package/src/components/AppCompInputTextToFillNx.vue +316 -313
  80. package/src/components/AppCompJauge.vue +81 -81
  81. package/src/components/AppCompMenu.vue +6 -1
  82. package/src/components/AppCompMenuItem.vue +246 -240
  83. package/src/components/AppCompNavigation.vue +977 -972
  84. package/src/components/AppCompNoteCall.vue +167 -161
  85. package/src/components/AppCompNoteCredit.vue +496 -491
  86. package/src/components/AppCompPlayBarNext.vue +2290 -2288
  87. package/src/components/AppCompPopUpNext.vue +508 -504
  88. package/src/components/AppCompQuizNext.vue +515 -510
  89. package/src/components/AppCompQuizRecall.vue +355 -350
  90. package/src/components/AppCompSVGNext.vue +346 -346
  91. package/src/components/AppCompSettingsMenu.vue +177 -172
  92. package/src/components/AppCompTableOfContent.vue +433 -427
  93. package/src/components/AppCompVideoPlayer.vue +377 -377
  94. package/src/components/AppCompViewDisplay.vue +6 -6
  95. package/src/components/BaseModule.vue +55 -55
  96. package/src/composables/useIdleDetector.js +56 -56
  97. package/src/composables/useQuiz.js +89 -89
  98. package/src/composables/useTimer.js +172 -172
  99. package/src/directives/nvdaFix.js +53 -53
  100. package/src/externalComps/ModuleView.vue +22 -22
  101. package/src/externalComps/SummaryView.vue +91 -91
  102. package/src/main.js +493 -476
  103. package/src/module/stores/appStore.js +960 -947
  104. package/src/module/xapi/ADL.js +520 -520
  105. package/src/module/xapi/Crypto/Hasher.js +241 -241
  106. package/src/module/xapi/Crypto/WordArray.js +278 -278
  107. package/src/module/xapi/Crypto/algorithms/BufferedBlockAlgorithm.js +103 -103
  108. package/src/module/xapi/Crypto/algorithms/C_algo.js +315 -315
  109. package/src/module/xapi/Crypto/algorithms/HMAC.js +9 -9
  110. package/src/module/xapi/Crypto/algorithms/SHA1.js +9 -9
  111. package/src/module/xapi/Crypto/encoders/Base.js +105 -105
  112. package/src/module/xapi/Crypto/encoders/Base64.js +99 -99
  113. package/src/module/xapi/Crypto/encoders/Hex.js +61 -61
  114. package/src/module/xapi/Crypto/encoders/Latin1.js +61 -61
  115. package/src/module/xapi/Crypto/encoders/Utf8.js +45 -45
  116. package/src/module/xapi/Crypto/index.js +53 -53
  117. package/src/module/xapi/Statement/activity.js +47 -47
  118. package/src/module/xapi/Statement/agent.js +55 -55
  119. package/src/module/xapi/Statement/group.js +26 -26
  120. package/src/module/xapi/Statement/index.js +259 -259
  121. package/src/module/xapi/Statement/statement.js +253 -253
  122. package/src/module/xapi/Statement/statementRef.js +23 -23
  123. package/src/module/xapi/Statement/substatement.js +22 -22
  124. package/src/module/xapi/Statement/verb.js +36 -36
  125. package/src/module/xapi/activitytypes.js +17 -17
  126. package/src/module/xapi/launch.js +157 -157
  127. package/src/module/xapi/utils.js +167 -167
  128. package/src/module/xapi/verbs.js +294 -294
  129. package/src/module/xapi/wrapper.js +1895 -1895
  130. package/src/module/xapi/xapiStatement.js +444 -444
  131. package/src/plugins/analytics.js +34 -34
  132. package/src/plugins/bus.js +12 -8
  133. package/src/plugins/gsap.js +17 -17
  134. package/src/plugins/helper.js +355 -358
  135. package/src/plugins/i18n.js +27 -26
  136. package/src/plugins/idb.js +227 -227
  137. package/src/plugins/save.js +37 -37
  138. package/src/plugins/scorm.js +287 -287
  139. package/src/plugins/xapi.js +11 -11
  140. package/src/public/index.html +33 -33
  141. package/src/router/index.js +57 -57
  142. package/src/router/routes.js +312 -312
  143. package/src/shared/generalfuncs.js +344 -344
  144. package/src/shared/validators.js +1018 -1018
  145. package/tests/component/AppBaseButton.spec.js +53 -0
  146. package/tests/component/pinia.spec.js +24 -0
  147. package/{src/components/tests__ → tests/unit}/AppBaseButton.spec.js +53 -53
  148. package/tests/unit/AppCompInputCheckBoxNx.spec.js +59 -0
  149. package/tests/unit/AppCompInputDropdownNx.spec.js +51 -0
  150. package/tests/unit/AppCompInputRadioNx.spec.js +59 -0
  151. package/tests/unit/AppCompInputTextNx.spec.js +44 -0
  152. package/tests/unit/AppCompInputTextTableNx.spec.js +77 -0
  153. package/tests/unit/AppCompInputTextToFillDropdownNx.spec.js +60 -0
  154. package/tests/unit/AppCompInputTextToFillNx.spec.js +45 -0
  155. package/tests/unit/AppCompQuizNext.spec.js +114 -0
  156. package/tests/unit/AppCompVideoPlayer.spec.js +177 -0
  157. package/{src/components/tests__ → tests/unit}/useTimer.spec.js +91 -91
  158. package/vitest.config.js +28 -19
  159. package/vitest.setup.js +28 -0
  160. package/src/components/AppBaseButton.test.js +0 -21
@@ -1,340 +1,343 @@
1
- <!-- About this Component--
2
- * Renders a Series of inputs to collect input for the Quiz component.
3
- * Related Quiz to question: TEXTE_TROUE_SELECT
4
- * Receives the a data object defined by user
5
- * Used by AppCompQuiz
6
- * Uses useQuiz composable
7
- -->
8
- <template>
9
- <div v-if="inputData.length > 0" :id="id" class="input-box">
10
- <fieldset :aria-label="fieldsetLabel">
11
- <div
12
- v-for="textInput in inputElements"
13
- :key="textInput.id"
14
- class="texteatrou"
15
- >
16
- <span
17
- v-if="textInput.type == 'text' && textInput.content.trim() !== ''"
18
- v-html="textInput.content"
19
- ></span>
20
- <label :for="`${id}_${textInput.id}-champ`" style="display: none">
21
- {{ $t('text.quiz') }}
22
- </label>
23
- <div
24
- v-if="textInput.type == 'select'"
25
- class="cnt-input"
26
- :class="`${retro[textInput.id.substr(textInput.id.length - 1)] ? retro[textInput.id.substr(textInput.id.length - 1)] : ''}`"
27
- >
28
- <v-select
29
- :id="`${id}_${textInput.id}-champ`"
30
- :model-value="inputsValue[textInput.index]"
31
- item-title="text"
32
- :open-text="`${$t('message.dropdown_list')}`"
33
- :item-props="true"
34
- :items="getItemOptions(textInput.index)"
35
- :aria-describedby="`${id}_${textInput.id}-msg-erreur`"
36
- :aria-labelledby="`${id}_${textInput.id}-label`"
37
- @update:model-value="onSelectUpdate($event, textInput.index)"
38
- />
39
- <span
40
- :id="`${id}_${textInput.id}-msg-erreur`"
41
- :key="`msg_chx_${id}_${textInput.id}`"
42
- class="sr-only"
43
- >
44
- {{ messageAccessibility[textInput.index] }}
45
- </span>
46
- </div>
47
- </div>
48
- </fieldset>
49
- </div>
50
- </template>
51
- <script>
52
- import { useQuiz } from '../composables/useQuiz'
53
- export default {
54
- name: 'AppCompInputTextToFillDropdownNext',
55
- /* PROPS USED FOR PARENT TO COMMUNICATE DATA TO CHILD */
56
- props: {
57
- modelValue: {
58
- type: Array,
59
- default: () => []
60
- },
61
- inputData: {
62
- type: Array,
63
- default: () => []
64
- },
65
- textBase: {
66
- type: String,
67
- default: ''
68
- },
69
- solution: {
70
- type: Array,
71
- default: () => []
72
- },
73
-
74
- id: {
75
- type: String,
76
- default: ''
77
- }
78
- },
79
- emits: ['update:modelValue', 'enable-submit'],
80
- setup(props) {
81
- const { retroType, addRetroStyle, resetRetroStyle } = useQuiz()
82
-
83
- return { retroType, addRetroStyle, resetRetroStyle }
84
- },
85
- data() {
86
- return {
87
- inputElements: [],
88
- result: [],
89
- retro: [],
90
- messageAccessibility: [],
91
- inputCount: null
92
- }
93
- },
94
- computed: {
95
- fieldsetLabel() {
96
- return `${this.$t('quizState.textToFillDropdownFieldset1')} ${this.inputCount} ${this.$t('quizState.textToFillDropdownFieldset2')}`
97
- },
98
- /* INTERNAL REACTIVE VALUE THAT BIND WITH MODELVALUE PROPS */
99
- inputsValue: {
100
- get() {
101
- return this.modelValue
102
- },
103
- set(newValue) {
104
- this.resetRetroStyle([this.retro, this.messageAccessibility])
105
- this.$emit('update:modelValue', [...newValue])
106
- }
107
- },
108
- /**
109
- * internal reactive value that map the options to add SELECTED attribute on each option
110
- * and Add entrie for the default selected Elements
111
- * this attribute value can be changed when user select the option
112
- */
113
- mappedOptions() {
114
- return this.inputData.map((op) => {
115
- const key = Object.keys(op)[0]
116
- const values = Object.values(op)[0]
117
-
118
- const newValues = [
119
- {
120
- value: null,
121
- disabled: true,
122
- selected: true,
123
- text: this.$t('message.first_option_dropdown')
124
- },
125
- ...values.map((el) => ({
126
- value: el,
127
- text: el,
128
- selected: false
129
- }))
130
- ]
131
-
132
- return {
133
- [key]: newValues
134
- }
135
- })
136
- }
137
- },
138
- watch: {
139
- /**
140
- * @description watcher to track the inputs content
141
- * remove an element from the inputs array (modelValue) when it is empty
142
- */
143
- inputsValue: {
144
- handler() {
145
- const filledCount = this.modelValue.filter(
146
- (val) => val !== null && val !== undefined
147
- )
148
-
149
- this.$emit('enable-submit', filledCount.length == this.inputData.length)
150
- },
151
- deep: true,
152
- immediate: true
153
- }
154
- },
155
-
156
- mounted() {},
157
- created() {
158
- this.createTextWithInput(this.textBase)
159
- },
160
-
161
- methods: {
162
- /**
163
- * @description create the object to generate the text and selects options
164
- * for each fillable input. The created object is assigned to inputElements
165
- * @param {String} str the text with holes to fill
166
- */
167
- createTextWithInput(str) {
168
- const regex = /\$%\S*%\$/g // regex pattern to match exp: $%number%$
169
-
170
- let splittedText = str.split(regex)
171
- let listInput = []
172
-
173
- for (let i = 0; i < splittedText.length - 1; i++) {
174
- listInput.push({
175
- id: `text-${i}`,
176
- type: 'text',
177
- content: splittedText[i]
178
- })
179
-
180
- if (i < splittedText.length - 1)
181
- listInput.push({ id: `select-${i}`, type: 'select', index: i })
182
- }
183
- let lI = listInput.findLast((element) => element.type == 'select')
184
- this.inputCount = lI.index + 1
185
-
186
- this.inputElements = listInput
187
- },
188
- /**
189
- * @description Method to force update of this.inputsValue.
190
- * this force the old value and new value of the input to be tracked by VUE.
191
- * @param newValue, the new value of the input
192
- * @param index index of the selected element
193
- */
194
-
195
- onSelectUpdate(newValue, index) {
196
- if (this.$el && this.$el.id !== this.id) return //prevent event from firing on other input
197
- this.resetRetroStyle([this.retro, this.messageAccessibility])
198
- const updated = [...this.inputsValue] //create new array from inputsValue
199
- updated[index] = newValue
200
- this.inputsValue = updated //trigger vue reactivity for inputsValue
201
- },
202
-
203
- /**
204
- * @description Method to retrive the options of each selectable input in the mappedOption Object
205
- * @param {Number } index - number representing the index of the item in the array of inputElements
206
- * @Return a collection of item representing the options for a the selectables
207
- */
208
- getItemOptions(index) {
209
- return Object.values(this.mappedOptions[index])[0]
210
- },
211
- /**
212
- * @description Component validation method to validate the user input against the solution
213
- * can handle logic of Css style to apply (correct/wrong Answer) and return the result
214
- * @return {Object} result
215
- */
216
- validateAnswer() {
217
- let mappedResults
218
-
219
- if (this.solution != null) {
220
- mappedResults = this.inputsValue.map((a, i) => {
221
- const s = Object.values(this.solution[i])[0]
222
- return { selected: a, correct: s == a }
223
- })
224
- } else {
225
- mappedResults = this.inputsValue.map((a) => {
226
- return { selected: a, correct: false }
227
- })
228
- }
229
-
230
- //Set the valition style
231
- let { classRetro, mesA11y } = this.addRetroStyle(
232
- this.solution,
233
- mappedResults,
234
- this.inputsValue.length
235
- )
236
- this.retro = classRetro
237
- this.messageAccessibility = mesA11y
238
- //Retrive the user response from validation
239
- this.result = mappedResults.filter((a) => a.selected)
240
-
241
- let retro = this.retroType(this.solution, this.result)
242
-
243
- let cAns
244
- if (this.solution != null) cAns = this.computeResult(this.result)
245
- else cAns = false
246
- //Return this to parent
247
- return {
248
- userAnswer: this.result,
249
- correctAnswer: cAns,
250
- retroType: retro
251
- }
252
- },
253
-
254
- /**
255
- * @description - compute the correct elements in user response
256
- * compare with the solution and return wether user succeeded or failed
257
- * @param {Object} result - user responses
258
- * @retuns boolean expressing the fail/success result
259
- */
260
- computeResult(result) {
261
- const res = result.filter((el) => el.correct)
262
- return res.length == this.solution.length
263
- }
264
- }
265
- }
266
- </script>
267
- <style lang="scss" scoped>
268
- select {
269
- &.custom-select {
270
- background-image: inherit;
271
-
272
- &:focus {
273
- border-color: inherit;
274
- box-shadow: inherit;
275
- }
276
- }
277
- }
278
-
279
- /***** style dev *****/
280
- .texteatrou {
281
- display: inline;
282
- }
283
- .texteatrou > select {
284
- display: inline;
285
- width: auto;
286
- }
287
-
288
- .cnt-input {
289
- position: relative;
290
- }
291
-
292
- fieldset {
293
- border: inherit;
294
- }
295
-
296
- div.texteatrou {
297
- display: inline !important;
298
-
299
-
300
- .cnt-input {
301
- display: inline;
302
- .v-input {
303
- display: inline-block !important;
304
-
305
-
306
-
307
- :deep(.v-input__control),
308
- :deep(.v-field) {
309
- width: 240px !important;
310
- grid-area: inherit !important;
311
-
312
- .v-field__field {
313
- height: 25px !important;
314
- padding: 0 !important;
315
- min-height: inherit !important;
316
-
317
- div.v-field__input {
318
- padding-top: 0 !important;
319
- padding-bottom: 0 !important;
320
- min-height: inherit !important;
321
-
322
- .v-select__selection {
323
- min-height: inherit !important;
324
- }
325
- }
326
- }
327
-
328
- .v-field__append-inner {
329
- margin-right: 6px;
330
- }
331
- }
332
-
333
- .v-input__details {
334
- display: none;
335
- }
336
- }
337
- }
338
- }
339
-
340
- </style>
1
+ <!-- About this Component--
2
+ * Renders a Series of inputs to collect input for the Quiz component.
3
+ * Related Quiz to question: TEXTE_TROUE_SELECT
4
+ * Receives the a data object defined by user
5
+ * Used by AppCompQuiz
6
+ * Uses useQuiz composable
7
+ -->
8
+ <template>
9
+ <div v-if="inputData.length > 0" :id="id" class="input-box">
10
+ <fieldset :aria-label="fieldsetLabel">
11
+ <div
12
+ v-for="textInput in inputElements"
13
+ :key="textInput.id"
14
+ class="texteatrou"
15
+ >
16
+ <span
17
+ v-if="textInput.type == 'text' && textInput.content.trim() !== ''"
18
+ v-html="textInput.content"
19
+ ></span>
20
+ <label :for="`${id}_${textInput.id}-champ`" style="display: none">
21
+ {{ $t('text.quiz') }}
22
+ </label>
23
+ <div
24
+ v-if="textInput.type == 'select'"
25
+ class="cnt-input"
26
+ :class="`${retro[textInput.id.substr(textInput.id.length - 1)] ? retro[textInput.id.substr(textInput.id.length - 1)] : ''}`"
27
+ >
28
+ <v-select
29
+ :id="`${id}_${textInput.id}-champ`"
30
+ :model-value="inputsValue[textInput.index]"
31
+ item-title="text"
32
+ :open-text="`${$t('message.dropdown_list')}`"
33
+ :item-props="true"
34
+ :items="getItemOptions(textInput.index)"
35
+ :aria-describedby="`${id}_${textInput.id}-msg-erreur`"
36
+ :aria-labelledby="`${id}_${textInput.id}-label`"
37
+ @update:model-value="onSelectUpdate($event, textInput.index)"
38
+ />
39
+ <span
40
+ :id="`${id}_${textInput.id}-msg-erreur`"
41
+ :key="`msg_chx_${id}_${textInput.id}`"
42
+ class="sr-only"
43
+ >
44
+ {{ messageAccessibility[textInput.index] }}
45
+ </span>
46
+ </div>
47
+ </div>
48
+ </fieldset>
49
+ </div>
50
+ </template>
51
+ <script>
52
+ import { useQuiz } from '../composables/useQuiz'
53
+ import { useI18n } from 'vue-i18n'
54
+
55
+ export default {
56
+ name: 'AppCompInputTextToFillDropdownNext',
57
+ /* PROPS USED FOR PARENT TO COMMUNICATE DATA TO CHILD */
58
+ props: {
59
+ modelValue: {
60
+ type: Array,
61
+ default: () => []
62
+ },
63
+ inputData: {
64
+ type: Array,
65
+ default: () => []
66
+ },
67
+ textBase: {
68
+ type: String,
69
+ default: ''
70
+ },
71
+ solution: {
72
+ type: Array,
73
+ default: () => []
74
+ },
75
+
76
+ id: {
77
+ type: String,
78
+ default: ''
79
+ }
80
+ },
81
+ emits: ['update:modelValue', 'enable-submit'],
82
+ setup(props) {
83
+ const { t } = useI18n()
84
+ const { retroType, addRetroStyle, resetRetroStyle } = useQuiz()
85
+
86
+ return { retroType, addRetroStyle, resetRetroStyle, t }
87
+ },
88
+ data() {
89
+ return {
90
+ inputElements: [],
91
+ result: [],
92
+ retro: [],
93
+ messageAccessibility: [],
94
+ inputCount: null
95
+ }
96
+ },
97
+ computed: {
98
+ fieldsetLabel() {
99
+ return `${this.$t('quizState.textToFillDropdownFieldset1')} ${this.inputCount} ${this.$t('quizState.textToFillDropdownFieldset2')}`
100
+ },
101
+ /* INTERNAL REACTIVE VALUE THAT BIND WITH MODELVALUE PROPS */
102
+ inputsValue: {
103
+ get() {
104
+ return this.modelValue
105
+ },
106
+ set(newValue) {
107
+ this.resetRetroStyle([this.retro, this.messageAccessibility])
108
+ this.$emit('update:modelValue', [...newValue])
109
+ }
110
+ },
111
+ /**
112
+ * internal reactive value that map the options to add SELECTED attribute on each option
113
+ * and Add entrie for the default selected Elements
114
+ * this attribute value can be changed when user select the option
115
+ */
116
+ mappedOptions() {
117
+ return this.inputData.map((op) => {
118
+ const key = Object.keys(op)[0]
119
+ const values = Object.values(op)[0]
120
+
121
+ const newValues = [
122
+ {
123
+ value: null,
124
+ disabled: true,
125
+ selected: true,
126
+ text: this.$t('message.first_option_dropdown')
127
+ },
128
+ ...values.map((el) => ({
129
+ value: el,
130
+ text: el,
131
+ selected: false
132
+ }))
133
+ ]
134
+
135
+ return {
136
+ [key]: newValues
137
+ }
138
+ })
139
+ }
140
+ },
141
+ watch: {
142
+ /**
143
+ * @description watcher to track the inputs content
144
+ * remove an element from the inputs array (modelValue) when it is empty
145
+ */
146
+ inputsValue: {
147
+ handler() {
148
+ const filledCount = this.modelValue.filter(
149
+ (val) => val !== null && val !== undefined
150
+ )
151
+
152
+ this.$emit('enable-submit', filledCount.length == this.inputData.length)
153
+ },
154
+ deep: true,
155
+ immediate: true
156
+ }
157
+ },
158
+
159
+ mounted() {},
160
+ created() {
161
+ this.createTextWithInput(this.textBase)
162
+ },
163
+
164
+ methods: {
165
+ /**
166
+ * @description create the object to generate the text and selects options
167
+ * for each fillable input. The created object is assigned to inputElements
168
+ * @param {String} str the text with holes to fill
169
+ */
170
+ createTextWithInput(str) {
171
+ const regex = /\$%\S*%\$/g // regex pattern to match exp: $%number%$
172
+
173
+ let splittedText = str.split(regex)
174
+ let listInput = []
175
+
176
+ for (let i = 0; i < splittedText.length - 1; i++) {
177
+ listInput.push({
178
+ id: `text-${i}`,
179
+ type: 'text',
180
+ content: splittedText[i]
181
+ })
182
+
183
+ if (i < splittedText.length - 1)
184
+ listInput.push({ id: `select-${i}`, type: 'select', index: i })
185
+ }
186
+ let lI = listInput.findLast((element) => element.type == 'select')
187
+ this.inputCount = lI.index + 1
188
+
189
+ this.inputElements = listInput
190
+ },
191
+ /**
192
+ * @description Method to force update of this.inputsValue.
193
+ * this force the old value and new value of the input to be tracked by VUE.
194
+ * @param newValue, the new value of the input
195
+ * @param index index of the selected element
196
+ */
197
+
198
+ onSelectUpdate(newValue, index) {
199
+ if (this.$el && this.$el.id !== this.id) return //prevent event from firing on other input
200
+ this.resetRetroStyle([this.retro, this.messageAccessibility])
201
+ const updated = [...this.inputsValue] //create new array from inputsValue
202
+ updated[index] = newValue
203
+ this.inputsValue = updated //trigger vue reactivity for inputsValue
204
+ },
205
+
206
+ /**
207
+ * @description Method to retrive the options of each selectable input in the mappedOption Object
208
+ * @param {Number } index - number representing the index of the item in the array of inputElements
209
+ * @Return a collection of item representing the options for a the selectables
210
+ */
211
+ getItemOptions(index) {
212
+ return Object.values(this.mappedOptions[index])[0]
213
+ },
214
+ /**
215
+ * @description Component validation method to validate the user input against the solution
216
+ * can handle logic of Css style to apply (correct/wrong Answer) and return the result
217
+ * @return {Object} result
218
+ */
219
+ validateAnswer() {
220
+ let mappedResults
221
+
222
+ if (this.solution != null) {
223
+ mappedResults = this.inputsValue.map((a, i) => {
224
+ const s = Object.values(this.solution[i])[0]
225
+ return { selected: a, correct: s == a }
226
+ })
227
+ } else {
228
+ mappedResults = this.inputsValue.map((a) => {
229
+ return { selected: a, correct: false }
230
+ })
231
+ }
232
+
233
+ //Set the valition style
234
+ let { classRetro, mesA11y } = this.addRetroStyle(
235
+ this.solution,
236
+ mappedResults,
237
+ this.inputsValue.length
238
+ )
239
+ this.retro = classRetro
240
+ this.messageAccessibility = mesA11y
241
+ //Retrive the user response from validation
242
+ this.result = mappedResults.filter((a) => a.selected)
243
+
244
+ let retro = this.retroType(this.solution, this.result)
245
+
246
+ let cAns
247
+ if (this.solution != null) cAns = this.computeResult(this.result)
248
+ else cAns = false
249
+ //Return this to parent
250
+ return {
251
+ userAnswer: this.result,
252
+ correctAnswer: cAns,
253
+ retroType: retro
254
+ }
255
+ },
256
+
257
+ /**
258
+ * @description - compute the correct elements in user response
259
+ * compare with the solution and return wether user succeeded or failed
260
+ * @param {Object} result - user responses
261
+ * @retuns boolean expressing the fail/success result
262
+ */
263
+ computeResult(result) {
264
+ const res = result.filter((el) => el.correct)
265
+ return res.length == this.solution.length
266
+ }
267
+ }
268
+ }
269
+ </script>
270
+ <style lang="scss" scoped>
271
+ select {
272
+ &.custom-select {
273
+ background-image: inherit;
274
+
275
+ &:focus {
276
+ border-color: inherit;
277
+ box-shadow: inherit;
278
+ }
279
+ }
280
+ }
281
+
282
+ /***** style dev *****/
283
+ .texteatrou {
284
+ display: inline;
285
+ }
286
+ .texteatrou > select {
287
+ display: inline;
288
+ width: auto;
289
+ }
290
+
291
+ .cnt-input {
292
+ position: relative;
293
+ }
294
+
295
+ fieldset {
296
+ border: inherit;
297
+ }
298
+
299
+ div.texteatrou {
300
+ display: inline !important;
301
+
302
+
303
+ .cnt-input {
304
+ display: inline;
305
+ .v-input {
306
+ display: inline-block !important;
307
+
308
+
309
+
310
+ :deep(.v-input__control),
311
+ :deep(.v-field) {
312
+ width: 240px !important;
313
+ grid-area: inherit !important;
314
+
315
+ .v-field__field {
316
+ height: 25px !important;
317
+ padding: 0 !important;
318
+ min-height: inherit !important;
319
+
320
+ div.v-field__input {
321
+ padding-top: 0 !important;
322
+ padding-bottom: 0 !important;
323
+ min-height: inherit !important;
324
+
325
+ .v-select__selection {
326
+ min-height: inherit !important;
327
+ }
328
+ }
329
+ }
330
+
331
+ .v-field__append-inner {
332
+ margin-right: 6px;
333
+ }
334
+ }
335
+
336
+ .v-input__details {
337
+ display: none;
338
+ }
339
+ }
340
+ }
341
+ }
342
+
343
+ </style>