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.
- package/.editorconfig +7 -7
- package/.gitlab-ci.yml +124 -0
- package/.prettierrc +11 -11
- package/.vscode/extensions.json +8 -8
- package/.vscode/settings.json +46 -16
- package/CHANGELOG +520 -520
- package/README.md +57 -57
- package/documentation/.vitepress/config.js +114 -114
- package/documentation/api-examples.md +49 -49
- package/documentation/composants/app-base-button.md +58 -58
- package/documentation/composants/app-base-error-display.md +59 -59
- package/documentation/composants/app-base-popover.md +68 -68
- package/documentation/composants/app-comp-audio.md +75 -75
- package/documentation/composants/app-comp-branch-buttons.md +111 -111
- package/documentation/composants/app-comp-button-progress.md +53 -53
- package/documentation/composants/app-comp-carousel.md +53 -53
- package/documentation/composants/app-comp-container.md +53 -53
- package/documentation/composants/app-comp-input-checkbox-next.md +42 -42
- package/documentation/composants/app-comp-input-dropdown-next.md +34 -34
- package/documentation/composants/app-comp-input-radio-next.md +39 -39
- package/documentation/composants/app-comp-input-text-next.md +35 -35
- package/documentation/composants/app-comp-input-text-table-next.md +34 -34
- package/documentation/composants/app-comp-input-text-to-fill-dropdown-next.md +53 -53
- package/documentation/composants/app-comp-input-text-to-fill-next.md +31 -31
- package/documentation/composants/app-comp-jauge.md +31 -31
- package/documentation/composants/app-comp-menu-item.md +55 -55
- package/documentation/composants/app-comp-menu.md +29 -29
- package/documentation/composants/app-comp-navigation.md +41 -41
- package/documentation/composants/app-comp-note-call.md +53 -53
- package/documentation/composants/app-comp-note-credit.md +53 -53
- package/documentation/composants/app-comp-play-bar-next.md +53 -53
- package/documentation/composants/app-comp-pop-up-next.md +93 -93
- package/documentation/composants/app-comp-quiz-next.md +235 -235
- package/documentation/composants/app-comp-quiz-recall.md +53 -53
- package/documentation/composants/app-comp-svg-next.md +53 -53
- package/documentation/composants/app-comp-table-of-content.md +50 -50
- package/documentation/composants/app-comp-video-player.md +82 -82
- package/documentation/composants.md +46 -46
- package/documentation/composants_critiques/ModelPageComposant.md +53 -53
- package/documentation/composants_critiques/app-base-module.md +43 -43
- package/documentation/composants_critiques/app-base-page.md +48 -48
- package/documentation/composants_critiques/app-base.md +311 -311
- package/documentation/composants_critiques/main.md +15 -15
- package/documentation/demarrage.md +50 -50
- package/documentation/deploiement.md +57 -57
- package/documentation/index.md +33 -33
- package/documentation/markdown-examples.md +85 -85
- package/documentation/public/vite.svg +14 -14
- package/documentation/public/vuejs.svg +1 -1
- package/documentation/public/vuetify.svg +5 -5
- package/eslint.config.js +60 -60
- package/junit-report.xml +182 -0
- package/package.json +66 -59
- package/playwright/index.html +12 -0
- package/playwright/index.js +21 -0
- package/playwright-ct.config.js +95 -0
- package/src/$locales/en.json +157 -157
- package/src/$locales/fr.json +120 -120
- package/src/assets/data/onboardingMessages.json +47 -47
- package/src/components/AppBase.vue +1171 -1169
- package/src/components/AppBaseButton.vue +90 -95
- package/src/components/AppBaseErrorDisplay.vue +438 -438
- package/src/components/AppBaseFlipCard.vue +84 -84
- package/src/components/AppBaseModule.vue +1639 -1634
- package/src/components/AppBasePage.vue +867 -866
- package/src/components/AppBasePopover.vue +41 -41
- package/src/components/AppBaseSkeleton.vue +66 -66
- package/src/components/AppCompAudio.vue +261 -256
- package/src/components/AppCompBranchButtons.vue +508 -508
- package/src/components/AppCompButtonProgress.vue +137 -132
- package/src/components/AppCompCarousel.vue +342 -336
- package/src/components/AppCompContainer.vue +29 -29
- package/src/components/AppCompInputCheckBoxNx.vue +325 -323
- package/src/components/AppCompInputDropdownNx.vue +302 -299
- package/src/components/AppCompInputRadioNx.vue +287 -284
- package/src/components/AppCompInputTextNx.vue +156 -153
- package/src/components/AppCompInputTextTableNx.vue +205 -202
- package/src/components/AppCompInputTextToFillDropdownNx.vue +343 -340
- package/src/components/AppCompInputTextToFillNx.vue +316 -313
- package/src/components/AppCompJauge.vue +81 -81
- package/src/components/AppCompMenu.vue +6 -1
- package/src/components/AppCompMenuItem.vue +246 -240
- package/src/components/AppCompNavigation.vue +977 -972
- package/src/components/AppCompNoteCall.vue +167 -161
- package/src/components/AppCompNoteCredit.vue +496 -491
- package/src/components/AppCompPlayBarNext.vue +2290 -2288
- package/src/components/AppCompPopUpNext.vue +508 -504
- package/src/components/AppCompQuizNext.vue +515 -510
- package/src/components/AppCompQuizRecall.vue +355 -350
- package/src/components/AppCompSVGNext.vue +346 -346
- package/src/components/AppCompSettingsMenu.vue +177 -172
- package/src/components/AppCompTableOfContent.vue +433 -427
- package/src/components/AppCompVideoPlayer.vue +377 -377
- package/src/components/AppCompViewDisplay.vue +6 -6
- package/src/components/BaseModule.vue +55 -55
- package/src/composables/useIdleDetector.js +56 -56
- package/src/composables/useQuiz.js +89 -89
- package/src/composables/useTimer.js +172 -172
- package/src/directives/nvdaFix.js +53 -53
- package/src/externalComps/ModuleView.vue +22 -22
- package/src/externalComps/SummaryView.vue +91 -91
- package/src/main.js +493 -476
- package/src/module/stores/appStore.js +960 -947
- package/src/module/xapi/ADL.js +520 -520
- package/src/module/xapi/Crypto/Hasher.js +241 -241
- package/src/module/xapi/Crypto/WordArray.js +278 -278
- package/src/module/xapi/Crypto/algorithms/BufferedBlockAlgorithm.js +103 -103
- package/src/module/xapi/Crypto/algorithms/C_algo.js +315 -315
- package/src/module/xapi/Crypto/algorithms/HMAC.js +9 -9
- package/src/module/xapi/Crypto/algorithms/SHA1.js +9 -9
- package/src/module/xapi/Crypto/encoders/Base.js +105 -105
- package/src/module/xapi/Crypto/encoders/Base64.js +99 -99
- package/src/module/xapi/Crypto/encoders/Hex.js +61 -61
- package/src/module/xapi/Crypto/encoders/Latin1.js +61 -61
- package/src/module/xapi/Crypto/encoders/Utf8.js +45 -45
- package/src/module/xapi/Crypto/index.js +53 -53
- package/src/module/xapi/Statement/activity.js +47 -47
- package/src/module/xapi/Statement/agent.js +55 -55
- package/src/module/xapi/Statement/group.js +26 -26
- package/src/module/xapi/Statement/index.js +259 -259
- package/src/module/xapi/Statement/statement.js +253 -253
- package/src/module/xapi/Statement/statementRef.js +23 -23
- package/src/module/xapi/Statement/substatement.js +22 -22
- package/src/module/xapi/Statement/verb.js +36 -36
- package/src/module/xapi/activitytypes.js +17 -17
- package/src/module/xapi/launch.js +157 -157
- package/src/module/xapi/utils.js +167 -167
- package/src/module/xapi/verbs.js +294 -294
- package/src/module/xapi/wrapper.js +1895 -1895
- package/src/module/xapi/xapiStatement.js +444 -444
- package/src/plugins/analytics.js +34 -34
- package/src/plugins/bus.js +12 -8
- package/src/plugins/gsap.js +17 -17
- package/src/plugins/helper.js +355 -358
- package/src/plugins/i18n.js +27 -26
- package/src/plugins/idb.js +227 -227
- package/src/plugins/save.js +37 -37
- package/src/plugins/scorm.js +287 -287
- package/src/plugins/xapi.js +11 -11
- package/src/public/index.html +33 -33
- package/src/router/index.js +57 -57
- package/src/router/routes.js +312 -312
- package/src/shared/generalfuncs.js +344 -344
- package/src/shared/validators.js +1018 -1018
- package/tests/component/AppBaseButton.spec.js +53 -0
- package/tests/component/pinia.spec.js +24 -0
- package/{src/components/tests__ → tests/unit}/AppBaseButton.spec.js +53 -53
- package/tests/unit/AppCompInputCheckBoxNx.spec.js +59 -0
- package/tests/unit/AppCompInputDropdownNx.spec.js +51 -0
- package/tests/unit/AppCompInputRadioNx.spec.js +59 -0
- package/tests/unit/AppCompInputTextNx.spec.js +44 -0
- package/tests/unit/AppCompInputTextTableNx.spec.js +77 -0
- package/tests/unit/AppCompInputTextToFillDropdownNx.spec.js +60 -0
- package/tests/unit/AppCompInputTextToFillNx.spec.js +45 -0
- package/tests/unit/AppCompQuizNext.spec.js +114 -0
- package/tests/unit/AppCompVideoPlayer.spec.js +177 -0
- package/{src/components/tests__ → tests/unit}/useTimer.spec.js +91 -91
- package/vitest.config.js +28 -19
- package/vitest.setup.js +28 -0
- package/src/components/AppBaseButton.test.js +0 -21
|
@@ -1,510 +1,515 @@
|
|
|
1
|
-
<!-- About this Component--
|
|
2
|
-
* Component to render a Quiz to user: question with serie of inputs
|
|
3
|
-
* Receives the a quiz data object defined by user
|
|
4
|
-
* Base on Quiz type will render a specific input type for the quiz
|
|
5
|
-
* Example of use: in an page you can add a quiz/exercice by calling:
|
|
6
|
-
** <app-comp-quiz-next :quiz-data="my_quiz_data_json"/>
|
|
7
|
-
** where "my_quiz_data_json" is a json object of the your quiz/exercices
|
|
8
|
-
|
|
9
|
-
-->
|
|
10
|
-
<template>
|
|
11
|
-
<app-base-error-display
|
|
12
|
-
v-if="errorList && errorList.length"
|
|
13
|
-
:error-group="'component'"
|
|
14
|
-
:error-title="'ERREUR: COMPOSANT DE QUIZ'"
|
|
15
|
-
:errors-list="errorList"
|
|
16
|
-
/>
|
|
17
|
-
<v-row v-else>
|
|
18
|
-
<v-col
|
|
19
|
-
v-if="quizData"
|
|
20
|
-
cols="12"
|
|
21
|
-
:class="`container_quiz_${quizData.type_question}`"
|
|
22
|
-
>
|
|
23
|
-
<!--Show skeleton while quiz data is not ready-->
|
|
24
|
-
<template v-if="!isReady">
|
|
25
|
-
<app-base-skeleton
|
|
26
|
-
:skeleton-text="''"
|
|
27
|
-
:skeleton-type="skeletonType(quizData.type_question)"
|
|
28
|
-
/>
|
|
29
|
-
{{ quizData.type_question }}
|
|
30
|
-
</template>
|
|
31
|
-
<!--Show when quiz data is ready-->
|
|
32
|
-
<template v-else>
|
|
33
|
-
<div :class="`quiz_${quizData.type_question}`">
|
|
34
|
-
<div
|
|
35
|
-
:id="`${quizData.type_question}_${quizData.id}`"
|
|
36
|
-
class="quiz-question"
|
|
37
|
-
>
|
|
38
|
-
<span class="sr-only">question :</span>
|
|
39
|
-
<div v-html="quizEnnonce" />
|
|
40
|
-
</div>
|
|
41
|
-
|
|
42
|
-
<component
|
|
43
|
-
:is="dynamicInputComponent"
|
|
44
|
-
v-bind="quizElement"
|
|
45
|
-
:ref="`Qz_${quizData.id}`"
|
|
46
|
-
v-model="response"
|
|
47
|
-
@enable-submit="enableValidateButton"
|
|
48
|
-
/>
|
|
49
|
-
|
|
50
|
-
<div class="btn-ctrl-quiz">
|
|
51
|
-
<app-base-button
|
|
52
|
-
:id="`btn_quiz_${quizData.id}`"
|
|
53
|
-
ref="quiz"
|
|
54
|
-
class="btn-quiz btn-main"
|
|
55
|
-
:is-disabled="!isEnabled"
|
|
56
|
-
:title="txtBtnSubmit"
|
|
57
|
-
@click="handleValidation"
|
|
58
|
-
>
|
|
59
|
-
{{ txtBtnSubmit }}
|
|
60
|
-
</app-base-button>
|
|
61
|
-
</div>
|
|
62
|
-
</div>
|
|
63
|
-
</template>
|
|
64
|
-
</v-col>
|
|
65
|
-
<v-col cols="12" class="ctn-retro" aria-live="polite">
|
|
66
|
-
<transition name="fade" mode="in-out">
|
|
67
|
-
<div
|
|
68
|
-
v-if="showRetro"
|
|
69
|
-
:class="`retro_inline_wrapper retro_inline_${retroType}`"
|
|
70
|
-
:aria-label="retroAriaLabel"
|
|
71
|
-
>
|
|
72
|
-
<div class="retro-title-container">
|
|
73
|
-
<span class="retro-title">{{ retroTitle }}</span>
|
|
74
|
-
</div>
|
|
75
|
-
<div class="retro-text-container" v-html="retroHtml"></div>
|
|
76
|
-
</div>
|
|
77
|
-
</transition>
|
|
78
|
-
</v-col>
|
|
79
|
-
</v-row>
|
|
80
|
-
</template>
|
|
81
|
-
<script>
|
|
82
|
-
import { mapState } from 'pinia'
|
|
83
|
-
import { useAppStore } from '../module/stores/appStore'
|
|
84
|
-
import { defineAsyncComponent } from 'vue'
|
|
85
|
-
import { computed } from 'vue'
|
|
86
|
-
import { validateQuizData } from '../shared/validators'
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
}
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
},
|
|
307
|
-
|
|
308
|
-
this.$bus.$
|
|
309
|
-
this.$bus.$
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
if (
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
}
|
|
372
|
-
}
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
case '
|
|
403
|
-
if (this.getModuleInfo.courseID)
|
|
404
|
-
text = `${aName}
|
|
405
|
-
else text = `
|
|
406
|
-
break
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
this.
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
this.
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
if (
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
}
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
}
|
|
510
|
-
</
|
|
1
|
+
<!-- About this Component--
|
|
2
|
+
* Component to render a Quiz to user: question with serie of inputs
|
|
3
|
+
* Receives the a quiz data object defined by user
|
|
4
|
+
* Base on Quiz type will render a specific input type for the quiz
|
|
5
|
+
* Example of use: in an page you can add a quiz/exercice by calling:
|
|
6
|
+
** <app-comp-quiz-next :quiz-data="my_quiz_data_json"/>
|
|
7
|
+
** where "my_quiz_data_json" is a json object of the your quiz/exercices
|
|
8
|
+
|
|
9
|
+
-->
|
|
10
|
+
<template>
|
|
11
|
+
<app-base-error-display
|
|
12
|
+
v-if="errorList && errorList.length"
|
|
13
|
+
:error-group="'component'"
|
|
14
|
+
:error-title="'ERREUR: COMPOSANT DE QUIZ'"
|
|
15
|
+
:errors-list="errorList"
|
|
16
|
+
/>
|
|
17
|
+
<v-row v-else>
|
|
18
|
+
<v-col
|
|
19
|
+
v-if="quizData"
|
|
20
|
+
cols="12"
|
|
21
|
+
:class="`container_quiz_${quizData.type_question}`"
|
|
22
|
+
>
|
|
23
|
+
<!--Show skeleton while quiz data is not ready-->
|
|
24
|
+
<template v-if="!isReady">
|
|
25
|
+
<app-base-skeleton
|
|
26
|
+
:skeleton-text="''"
|
|
27
|
+
:skeleton-type="skeletonType(quizData.type_question)"
|
|
28
|
+
/>
|
|
29
|
+
{{ quizData.type_question }}
|
|
30
|
+
</template>
|
|
31
|
+
<!--Show when quiz data is ready-->
|
|
32
|
+
<template v-else>
|
|
33
|
+
<div :class="`quiz_${quizData.type_question}`">
|
|
34
|
+
<div
|
|
35
|
+
:id="`${quizData.type_question}_${quizData.id}`"
|
|
36
|
+
class="quiz-question"
|
|
37
|
+
>
|
|
38
|
+
<span class="sr-only">question :</span>
|
|
39
|
+
<div v-html="quizEnnonce" />
|
|
40
|
+
</div>
|
|
41
|
+
|
|
42
|
+
<component
|
|
43
|
+
:is="dynamicInputComponent"
|
|
44
|
+
v-bind="quizElement"
|
|
45
|
+
:ref="`Qz_${quizData.id}`"
|
|
46
|
+
v-model="response"
|
|
47
|
+
@enable-submit="enableValidateButton"
|
|
48
|
+
/>
|
|
49
|
+
|
|
50
|
+
<div class="btn-ctrl-quiz">
|
|
51
|
+
<app-base-button
|
|
52
|
+
:id="`btn_quiz_${quizData.id}`"
|
|
53
|
+
ref="quiz"
|
|
54
|
+
class="btn-quiz btn-main"
|
|
55
|
+
:is-disabled="!isEnabled"
|
|
56
|
+
:title="txtBtnSubmit"
|
|
57
|
+
@click="handleValidation"
|
|
58
|
+
>
|
|
59
|
+
{{ txtBtnSubmit }}
|
|
60
|
+
</app-base-button>
|
|
61
|
+
</div>
|
|
62
|
+
</div>
|
|
63
|
+
</template>
|
|
64
|
+
</v-col>
|
|
65
|
+
<v-col cols="12" class="ctn-retro" aria-live="polite">
|
|
66
|
+
<transition name="fade" mode="in-out">
|
|
67
|
+
<div
|
|
68
|
+
v-if="showRetro"
|
|
69
|
+
:class="`retro_inline_wrapper retro_inline_${retroType}`"
|
|
70
|
+
:aria-label="retroAriaLabel"
|
|
71
|
+
>
|
|
72
|
+
<div class="retro-title-container">
|
|
73
|
+
<span class="retro-title">{{ retroTitle }}</span>
|
|
74
|
+
</div>
|
|
75
|
+
<div class="retro-text-container" v-html="retroHtml"></div>
|
|
76
|
+
</div>
|
|
77
|
+
</transition>
|
|
78
|
+
</v-col>
|
|
79
|
+
</v-row>
|
|
80
|
+
</template>
|
|
81
|
+
<script>
|
|
82
|
+
import { mapState } from 'pinia'
|
|
83
|
+
import { useAppStore } from '../module/stores/appStore'
|
|
84
|
+
import { defineAsyncComponent } from 'vue'
|
|
85
|
+
import { computed } from 'vue'
|
|
86
|
+
import { validateQuizData } from '../shared/validators'
|
|
87
|
+
import { useI18n } from 'vue-i18n'
|
|
88
|
+
|
|
89
|
+
export default {
|
|
90
|
+
name: 'AppCompQuiz',
|
|
91
|
+
components: {
|
|
92
|
+
AppCompInputTextBox: defineAsyncComponent(
|
|
93
|
+
() => import('./AppCompInputTextNx.vue')
|
|
94
|
+
),
|
|
95
|
+
AppCompInputCheckBox: defineAsyncComponent(
|
|
96
|
+
() => import('./AppCompInputCheckBoxNx.vue')
|
|
97
|
+
),
|
|
98
|
+
AppCompInputDropdown: defineAsyncComponent(
|
|
99
|
+
() => import('./AppCompInputDropdownNx.vue')
|
|
100
|
+
),
|
|
101
|
+
AppCompInputRadio: defineAsyncComponent(
|
|
102
|
+
() => import('./AppCompInputRadioNx.vue')
|
|
103
|
+
),
|
|
104
|
+
AppCompInputTextToFillDropdown: defineAsyncComponent(
|
|
105
|
+
() => import('./AppCompInputTextToFillDropdownNx.vue')
|
|
106
|
+
),
|
|
107
|
+
AppCompInputTextTable: defineAsyncComponent(
|
|
108
|
+
() => import('./AppCompInputTextTableNx.vue')
|
|
109
|
+
),
|
|
110
|
+
AppCompInputTextToFill: defineAsyncComponent(
|
|
111
|
+
() => import('./AppCompInputTextToFillNx.vue')
|
|
112
|
+
)
|
|
113
|
+
},
|
|
114
|
+
inject: ['userInteraction'],
|
|
115
|
+
provide() {
|
|
116
|
+
return {
|
|
117
|
+
quizAssociation: computed(() => this.quizAssociation),
|
|
118
|
+
quizDragDrop: computed(() => this.quizDragDrop),
|
|
119
|
+
quizSubmit: computed(() => this.quizSubmit), //added to explod comp^
|
|
120
|
+
initQuizSelected: computed(() => this.initQuizSelected), //value of the saved answers of the dropdown quiz
|
|
121
|
+
initQuizTable: computed(() => this.initQuizTable) //value of the saved answers of the table quiz
|
|
122
|
+
}
|
|
123
|
+
},
|
|
124
|
+
props: {
|
|
125
|
+
/* MAIN PROP USED FOR 2 WAY BINDING OF DATA PARENT&CHILD */
|
|
126
|
+
modelValue: [Array, null],
|
|
127
|
+
consigne: {
|
|
128
|
+
type: Boolean,
|
|
129
|
+
default: true
|
|
130
|
+
},
|
|
131
|
+
shuffleAnswers: {
|
|
132
|
+
type: Boolean,
|
|
133
|
+
default: false
|
|
134
|
+
},
|
|
135
|
+
quizData: {
|
|
136
|
+
type: Object,
|
|
137
|
+
validator: function (value) {
|
|
138
|
+
const { errorInConsole } = validateQuizData(value)
|
|
139
|
+
if (errorInConsole.length)
|
|
140
|
+
for (let err of errorInConsole)
|
|
141
|
+
console.warn(
|
|
142
|
+
` %cAppCompQuiz>>>${err}`,
|
|
143
|
+
'background: orange; color: white; display: block; margin:5px;'
|
|
144
|
+
)
|
|
145
|
+
|
|
146
|
+
return errorInConsole.length == 0
|
|
147
|
+
},
|
|
148
|
+
default: () => {}
|
|
149
|
+
}
|
|
150
|
+
},
|
|
151
|
+
setup() {
|
|
152
|
+
const { t } = useI18n()
|
|
153
|
+
return { t }
|
|
154
|
+
},
|
|
155
|
+
data() {
|
|
156
|
+
return {
|
|
157
|
+
totalAttempts: 0, //Total attempts to answer the quiz
|
|
158
|
+
quizCompleted: false,
|
|
159
|
+
quizSubmit: false,
|
|
160
|
+
retroType: '',
|
|
161
|
+
retroTitle: '',
|
|
162
|
+
retroHtml: '',
|
|
163
|
+
showRetro: false,
|
|
164
|
+
errorList: [],
|
|
165
|
+
response: [],
|
|
166
|
+
isEnabled: false,
|
|
167
|
+
justInitialized: null,
|
|
168
|
+
previousResponse: null,
|
|
169
|
+
skeletonCount: 5, //Number of skeleton lines to show
|
|
170
|
+
isReady: false
|
|
171
|
+
}
|
|
172
|
+
},
|
|
173
|
+
computed: {
|
|
174
|
+
...mapState(useAppStore, [
|
|
175
|
+
'getUserInteraction',
|
|
176
|
+
'getCurrentPage',
|
|
177
|
+
'getUserDataStatus',
|
|
178
|
+
'getModuleInfo'
|
|
179
|
+
]),
|
|
180
|
+
quizElement() {
|
|
181
|
+
const propMapping = {
|
|
182
|
+
choix_reponse: 'inputData',
|
|
183
|
+
texte_base: 'textBase',
|
|
184
|
+
max_essai: 'maxEssai'
|
|
185
|
+
}
|
|
186
|
+
this.quizData
|
|
187
|
+
|
|
188
|
+
const quiz = {}
|
|
189
|
+
|
|
190
|
+
for (const key in this.quizData) {
|
|
191
|
+
const mappedKey = propMapping[key] || key
|
|
192
|
+
quiz[mappedKey] = this.quizData[key]
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
return quiz
|
|
196
|
+
},
|
|
197
|
+
retroAriaLabel() {
|
|
198
|
+
let label = ''
|
|
199
|
+
switch (this.retroType) {
|
|
200
|
+
case 'neutral':
|
|
201
|
+
label = this.$t('quizState.neutralAnswer')
|
|
202
|
+
break
|
|
203
|
+
case 'positive':
|
|
204
|
+
label = this.$t('quizState.goodAnswer')
|
|
205
|
+
break
|
|
206
|
+
case 'negative':
|
|
207
|
+
label = this.$t('quizState.badAnswer')
|
|
208
|
+
break
|
|
209
|
+
}
|
|
210
|
+
return label
|
|
211
|
+
},
|
|
212
|
+
|
|
213
|
+
/**
|
|
214
|
+
* @description
|
|
215
|
+
* @returns {string} the componant of the quiz input
|
|
216
|
+
*/
|
|
217
|
+
dynamicInputComponent() {
|
|
218
|
+
const { type_question } = this.quizData
|
|
219
|
+
|
|
220
|
+
let inputComponent
|
|
221
|
+
switch (true) {
|
|
222
|
+
case type_question == 'choix_unique':
|
|
223
|
+
inputComponent = 'AppCompInputRadio'
|
|
224
|
+
break
|
|
225
|
+
case type_question == 'dropdown':
|
|
226
|
+
inputComponent = 'AppCompInputDropdown'
|
|
227
|
+
break
|
|
228
|
+
case type_question == 'choix_mult':
|
|
229
|
+
inputComponent = 'AppCompInputCheckBox'
|
|
230
|
+
break
|
|
231
|
+
case type_question == 'reponse_ouverte':
|
|
232
|
+
inputComponent = 'AppCompInputTextBox'
|
|
233
|
+
break
|
|
234
|
+
case type_question == 'texte_troue':
|
|
235
|
+
inputComponent = 'AppCompInputTextToFill'
|
|
236
|
+
break
|
|
237
|
+
case type_question == 'texte_tableau':
|
|
238
|
+
inputComponent = 'AppCompInputTextTable'
|
|
239
|
+
break
|
|
240
|
+
case type_question == 'texte_troue_select':
|
|
241
|
+
inputComponent = 'AppCompInputTextToFillDropdown'
|
|
242
|
+
break
|
|
243
|
+
default:
|
|
244
|
+
inputComponent = `div`
|
|
245
|
+
}
|
|
246
|
+
return inputComponent
|
|
247
|
+
},
|
|
248
|
+
|
|
249
|
+
/**
|
|
250
|
+
* @description controls the showin of warning in the page when a prop is wrongly passed
|
|
251
|
+
*/
|
|
252
|
+
errorQuizItems() {
|
|
253
|
+
const errors = validateQuizData(this.quizData)
|
|
254
|
+
return errors
|
|
255
|
+
},
|
|
256
|
+
|
|
257
|
+
/**
|
|
258
|
+
* @description track the initialized state of this component.
|
|
259
|
+
* Indicate wheither the component is just starting.
|
|
260
|
+
* This computed property is use to control when some event /action should happen
|
|
261
|
+
*/
|
|
262
|
+
compJustInitialized() {
|
|
263
|
+
return this.justInitialized
|
|
264
|
+
},
|
|
265
|
+
|
|
266
|
+
/**
|
|
267
|
+
* @description Text to display for the quiz button
|
|
268
|
+
* @return {String} string to use by the local
|
|
269
|
+
* */
|
|
270
|
+
txtBtnSubmit() {
|
|
271
|
+
let str = ''
|
|
272
|
+
const { solution } = this.quizData
|
|
273
|
+
|
|
274
|
+
if (solution === null) str = this.$t('button.save')
|
|
275
|
+
//There is more than on element in the solution
|
|
276
|
+
else str = this.$t('button.quiz_verify')
|
|
277
|
+
|
|
278
|
+
return str
|
|
279
|
+
},
|
|
280
|
+
|
|
281
|
+
/**
|
|
282
|
+
* @description Ennonce of the quiz - parse to add image if defined
|
|
283
|
+
*/
|
|
284
|
+
|
|
285
|
+
quizEnnonce() {
|
|
286
|
+
const { ennonce } = this.quizData
|
|
287
|
+
let _ennonce = ennonce
|
|
288
|
+
return _ennonce
|
|
289
|
+
}
|
|
290
|
+
},
|
|
291
|
+
watch: {
|
|
292
|
+
getUserInteraction: {
|
|
293
|
+
async handler() {
|
|
294
|
+
if (!this.getPreviousAnswers(this.userInteraction.quizAnswers)) {
|
|
295
|
+
return setTimeout(() => (this.isReady = true), 500)
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
this.previousResponse = await this.getPreviousAnswers(
|
|
299
|
+
this.userInteraction.quizAnswers
|
|
300
|
+
)
|
|
301
|
+
this.response = this.previousResponse
|
|
302
|
+
},
|
|
303
|
+
immediate: true,
|
|
304
|
+
deep: true
|
|
305
|
+
}
|
|
306
|
+
},
|
|
307
|
+
created() {
|
|
308
|
+
this.$bus.$on('input-error', this.showErrorsMessages)
|
|
309
|
+
this.$bus.$on('hide-retro', this.hideShowRetro)
|
|
310
|
+
this.justInitialized = true
|
|
311
|
+
},
|
|
312
|
+
beforeUnmount() {
|
|
313
|
+
this.$bus.$off('input-error', this.showErrorsMessages)
|
|
314
|
+
this.$bus.$off('hide-retro', this.hideShowRetro)
|
|
315
|
+
},
|
|
316
|
+
|
|
317
|
+
mounted() {
|
|
318
|
+
if (import.meta.env.DEV) {
|
|
319
|
+
this.showErrorsMessages(this.errorQuizItems)
|
|
320
|
+
}
|
|
321
|
+
setTimeout(() => {
|
|
322
|
+
this.justInitialized = false
|
|
323
|
+
}, 500)
|
|
324
|
+
},
|
|
325
|
+
methods: {
|
|
326
|
+
showErrorsMessages(data) {
|
|
327
|
+
if (!data || !data.errors) return
|
|
328
|
+
if (data.e !== this.quizData.id) return
|
|
329
|
+
|
|
330
|
+
const { errorList, errorInConsole } = data.errors
|
|
331
|
+
this.errorList = errorList
|
|
332
|
+
|
|
333
|
+
if (errorInConsole.length)
|
|
334
|
+
errorInConsole.forEach((err) => {
|
|
335
|
+
console.warn(
|
|
336
|
+
` %cAppCompQuiz>>>${err}`,
|
|
337
|
+
'background: orange; color: white; display: block; margin:5px;'
|
|
338
|
+
)
|
|
339
|
+
})
|
|
340
|
+
},
|
|
341
|
+
|
|
342
|
+
/**
|
|
343
|
+
* @description disables the quiz
|
|
344
|
+
* @todo must be expanded for what happens when the quiz is disabled
|
|
345
|
+
*/
|
|
346
|
+
setQuizCompleted() {
|
|
347
|
+
this.quizCompleted = true
|
|
348
|
+
},
|
|
349
|
+
|
|
350
|
+
/**
|
|
351
|
+
* @description saves the submitted answer in the store
|
|
352
|
+
* - Create new entry in userData for the quiz if it doesn't exist or update its data
|
|
353
|
+
* - Send xapi competion statement of the quiz to LRS
|
|
354
|
+
* @param {Object} result - contain the user answer and success of is reponse
|
|
355
|
+
* deprecated param {Boolean} isCounted inticates in wich array it will ba saved, true = quizAnswers, false = pollAnswers
|
|
356
|
+
*/
|
|
357
|
+
saveAnswer(result) {
|
|
358
|
+
const { userAnswer, correctAnswer } = result
|
|
359
|
+
const quizID = this.quizData.id
|
|
360
|
+
|
|
361
|
+
if (
|
|
362
|
+
!this.userInteraction.quizAnswers ||
|
|
363
|
+
!this.userInteraction.quizAnswers[quizID]
|
|
364
|
+
) {
|
|
365
|
+
this.userInteraction.quizAnswers = {
|
|
366
|
+
...this.userInteraction.quizAnswers,
|
|
367
|
+
[quizID]: {
|
|
368
|
+
value: userAnswer,
|
|
369
|
+
total_attempts: this.totalAttempts
|
|
370
|
+
}
|
|
371
|
+
}
|
|
372
|
+
} else {
|
|
373
|
+
this.userInteraction.quizAnswers[quizID] = {
|
|
374
|
+
value: userAnswer,
|
|
375
|
+
total_attempts: this.totalAttempts
|
|
376
|
+
}
|
|
377
|
+
}
|
|
378
|
+
|
|
379
|
+
const id = this.getCurrentPage.activityRef
|
|
380
|
+
let aName = ''
|
|
381
|
+
let text = ''
|
|
382
|
+
let txtEnnonce = document.querySelector('.quiz-question div').innerText
|
|
383
|
+
|
|
384
|
+
switch (true) {
|
|
385
|
+
case id == 'A00':
|
|
386
|
+
aName = 'Introduction'
|
|
387
|
+
break
|
|
388
|
+
case id == 'A99':
|
|
389
|
+
aName = 'Conclusion'
|
|
390
|
+
break
|
|
391
|
+
|
|
392
|
+
default: {
|
|
393
|
+
let d = id.replace('A', '').trim()
|
|
394
|
+
d = parseInt(d)
|
|
395
|
+
aName = `Exercice ${this.quizData.id} de ${this.$t(
|
|
396
|
+
'text.activity'
|
|
397
|
+
)} ${d}`
|
|
398
|
+
}
|
|
399
|
+
}
|
|
400
|
+
|
|
401
|
+
switch (this.$i18n.locale) {
|
|
402
|
+
case 'fr':
|
|
403
|
+
if (this.getModuleInfo.courseID)
|
|
404
|
+
text = `${aName} de ${this.getModuleInfo.id} `
|
|
405
|
+
else text = `Le ${this.getModuleInfo.id}`
|
|
406
|
+
break
|
|
407
|
+
case 'en':
|
|
408
|
+
if (this.getModuleInfo.courseID)
|
|
409
|
+
text = `${aName} of ${this.getModuleInfo.id}`
|
|
410
|
+
else text = `The ${this.getModuleInfo.id}`
|
|
411
|
+
break
|
|
412
|
+
}
|
|
413
|
+
//Creating custom statement
|
|
414
|
+
const stmtQuiz = {
|
|
415
|
+
id: `exercices/${this.quizData.id}`,
|
|
416
|
+
verb: 'answered',
|
|
417
|
+
definition: txtEnnonce,
|
|
418
|
+
description: text,
|
|
419
|
+
type: 'http://adlnet.gov/expapi/activities/cmi.interaction',
|
|
420
|
+
result: {
|
|
421
|
+
response: JSON.stringify({ ...userAnswer, correctAnswer })
|
|
422
|
+
}
|
|
423
|
+
}
|
|
424
|
+
|
|
425
|
+
this.$bus.$emit('send-xapi-statement', stmtQuiz)
|
|
426
|
+
},
|
|
427
|
+
|
|
428
|
+
/**
|
|
429
|
+
* @description gets the answers for this quiz from existing answers record
|
|
430
|
+
* @param {Object} answers the list of quizes answered from the userInteraction
|
|
431
|
+
*/
|
|
432
|
+
getPreviousAnswers(answers) {
|
|
433
|
+
const { id, type_question } = this.quizData
|
|
434
|
+
|
|
435
|
+
if (!answers || !answers[id]) return null
|
|
436
|
+
const { value, total_attempts } = answers[id]
|
|
437
|
+
|
|
438
|
+
this.totalAttempts = total_attempts
|
|
439
|
+
if (this.quizLimitActive) this.quizCompleted = true //Signal that quiz has been completed
|
|
440
|
+
let isExecption = ['choix_unique']
|
|
441
|
+
if (isExecption.includes(type_question)) {
|
|
442
|
+
this.isReady = true
|
|
443
|
+
return (this.response = [value])
|
|
444
|
+
}
|
|
445
|
+
|
|
446
|
+
this.response = value.map((a) => (a = a.filled || a.selected))
|
|
447
|
+
this.isReady = true
|
|
448
|
+
return this.response
|
|
449
|
+
},
|
|
450
|
+
/**
|
|
451
|
+
* @description method to initalize validation process and save the user answer
|
|
452
|
+
* Call the validation method of the child component (input) and save tho store/LRS
|
|
453
|
+
* Validation Will process will only proceed when the user responses has changed
|
|
454
|
+
*/
|
|
455
|
+
handleValidation() {
|
|
456
|
+
const id = this.quizData.id
|
|
457
|
+
const child = this.$refs[`Qz_${id}`]
|
|
458
|
+
const result = child.validateAnswer()
|
|
459
|
+
|
|
460
|
+
this.hideShowRetro(child, true)
|
|
461
|
+
this.retroType = result.retroType
|
|
462
|
+
|
|
463
|
+
switch (true) {
|
|
464
|
+
case result.retroType == 'retro_positive':
|
|
465
|
+
this.retroTitle = this.quizData.retroaction.retro_positive.title
|
|
466
|
+
this.retroHtml = this.quizData.retroaction.retro_positive.hypertext
|
|
467
|
+
break
|
|
468
|
+
case result.retroType == 'retro_negative':
|
|
469
|
+
this.retroTitle = this.quizData.retroaction.retro_negative.title
|
|
470
|
+
this.retroHtml = this.quizData.retroaction.retro_negative.hypertext
|
|
471
|
+
break
|
|
472
|
+
case result.retroType == 'retro_neutre':
|
|
473
|
+
this.retroTitle = this.quizData.retroaction.retro_neutre.title
|
|
474
|
+
this.retroHtml = this.quizData.retroaction.retro_neutre.hypertext
|
|
475
|
+
break
|
|
476
|
+
}
|
|
477
|
+
// return if there no changes in the user response
|
|
478
|
+
if (
|
|
479
|
+
this.previousResponse &&
|
|
480
|
+
JSON.stringify(this.previousResponse) == JSON.stringify(this.response)
|
|
481
|
+
)
|
|
482
|
+
return
|
|
483
|
+
this.totalAttempts += 1
|
|
484
|
+
this.saveAnswer(result)
|
|
485
|
+
},
|
|
486
|
+
/**
|
|
487
|
+
* @description Enables/disables valdate button
|
|
488
|
+
*/
|
|
489
|
+
enableValidateButton(value) {
|
|
490
|
+
this.isEnabled = value
|
|
491
|
+
},
|
|
492
|
+
/**
|
|
493
|
+
* @description hide /show the retroaction
|
|
494
|
+
*/
|
|
495
|
+
hideShowRetro(el, value) {
|
|
496
|
+
if (!el || !el.id) return
|
|
497
|
+
if (el.id !== this.quizData.id) return
|
|
498
|
+
|
|
499
|
+
this.showRetro = value
|
|
500
|
+
},
|
|
501
|
+
skeletonType(type) {
|
|
502
|
+
if (type == 'reponse_ouverte') {
|
|
503
|
+
return `quiz-texte`
|
|
504
|
+
} else {
|
|
505
|
+
return `quiz-choix`
|
|
506
|
+
}
|
|
507
|
+
}
|
|
508
|
+
}
|
|
509
|
+
}
|
|
510
|
+
</script>
|
|
511
|
+
<style lang="scss">
|
|
512
|
+
.custom-control {
|
|
513
|
+
z-index: 0;
|
|
514
|
+
}
|
|
515
|
+
</style>
|