fcad-core-dragon 2.1.1 → 2.2.0-beta.1

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