fcad-core-dragon 2.0.0-beta.0 → 2.0.0-beta.10

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 (161) hide show
  1. package/.editorconfig +6 -31
  2. package/.prettierrc +11 -0
  3. package/.vscode/extensions.json +8 -0
  4. package/.vscode/settings.json +16 -0
  5. package/CHANGELOG +153 -0
  6. package/README.md +28 -43
  7. package/documentation/.vitepress/config.js +114 -0
  8. package/documentation/api-examples.md +49 -0
  9. package/documentation/composants/app-base-button.md +58 -0
  10. package/documentation/composants/app-base-error-display.md +59 -0
  11. package/documentation/composants/app-base-popover.md +68 -0
  12. package/documentation/composants/app-comp-audio.md +75 -0
  13. package/documentation/composants/app-comp-branch-buttons.md +111 -0
  14. package/documentation/composants/app-comp-button-progress.md +53 -0
  15. package/documentation/composants/app-comp-carousel.md +53 -0
  16. package/documentation/composants/app-comp-container.md +53 -0
  17. package/documentation/composants/app-comp-input-checkbox-next.md +42 -0
  18. package/documentation/composants/app-comp-input-dropdown-next.md +34 -0
  19. package/documentation/composants/app-comp-input-radio-next.md +39 -0
  20. package/documentation/composants/app-comp-input-text-next.md +35 -0
  21. package/documentation/composants/app-comp-input-text-table-next.md +34 -0
  22. package/documentation/composants/app-comp-input-text-to-fill-dropdown-next.md +53 -0
  23. package/documentation/composants/app-comp-input-text-to-fill-next.md +31 -0
  24. package/documentation/composants/app-comp-jauge.md +31 -0
  25. package/documentation/composants/app-comp-menu-item.md +55 -0
  26. package/documentation/composants/app-comp-menu.md +29 -0
  27. package/documentation/composants/app-comp-navigation.md +41 -0
  28. package/documentation/composants/app-comp-note-call.md +53 -0
  29. package/documentation/composants/app-comp-note-credit.md +53 -0
  30. package/documentation/composants/app-comp-play-bar-next.md +53 -0
  31. package/documentation/composants/app-comp-pop-up-next.md +93 -0
  32. package/documentation/composants/app-comp-quiz-next.md +235 -0
  33. package/documentation/composants/app-comp-quiz-recall.md +53 -0
  34. package/documentation/composants/app-comp-svg-next.md +53 -0
  35. package/documentation/composants/app-comp-table-of-content.md +50 -0
  36. package/documentation/composants/app-comp-video-player.md +82 -0
  37. package/documentation/composants.md +46 -0
  38. package/documentation/composants_critiques/ModelPageComposant.md +53 -0
  39. package/documentation/composants_critiques/app-base-module.md +43 -0
  40. package/documentation/composants_critiques/app-base-page.md +48 -0
  41. package/documentation/composants_critiques/app-base.md +311 -0
  42. package/documentation/composants_critiques/main.md +15 -0
  43. package/documentation/demarrage.md +50 -0
  44. package/documentation/deploiement.md +58 -0
  45. package/documentation/index.md +33 -0
  46. package/documentation/markdown-examples.md +85 -0
  47. package/documentation/public/npm_version.png +0 -0
  48. package/documentation/public/vite.svg +15 -0
  49. package/documentation/public/vuejs.svg +2 -0
  50. package/documentation/public/vuetify.svg +6 -0
  51. package/eslint.config.js +60 -0
  52. package/package.json +43 -49
  53. package/src/$locales/en.json +94 -92
  54. package/src/$locales/fr.json +77 -113
  55. package/src/assets/data/onboardingMessages.json +1 -1
  56. package/src/components/AppBase.vue +971 -437
  57. package/src/components/AppBaseButton.test.js +21 -0
  58. package/src/components/AppBaseButton.vue +42 -10
  59. package/src/components/AppBaseErrorDisplay.vue +209 -162
  60. package/src/components/AppBaseFlipCard.vue +1 -0
  61. package/src/components/AppBaseModule.vue +1021 -976
  62. package/src/components/AppBasePage.vue +656 -128
  63. package/src/components/AppBasePopover.vue +41 -0
  64. package/src/components/AppBaseSkeleton.vue +66 -0
  65. package/src/components/AppCompAudio.vue +256 -0
  66. package/src/components/AppCompBranchButtons.vue +108 -181
  67. package/src/components/AppCompButtonProgress.vue +44 -70
  68. package/src/components/AppCompCarousel.vue +284 -233
  69. package/src/components/AppCompContainer.vue +29 -0
  70. package/src/components/AppCompInputCheckBoxNx.vue +323 -0
  71. package/src/components/AppCompInputDropdownNx.vue +299 -0
  72. package/src/components/AppCompInputRadioNx.vue +284 -0
  73. package/src/components/AppCompInputTextNx.vue +153 -0
  74. package/src/components/AppCompInputTextTableNx.vue +202 -0
  75. package/src/components/AppCompInputTextToFillDropdownNx.vue +340 -0
  76. package/src/components/AppCompInputTextToFillNx.vue +313 -0
  77. package/src/components/AppCompJauge.vue +36 -11
  78. package/src/components/AppCompMenu.vue +312 -92
  79. package/src/components/AppCompMenuItem.vue +129 -105
  80. package/src/components/AppCompNavigation.vue +972 -0
  81. package/src/components/AppCompNoteCall.vue +161 -0
  82. package/src/components/AppCompNoteCredit.vue +491 -0
  83. package/src/components/AppCompPlayBarNext.vue +2288 -0
  84. package/src/components/AppCompPopUpNext.vue +504 -0
  85. package/src/components/AppCompQuizNext.vue +510 -0
  86. package/src/components/AppCompQuizRecall.vue +350 -0
  87. package/src/components/AppCompSVGNext.vue +346 -0
  88. package/src/components/AppCompSettingsMenu.vue +18 -16
  89. package/src/components/AppCompTableOfContent.vue +340 -122
  90. package/src/components/AppCompVideoPlayer.vue +377 -0
  91. package/src/components/BaseModule.vue +24 -117
  92. package/src/components/tests__/AppBaseButton.spec.js +53 -0
  93. package/src/components/tests__/useTimer.spec.js +91 -0
  94. package/src/composables/useIdleDetector.js +56 -0
  95. package/src/composables/useQuiz.js +89 -0
  96. package/src/composables/useTimer.js +172 -0
  97. package/src/directives/nvdaFix.js +53 -0
  98. package/src/externalComps/ModuleView.vue +22 -0
  99. package/src/externalComps/SummaryView.vue +91 -0
  100. package/src/main.js +400 -142
  101. package/src/module/stores/appStore.js +947 -0
  102. package/src/module/xapi/ADL.js +241 -60
  103. package/src/module/xapi/Crypto/Hasher.js +8 -8
  104. package/src/module/xapi/Crypto/WordArray.js +6 -6
  105. package/src/module/xapi/Crypto/algorithms/BufferedBlockAlgorithm.js +4 -4
  106. package/src/module/xapi/Crypto/algorithms/C_algo.js +14 -18
  107. package/src/module/xapi/Crypto/algorithms/HMAC.js +1 -1
  108. package/src/module/xapi/Crypto/algorithms/SHA1.js +1 -1
  109. package/src/module/xapi/Crypto/encoders/Base.js +7 -7
  110. package/src/module/xapi/Crypto/encoders/Base64.js +3 -3
  111. package/src/module/xapi/Crypto/encoders/Hex.js +4 -3
  112. package/src/module/xapi/Crypto/encoders/Latin1.js +3 -3
  113. package/src/module/xapi/Crypto/encoders/Utf8.js +3 -3
  114. package/src/module/xapi/Statement/index.js +3 -3
  115. package/src/module/xapi/launch.js +10 -10
  116. package/src/module/xapi/utils.js +17 -17
  117. package/src/module/xapi/wrapper.js +223 -218
  118. package/src/module/xapi/xapiStatement.js +29 -29
  119. package/src/plugins/analytics.js +34 -0
  120. package/src/plugins/bus.js +7 -2
  121. package/src/plugins/gsap.js +9 -8
  122. package/src/plugins/helper.js +141 -43
  123. package/src/plugins/i18n.js +13 -18
  124. package/src/plugins/idb.js +46 -30
  125. package/src/plugins/save.js +1 -1
  126. package/src/plugins/scorm.js +15 -15
  127. package/src/plugins/xapi.js +2 -2
  128. package/src/public/index.html +23 -11
  129. package/src/router/index.js +57 -0
  130. package/src/router/routes.js +312 -0
  131. package/src/shared/generalfuncs.js +241 -10
  132. package/src/shared/validators.js +1018 -0
  133. package/vitest.config.js +19 -0
  134. package/.eslintignore +0 -29
  135. package/.eslintrc.js +0 -86
  136. package/.prettierrc.js +0 -5
  137. package/babel.config.js +0 -3
  138. package/src/components/AppBaseDragChoice.vue +0 -91
  139. package/src/components/AppBaseDropZone.vue +0 -112
  140. package/src/components/AppCompDragAndDrop.vue +0 -339
  141. package/src/components/AppCompInputAssociation.vue +0 -332
  142. package/src/components/AppCompInputCheckBox.vue +0 -227
  143. package/src/components/AppCompInputDropdown.vue +0 -184
  144. package/src/components/AppCompInputRadio.vue +0 -169
  145. package/src/components/AppCompInputTextBox.vue +0 -91
  146. package/src/components/AppCompInputTextTable.vue +0 -155
  147. package/src/components/AppCompInputTextToFillDropdown.vue +0 -255
  148. package/src/components/AppCompInputTextToFillText.vue +0 -164
  149. package/src/components/AppCompMediaPlayer.vue +0 -365
  150. package/src/components/AppCompNavigationFull.vue +0 -1791
  151. package/src/components/AppCompPlayBar.vue +0 -1540
  152. package/src/components/AppCompPopUp.vue +0 -523
  153. package/src/components/AppCompQuiz.vue +0 -2998
  154. package/src/components/AppCompToolTip.vue +0 -94
  155. package/src/mixins/$pageMixins.js +0 -381
  156. package/src/mixins/$quizMixins.js +0 -456
  157. package/src/mixins/timerMixin.js +0 -132
  158. package/src/module/store.js +0 -874
  159. package/src/plugins/timeManager.js +0 -77
  160. package/src/routes.js +0 -734
  161. package/vue.config.js +0 -83
@@ -0,0 +1,284 @@
1
+ <!-- About this Component--
2
+ * Renders a RADIO BUTTON input to display choices of response for the Quiz component
3
+ * Related Quiz to question: REPONSE_UNIQUE
4
+ * Receives the a data object defined by user
5
+ * Used by AppCompQuiz
6
+ * Uses useQuiz composable
7
+ -->
8
+
9
+ <template>
10
+ <div v-if="inputData.length > 0" :id="id" class="input-box">
11
+ <fieldset :aria-label="fieldsetLabel">
12
+ <template
13
+ v-for="(choixReponse, index) in quizInputDataValue"
14
+ :Key="`div_chx_${id}-${choixReponse.id}`"
15
+ >
16
+ <div class="box-radio">
17
+ <label
18
+ :key="`lbl_chx_${id}_${choixReponse.id}`"
19
+ :for="`chx_${id}_${choixReponse.id}`"
20
+ class="radio-label"
21
+ :class="[
22
+ { answerSlct: selected == choixReponse.value },
23
+ `${retro[index] || ''}`
24
+ ]"
25
+ >
26
+ <input
27
+ :id="`chx_${id}_${choixReponse.id}`"
28
+ :key="`chx_${id}_${choixReponse.id}`"
29
+ v-model="selected"
30
+ type="radio"
31
+ :name="`btn-radios-${id}`"
32
+ class="radio-input"
33
+ :aria-labelledby="`span_${id}_${choixReponse.id}`"
34
+ :aria-describedby="`${id}_${choixReponse.id}-msg-erreur`"
35
+ :value="choixReponse.value"
36
+ />
37
+
38
+ <span
39
+ :id="`span_${id}_${choixReponse.id}`"
40
+ class="radio-contenu"
41
+ v-html="choixReponse.value"
42
+ />
43
+ <span
44
+ :id="`${id}_${choixReponse.id}-msg-erreur`"
45
+ :key="`msg_chx_${id}_${choixReponse.id}`"
46
+ class="sr-only"
47
+ >
48
+ {{ messageAccessibility[index] }}
49
+ </span>
50
+ </label>
51
+ </div>
52
+ </template>
53
+ </fieldset>
54
+ </div>
55
+ </template>
56
+ <script>
57
+ import { useQuiz } from '../composables/useQuiz'
58
+ import { validateObjType } from '../shared/validators'
59
+ export default {
60
+ name: 'AppCompInputRadioNx',
61
+ /* PROPS USED FOR PARENT TO COMMUNICATE DATA TO CHILD */
62
+ props: {
63
+ modelValue: {
64
+ type: Array,
65
+ default: () => []
66
+ },
67
+ inputData: {
68
+ type: Array,
69
+ default: () => []
70
+ },
71
+
72
+ solution: {
73
+ type: Array,
74
+ default: () => []
75
+ },
76
+
77
+ id: {
78
+ type: String,
79
+ default: ''
80
+ }
81
+ },
82
+ /* EVENT EMITTED WHEN MODELVALUE CHANGES|UPDATE */
83
+ emits: ['update:modelValue', 'enable-submit'],
84
+
85
+ setup(props) {
86
+ const { retroType, addRetroStyle, resetRetroStyle } = useQuiz()
87
+
88
+ return { retroType, addRetroStyle, resetRetroStyle }
89
+ },
90
+ data() {
91
+ return {
92
+ quizInputDataValue: [],
93
+ result: null,
94
+ retro: [],
95
+ messageAccessibility: []
96
+ }
97
+ },
98
+ computed: {
99
+ fieldsetLabel() {
100
+ return `${this.$t('quizState.radioFieldset')} ${this.inputData.length} ${this.$t('quizState.options')}`
101
+ },
102
+ /* INTERNAL REACTIVE VALUE THAT BIND WITH MODELVALUE PROPS */
103
+ inputValue: {
104
+ get() {
105
+ return this.modelValue
106
+ },
107
+
108
+ set(newValue) {
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
+ * this attribute value can be changed when user select the option
115
+ */
116
+ mappedOptions() {
117
+ const options = this.inputData.map(
118
+ (op) => (op = { ...op, selected: false })
119
+ )
120
+
121
+ return options
122
+ },
123
+ selected: {
124
+ /**
125
+ * Return the value of the selected element in the modelValue
126
+ */
127
+ get() {
128
+ // return this.modelValue
129
+ const selectedItem = this.modelValue.find((item) => {
130
+ return item.selected
131
+ })
132
+
133
+ return selectedItem ? selectedItem.value : ''
134
+ },
135
+ /**
136
+ * Modify the value of a item in the array model and return the array
137
+ */
138
+ set(newValue) {
139
+ this.resetRetroStyle([this.retro, this.messageAccessibility])
140
+
141
+ // create change THE SELECTED attribute OF THE selected option
142
+ const updatedModelValue = this.mappedOptions
143
+ .map((option) => ({
144
+ ...option,
145
+ selected: option.value === newValue
146
+ }))
147
+ .filter((e) => e.selected)
148
+
149
+ this.inputValue = updatedModelValue // trigger the reactivity of the inputVAlue
150
+ }
151
+ }
152
+ },
153
+ watch: {
154
+ inputValue: {
155
+ handler(newValue, oldValue) {
156
+ if (this.$el && this.$el.id !== this.id) return
157
+
158
+ const selectedItem = this.modelValue.some((e) => e.selected)
159
+ this.$emit('enable-submit', selectedItem)
160
+ },
161
+ immediate: true,
162
+ deep: true
163
+ }
164
+ },
165
+
166
+ created() {
167
+ if (import.meta.env.DEV) {
168
+ let errors = this.validateInputData()
169
+
170
+ if (errors && errors.errorList.length)
171
+ return this.$bus.$emit('input-error', { e: this.id, errors })
172
+ }
173
+
174
+ this.quizInputDataValue = this.inputData
175
+ },
176
+ mounted() {},
177
+
178
+ methods: {
179
+ /**
180
+ * @description validate the raw data received by the component to render is view
181
+ * @returns {Object} errors - errorList: to display in view and errorConsole, to be displayed in console
182
+ */
183
+ validateInputData() {
184
+ let errors = null //array for errors dectected
185
+ let stringType = ['id', 'value']
186
+
187
+ if (!this.inputData.length) return errors
188
+ for (let i = 0; i < this.inputData.length; i++) {
189
+ errors = validateObjType(
190
+ this.inputData[i],
191
+ { stringType },
192
+ null,
193
+ `choix_reponse #${i + 1}`
194
+ )
195
+ const { errorList, errorInConsole } = errors
196
+
197
+ if (errorList.length || errorInConsole.length) return errors
198
+ }
199
+
200
+ return errors
201
+ },
202
+ /**
203
+ * @description Component validation method to validate the user input against the solution
204
+ * can handle logic of Css style to apply (correct/wrong Answer)
205
+ */
206
+ validateAnswer() {
207
+ //Validate the user answer
208
+ let validatedResults
209
+ if (this.solution != null) {
210
+ validatedResults = this.inputValue.map(
211
+ (a) =>
212
+ (a = {
213
+ ...a,
214
+ correct: this.solution.includes(a.id)
215
+ })
216
+ )
217
+ } else {
218
+ validatedResults = this.inputValue.map(
219
+ (a) =>
220
+ (a = {
221
+ ...a,
222
+ correct: null
223
+ })
224
+ )
225
+ }
226
+
227
+ const mappedResults = this.mappedOptions.map((a) => {
228
+ if (this.solution != null) {
229
+ if (a.id == validatedResults[0].id) a = validatedResults[0]
230
+ a.correct = this.solution.includes(a.id)
231
+ return a
232
+ } else {
233
+ if (a.id == validatedResults[0].id) a = validatedResults[0]
234
+ a.correct = null
235
+ return a
236
+ }
237
+ })
238
+
239
+ //Set the valition style
240
+ let { classRetro, mesA11y } = this.addRetroStyle(
241
+ this.solution,
242
+ mappedResults,
243
+ this.inputData.length
244
+ )
245
+ this.retro = classRetro
246
+ this.messageAccessibility = mesA11y
247
+
248
+ //Retrive the user response from validation
249
+ this.result = mappedResults.filter((a) => a.selected)
250
+
251
+ let retro = this.retroType(this.solution, this.result)
252
+
253
+ const { correct, ...answer } = this.result[0]
254
+
255
+ return {
256
+ userAnswer: answer,
257
+ correctAnswer: correct,
258
+ retroType: retro
259
+ }
260
+ }
261
+ }
262
+ }
263
+ </script>
264
+ <style lang="scss">
265
+ .custom-control-input:focus ~ .custom-control-label::before {
266
+ border: inherit;
267
+ }
268
+
269
+ .custom-control-input:focus ~ .custom-control-label::before {
270
+ box-shadow: inherit;
271
+ -webkit-box-shadow: inherit;
272
+ }
273
+
274
+ fieldset {
275
+ border: inherit;
276
+ }
277
+
278
+ .radio-label {
279
+ //position: relative;
280
+ span {
281
+ width: 90%;
282
+ }
283
+ }
284
+ </style>
@@ -0,0 +1,153 @@
1
+ <!-- About this Component--
2
+ * Renders a TEXTAREA input to collect input for the Quiz component
3
+ * Related Quiz to question: REPONSE_OUVERTE
4
+ Receives the a data object defined by user
5
+ * Used by AppCompQuiz
6
+ * Uses useQuiz composable
7
+ -->
8
+ <template>
9
+ <div :id="id" class="input-box box-reponse-ouverte">
10
+ <v-textarea
11
+ :id="`textArea_${id}`"
12
+ :value="modelValue"
13
+ :aria-labelledby="`textArea_${id}`"
14
+ :placeholder="$t('text.place_holder.for_textarea')"
15
+ rows="3"
16
+ no-resize
17
+ :aria-describedby="`${id}-msg-erreur`"
18
+ @update:model-value="onInputUpdate($event)"
19
+ />
20
+ </div>
21
+ </template>
22
+ <script>
23
+ import { useQuiz } from '../composables/useQuiz'
24
+ export default {
25
+ name: 'AppCompInputTextNx',
26
+ /* PROPS USED FOR PARENT TO COMMUNICATE DATA TO CHILD */
27
+ props: {
28
+ modelValue: {
29
+ type: Array,
30
+ default: () => []
31
+ },
32
+ inputData: {
33
+ type: Array,
34
+ default: () => []
35
+ },
36
+ solution: {
37
+ type: Array,
38
+ default: () => []
39
+ },
40
+ userAnswer: {
41
+ type: Array,
42
+ default: () => []
43
+ },
44
+ id: {
45
+ type: String,
46
+ default: ''
47
+ }
48
+ },
49
+ emits: ['update:modelValue', 'enable-submit'],
50
+ setup(props) {
51
+ const { retroType, addRetroStyle, resetRetroStyle } = useQuiz()
52
+ return { retroType, addRetroStyle, resetRetroStyle }
53
+ },
54
+ data() {
55
+ return {
56
+ quizInputDataValue: [],
57
+ result: null,
58
+ retro: [],
59
+ messageAccessibility: [],
60
+ textBox: null
61
+ }
62
+ },
63
+ computed: {
64
+ /* INTERNAL REACTIVE VALUE THAT BIND WITH MODELVALUE PROPS */
65
+
66
+ inputValue: {
67
+ get() {
68
+ return this.modelValue
69
+ },
70
+
71
+ set(newValue) {
72
+ this.$emit('update:modelValue', [newValue])
73
+ }
74
+ }
75
+ },
76
+ watch: {
77
+ inputValue: {
78
+ handler(newValue) {
79
+ if (this.$el && this.$el.id !== this.id) return
80
+ this.$emit('enable-submit', newValue[0] && newValue[0].trim() !== '')
81
+ },
82
+ deep: true,
83
+ immediate: true
84
+ }
85
+ },
86
+ created() {
87
+ if (import.meta.env.DEV) {
88
+ let errors = this.validateInputData()
89
+ if (errors && errors.errorList.length) {
90
+ return this.$bus.$emit('input-error', { e: this.id, errors })
91
+ }
92
+ }
93
+
94
+ this.quizInputDataValue = this.inputData
95
+ },
96
+ methods: {
97
+ validateInputData() {
98
+ let errors = null //array for errors dectected
99
+
100
+ if (this.inputData != undefined) return errors
101
+
102
+ return errors
103
+ },
104
+ /**
105
+ * @description Method to force update of this.inputsValue.
106
+ * this force the old value and new value of the input to be tracked by VUE.
107
+ * @param newValue, the new value of the input
108
+ */
109
+
110
+ onInputUpdate(newValue) {
111
+ if (this.$el && this.$el.id !== this.id) return //prevent event from firing on other input
112
+ this.resetRetroStyle([this.retro, this.messageAccessibility])
113
+
114
+ this.inputValue = newValue //trigger vue reactivity for inputsValue
115
+ },
116
+ /**
117
+ * @description Component validation method to validate the user input against the solution
118
+ * can handle logic of Css style to apply (correct/wrong Answer)
119
+ */
120
+ validateAnswer() {
121
+ this.result = this.inputValue.map(
122
+ (a) =>
123
+ (a = {
124
+ filled: a,
125
+ correct: true
126
+ })
127
+ )
128
+
129
+ let { classRetro, mesA11y } = this.addRetroStyle(
130
+ this.solution,
131
+ this.result,
132
+ this.inputValue.length
133
+ )
134
+
135
+ this.retro = classRetro
136
+ this.messageAccessibility = mesA11y
137
+
138
+ let tr = this.retroType(this.solution, this.result)
139
+
140
+ return {
141
+ userAnswer: this.result,
142
+ correctAnswer: null,
143
+ retroType: tr
144
+ }
145
+ }
146
+ }
147
+ }
148
+ </script>
149
+ <style lang="scss">
150
+ fieldset {
151
+ border: inherit;
152
+ }
153
+ </style>
@@ -0,0 +1,202 @@
1
+ <!-- About this Component--
2
+ * Renders a Series of SELECTS inputs to collect input for the Quiz component.
3
+ * Related Quiz to question: TEXTE_TABLEAU
4
+ * Receives the a data object defined by user
5
+ * Used by AppCompQuiz
6
+ * Uses useQuiz composable
7
+ -->
8
+ <template>
9
+ <div :id="id" class="input-box">
10
+ <fieldset :aria-label="fieldsetLabel">
11
+ <div v-for="(textInput, index) in inputData" :key="textInput.id">
12
+ <label
13
+ :id="`${id}_${textInput.id}-label`"
14
+ aria-hidden="true"
15
+ v-html="textInput.ennonce"
16
+ ></label>
17
+
18
+ <div class="cnt-input-text" :class="`${retro[index]}`">
19
+ <v-text-field
20
+ :id="`${id}_${textInput.id}-champ`"
21
+ :model-value="inputsValue[textInput.id]"
22
+ :placeholder="$t('text.place_holder.for_textarea')"
23
+ :aria-describedby="`${id}_${textInput.id}-msg-erreur`"
24
+ :aria-labelledby="`${id}_${textInput.id}-label`"
25
+ no-resize
26
+ @update:model-value="onSelectUpdate($event, textInput.id)"
27
+ />
28
+ </div>
29
+ <span :id="`${id}_${textInput.id}-msg-erreur`" class="sr-only">
30
+ {{ messageAccessibility[index] }}
31
+ </span>
32
+ </div>
33
+ </fieldset>
34
+ </div>
35
+ </template>
36
+ <script>
37
+ import { useQuiz } from '../composables/useQuiz'
38
+ import { validateObjType } from '../shared/validators'
39
+ export default {
40
+ name: 'AppCompInputTextTableNx',
41
+ /* PROPS USED FOR PARENT TO COMMUNICATE DATA TO CHILD */
42
+ props: {
43
+ modelValue: {
44
+ type: Array,
45
+ default: () => []
46
+ },
47
+ inputData: {
48
+ type: Array,
49
+ default: () => []
50
+ },
51
+ solution: {
52
+ type: Array,
53
+ default: () => []
54
+ },
55
+ id: {
56
+ type: String,
57
+ default: ''
58
+ }
59
+ },
60
+ emits: ['update:modelValue', 'enable-submit'],
61
+ setup() {
62
+ const { retroType, addRetroStyle, resetRetroStyle } = useQuiz()
63
+
64
+ return { retroType, addRetroStyle, resetRetroStyle }
65
+ },
66
+ data() {
67
+ return {
68
+ quizInputDataValue: [],
69
+ textInputs: [],
70
+ quizSolution: null,
71
+ inputElements: [],
72
+ result: null,
73
+ retro: [],
74
+ messageAccessibility: []
75
+ }
76
+ },
77
+ computed: {
78
+ fieldsetLabel() {
79
+ return `${this.$t('quizState.textTableFieldset1')} ${this.inputData.length} ${this.$t('quizState.textTableFieldset2')}`
80
+ },
81
+ /* INTERNAL REACTIVE VALUE THAT BIND WITH MODELVALUE PROPS */
82
+ inputsValue: {
83
+ get() {
84
+ return this.modelValue
85
+ },
86
+ set(newValue) {
87
+ this.$emit('update:modelValue', [...newValue])
88
+ }
89
+ }
90
+ },
91
+ watch: {
92
+ inputsValue: {
93
+ handler() {
94
+ const filled = this.modelValue.filter((val) => val && val.trim() !== '')
95
+
96
+ this.$emit('enable-submit', filled.length == this.inputData.length)
97
+ },
98
+ deep: true,
99
+ immediate: true
100
+ }
101
+ },
102
+ created() {
103
+ if (import.meta.env.DEV) {
104
+ let errors = this.validateInputData()
105
+ if (errors && errors.errorList.length) {
106
+ return this.$bus.$emit('input-error', { e: this.id, errors })
107
+ }
108
+ }
109
+
110
+ this.quizInputDataValue = this.inputData
111
+ },
112
+ mounted() {},
113
+
114
+ methods: {
115
+ validateInputData() {
116
+ let errors = { errorInConsole: [], errorList: [] }
117
+ let stringType = ['id', 'ennonce']
118
+ let e = null
119
+
120
+ if (!this.inputData.length) return errors
121
+ for (let i = 0; i < this.inputData.length; i++) {
122
+ e = validateObjType(
123
+ this.inputData[i],
124
+ { stringType },
125
+ null,
126
+ `texte tableau #${i + 1}`
127
+ )
128
+
129
+ if (e.errorInConsole.length && e.errorList.length) {
130
+ errors.errorInConsole.push(e.errorInConsole[0])
131
+ errors.errorList.push(e.errorList[0])
132
+ }
133
+ }
134
+
135
+ return errors
136
+ },
137
+ /**
138
+ * @description Method to force update of this.inputsValue.
139
+ * this force the old value and new value of the input to be tracked by VUE.
140
+ * @param newValue, the new value of the input
141
+ * @param index index of the selected element
142
+ */
143
+
144
+ onSelectUpdate(newValue, index) {
145
+ if (this.$el && this.$el.id !== this.id) return
146
+ this.resetRetroStyle([this.retro, this.messageAccessibility])
147
+
148
+ const updated = [...this.inputsValue] //create new array from inputsValue
149
+ updated[index] = newValue
150
+ this.inputsValue = updated //trigger vue reactivity for inputsValue
151
+ },
152
+ validateAnswer() {
153
+ let mappedResults
154
+
155
+ if (this.solution != null) {
156
+ mappedResults = this.inputsValue.map(
157
+ (a, i) =>
158
+ (a = {
159
+ filled: a,
160
+ correct: this.solution[i].reponse_value.includes(a)
161
+ })
162
+ )
163
+ } else {
164
+ mappedResults = this.inputsValue.map(
165
+ (a) =>
166
+ (a = {
167
+ filled: a,
168
+ correct: false
169
+ })
170
+ )
171
+ }
172
+
173
+ let { classRetro, mesA11y } = this.addRetroStyle(
174
+ this.solution,
175
+ mappedResults,
176
+ this.inputData.length
177
+ )
178
+
179
+ this.retro = classRetro
180
+ this.messageAccessibility = mesA11y
181
+
182
+ this.result = mappedResults.filter((a) => a.filled)
183
+ let tr = this.retroType(this.solution, mappedResults)
184
+
185
+ return {
186
+ userAnswer: this.result,
187
+ correctAnswer: this.solution,
188
+ retroType: tr
189
+ }
190
+ }
191
+ }
192
+ }
193
+ </script>
194
+ <style lang="scss" scoped>
195
+ .cnt-input-text {
196
+ position: relative;
197
+ }
198
+
199
+ fieldset {
200
+ border: inherit;
201
+ }
202
+ </style>