fcad-core-dragon 2.0.1 → 2.0.2-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.
- package/CHANGELOG +16 -1
- package/package.json +1 -1
- package/src/$locales/en.json +18 -4
- package/src/$locales/fr.json +17 -3
- package/src/components/AppBase.vue +36 -341
- package/src/components/AppBaseModule.vue +16 -21
- package/src/components/AppBasePage.vue +45 -14
- package/src/components/AppBaseSkeleton.vue +45 -0
- package/src/components/AppCompAudio.vue +12 -3
- package/src/components/AppCompButtonProgress.vue +13 -2
- package/src/components/AppCompCarousel.vue +12 -4
- package/src/components/AppCompInputCheckBoxNx.vue +324 -0
- package/src/components/AppCompInputDropdownNx.vue +295 -0
- package/src/components/AppCompInputRadioNx.vue +264 -0
- package/src/components/AppCompInputTextNx.vue +148 -0
- package/src/components/AppCompInputTextTableNx.vue +198 -0
- package/src/components/AppCompInputTextToFillDropdownNx.vue +291 -0
- package/src/components/AppCompInputTextToFillNx.vue +277 -0
- package/src/components/AppCompJauge.vue +11 -4
- package/src/components/AppCompMenu.vue +7 -14
- package/src/components/AppCompMenuItem.vue +7 -5
- package/src/components/AppCompNavigation.vue +21 -21
- package/src/components/AppCompNoteCall.vue +1 -0
- package/src/components/AppCompNoteCredit.vue +2 -1
- package/src/components/AppCompPlayBarNext.vue +94 -41
- package/src/components/AppCompPopUpNext.vue +6 -6
- package/src/components/AppCompQuizNx.vue +500 -0
- package/src/components/AppCompQuizRecall.vue +113 -66
- package/src/components/AppCompTableOfContent.vue +39 -10
- package/src/components/AppCompVideoPlayer.vue +1 -1
- package/src/composables/useQuiz.js +62 -179
- package/src/directives/nvdaFix.js +53 -0
- package/src/main.js +227 -30
- package/src/mixins/$mediaMixins.js +1 -11
- package/src/module/stores/appStore.js +29 -11
- package/src/plugins/idb.js +1 -1
- package/src/shared/generalfuncs.js +134 -0
- package/src/shared/validators.js +308 -234
- package/src/components/AppCompInputCheckBoxNext.vue +0 -205
- package/src/components/AppCompInputDropdownNext.vue +0 -201
- package/src/components/AppCompInputRadioNext.vue +0 -158
- package/src/components/AppCompInputTextNext.vue +0 -124
- package/src/components/AppCompInputTextTableNext.vue +0 -142
- package/src/components/AppCompInputTextToFillDropdownNext.vue +0 -238
- package/src/components/AppCompInputTextToFillNext.vue +0 -171
- package/src/components/AppCompQuizNext.vue +0 -2908
|
@@ -0,0 +1,324 @@
|
|
|
1
|
+
<!-- About this Component--
|
|
2
|
+
* Renders a checkboxe input type for the Quiz component
|
|
3
|
+
* Related Quiz to question: REPONSE_MULTIPLE
|
|
4
|
+
* Receives the a data object defined by user
|
|
5
|
+
* Used by AppCompQuizNx
|
|
6
|
+
-->
|
|
7
|
+
<template>
|
|
8
|
+
<div :id="id" class="input-box">
|
|
9
|
+
<fieldset :aria-label="fieldsetLabel">
|
|
10
|
+
<template
|
|
11
|
+
v-for="(choixReponse, index) in inputData"
|
|
12
|
+
:Key="`div_chx_${id}_${choixReponse.id}`"
|
|
13
|
+
>
|
|
14
|
+
<div class="box-checkbox" role="group">
|
|
15
|
+
<label
|
|
16
|
+
:key="`lbl_chx_${id}-${choixReponse.id}`"
|
|
17
|
+
:for="`boxchx_${id}_${choixReponse.id}`"
|
|
18
|
+
class="checkbox-label"
|
|
19
|
+
:class="[
|
|
20
|
+
{
|
|
21
|
+
answerSlct: isSelected(choixReponse.value)
|
|
22
|
+
},
|
|
23
|
+
`${retro[index]}`
|
|
24
|
+
]"
|
|
25
|
+
>
|
|
26
|
+
<input
|
|
27
|
+
:id="`boxchx_${id}_${choixReponse.id}`"
|
|
28
|
+
:key="`boxchx_${id}_${id}-${choixReponse.id}`"
|
|
29
|
+
:checked="isSelected(choixReponse.value)"
|
|
30
|
+
type="checkbox"
|
|
31
|
+
:name="`btn-checkbox-${id}`"
|
|
32
|
+
:aria-labelledby="`span_${id}_${choixReponse.id}`"
|
|
33
|
+
:aria-describedby="`${id}_${choixReponse.id}-msg-erreur`"
|
|
34
|
+
@change="onSelectUpdate($event, choixReponse.value)"
|
|
35
|
+
/>
|
|
36
|
+
<span
|
|
37
|
+
:id="`span_${id}_${choixReponse.id}`"
|
|
38
|
+
class="checkbox-contenu"
|
|
39
|
+
aria-hidden="true"
|
|
40
|
+
v-html="choixReponse.value"
|
|
41
|
+
></span>
|
|
42
|
+
<span
|
|
43
|
+
:id="`${id}_${choixReponse.id}-msg-erreur`"
|
|
44
|
+
:key="`msg_chx_${id}_${choixReponse.id}`"
|
|
45
|
+
class="sr-only"
|
|
46
|
+
>
|
|
47
|
+
{{ messageAccessibility[index] }}
|
|
48
|
+
</span>
|
|
49
|
+
</label>
|
|
50
|
+
</div>
|
|
51
|
+
</template>
|
|
52
|
+
</fieldset>
|
|
53
|
+
</div>
|
|
54
|
+
</template>
|
|
55
|
+
<script>
|
|
56
|
+
import { useQuiz } from '../composables/useQuiz'
|
|
57
|
+
import { validateObjType } from '../shared/validators'
|
|
58
|
+
export default {
|
|
59
|
+
name: 'AppCompInputCheckBoxNx',
|
|
60
|
+
/* PROPS USED FOR PARENT TO COMMUNICATE DATA TO CHILD */
|
|
61
|
+
props: {
|
|
62
|
+
modelValue: {
|
|
63
|
+
type: Array,
|
|
64
|
+
default: () => []
|
|
65
|
+
},
|
|
66
|
+
inputData: {
|
|
67
|
+
type: Array,
|
|
68
|
+
default: () => []
|
|
69
|
+
},
|
|
70
|
+
solution: {
|
|
71
|
+
type: Array,
|
|
72
|
+
default: () => []
|
|
73
|
+
},
|
|
74
|
+
id: {
|
|
75
|
+
type: String,
|
|
76
|
+
default: ''
|
|
77
|
+
}
|
|
78
|
+
},
|
|
79
|
+
emits: ['update:modelValue', 'enable-submit'],
|
|
80
|
+
setup() {
|
|
81
|
+
const { retroType, addRetroStyle, resetRetroStyle } = useQuiz()
|
|
82
|
+
|
|
83
|
+
return { retroType, addRetroStyle, resetRetroStyle }
|
|
84
|
+
},
|
|
85
|
+
data() {
|
|
86
|
+
return {
|
|
87
|
+
quizInputDataValue: [],
|
|
88
|
+
result: null,
|
|
89
|
+
retro: [],
|
|
90
|
+
messageAccessibility: [],
|
|
91
|
+
selectedItems: []
|
|
92
|
+
}
|
|
93
|
+
},
|
|
94
|
+
computed: {
|
|
95
|
+
fieldsetLabel() {
|
|
96
|
+
return `${this.$t('quizState.checkboxFieldset')} ${this.inputData.length} ${this.$t('quizState.options')}`
|
|
97
|
+
},
|
|
98
|
+
/* INTERNAL REACTIVE VALUE THAT BIND WITH MODELVALUE PROPS */
|
|
99
|
+
inputsValue: {
|
|
100
|
+
get() {
|
|
101
|
+
return this.modelValue
|
|
102
|
+
},
|
|
103
|
+
|
|
104
|
+
set(newValue) {
|
|
105
|
+
if (
|
|
106
|
+
(this.$el && this.$el.id !== this.id) ||
|
|
107
|
+
newValue.constructor !== Array
|
|
108
|
+
)
|
|
109
|
+
return
|
|
110
|
+
|
|
111
|
+
this.$emit('update:modelValue', newValue)
|
|
112
|
+
}
|
|
113
|
+
},
|
|
114
|
+
/**
|
|
115
|
+
* internal reactive value that map the options to add SELECTED attribute on each option
|
|
116
|
+
* this attribute value can be changed when user select the option
|
|
117
|
+
*/
|
|
118
|
+
mappedOptions() {
|
|
119
|
+
const options = this.inputData.map((op) => {
|
|
120
|
+
const selectedItem = this.modelValue.find((i) => i.id === op.id)
|
|
121
|
+
return {
|
|
122
|
+
...op,
|
|
123
|
+
selected: selectedItem ? selectedItem.selected : false
|
|
124
|
+
}
|
|
125
|
+
})
|
|
126
|
+
|
|
127
|
+
return options
|
|
128
|
+
}
|
|
129
|
+
},
|
|
130
|
+
watch: {
|
|
131
|
+
inputsValue: {
|
|
132
|
+
handler(newVal) {
|
|
133
|
+
this.$emit('enable-submit', newVal.length > 0)
|
|
134
|
+
},
|
|
135
|
+
deep: true,
|
|
136
|
+
immediate: true
|
|
137
|
+
}
|
|
138
|
+
},
|
|
139
|
+
created() {
|
|
140
|
+
if (import.meta.env.DEV) {
|
|
141
|
+
let errors = this.validateInputData()
|
|
142
|
+
|
|
143
|
+
if (errors && errors.errorList.length)
|
|
144
|
+
return this.$bus.$emit('input-error', { e: this.id, errors })
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
this.quizInputDataValue = this.inputData
|
|
148
|
+
},
|
|
149
|
+
mounted() {},
|
|
150
|
+
|
|
151
|
+
methods: {
|
|
152
|
+
/**
|
|
153
|
+
* @description validate the raw data received by the component to render is view
|
|
154
|
+
* @returns {Object} errors - errorList: to display in view and errorConsole, to be displayed in console
|
|
155
|
+
*/
|
|
156
|
+
validateInputData() {
|
|
157
|
+
let errors = null //array for errors dectected
|
|
158
|
+
let stringType = ['id', 'value']
|
|
159
|
+
|
|
160
|
+
if (!this.inputData.length) return errors
|
|
161
|
+
for (let i = 0; i < this.inputData.length; i++) {
|
|
162
|
+
errors = validateObjType(
|
|
163
|
+
this.inputData[i],
|
|
164
|
+
{ stringType },
|
|
165
|
+
null,
|
|
166
|
+
`choix_reponse #${i + 1}`
|
|
167
|
+
)
|
|
168
|
+
const { errorList, errorInConsole } = errors
|
|
169
|
+
|
|
170
|
+
if (errorList.length || errorInConsole.length) return errors
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
return errors
|
|
174
|
+
},
|
|
175
|
+
|
|
176
|
+
/**
|
|
177
|
+
* @description inicate wether the element is checked or not.
|
|
178
|
+
* Element is selected if in the modelValue
|
|
179
|
+
* @param value value of tje element to be checked
|
|
180
|
+
*/
|
|
181
|
+
isSelected(value) {
|
|
182
|
+
const existing = this.modelValue.includes(value)
|
|
183
|
+
return existing
|
|
184
|
+
},
|
|
185
|
+
|
|
186
|
+
/**
|
|
187
|
+
* @description Method to force update of this.inputsValue.
|
|
188
|
+
* this force the old value and new value of the input to be tracked by VUE.
|
|
189
|
+
* @param newValue, the new value of the input
|
|
190
|
+
* @param index index of the selected element
|
|
191
|
+
*/
|
|
192
|
+
onSelectUpdate(event, value) {
|
|
193
|
+
if (this.$el && this.$el.id !== this.id) return //prevent event from firing on other input
|
|
194
|
+
|
|
195
|
+
const isChecked = event.target.checked
|
|
196
|
+
this.resetRetroStyle([this.retro, this.messageAccessibility])
|
|
197
|
+
let updatedValue = [...this.modelValue]
|
|
198
|
+
|
|
199
|
+
if (isChecked) {
|
|
200
|
+
const existing = updatedValue.includes(value)
|
|
201
|
+
if (!existing) updatedValue.push(value)
|
|
202
|
+
} else updatedValue = updatedValue.filter((v) => v !== value)
|
|
203
|
+
|
|
204
|
+
this.$emit('update:modelValue', updatedValue)
|
|
205
|
+
},
|
|
206
|
+
/**
|
|
207
|
+
* @description Component validation method to validate the user input against the solution
|
|
208
|
+
* can handle logic of Css style to apply (correct/wrong Answer) and return the result
|
|
209
|
+
* @return {Object} result
|
|
210
|
+
*/
|
|
211
|
+
validateAnswer() {
|
|
212
|
+
// Validate the user answer
|
|
213
|
+
//Created a custom validated object for inputData
|
|
214
|
+
let mappedResults
|
|
215
|
+
if (this.solution != null) {
|
|
216
|
+
mappedResults = this.inputData.map(
|
|
217
|
+
(a) =>
|
|
218
|
+
(a = {
|
|
219
|
+
...a,
|
|
220
|
+
correct: this.solution.includes(a.id)
|
|
221
|
+
})
|
|
222
|
+
)
|
|
223
|
+
} else {
|
|
224
|
+
mappedResults = this.inputData.map(
|
|
225
|
+
(a) =>
|
|
226
|
+
(a = {
|
|
227
|
+
...a,
|
|
228
|
+
correct: null
|
|
229
|
+
})
|
|
230
|
+
)
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
let { classRetro, mesA11y } = this.addRetroStyle(
|
|
234
|
+
this.solution,
|
|
235
|
+
mappedResults,
|
|
236
|
+
this.inputData.length
|
|
237
|
+
)
|
|
238
|
+
|
|
239
|
+
this.retro = classRetro
|
|
240
|
+
this.messageAccessibility = mesA11y
|
|
241
|
+
|
|
242
|
+
//filer mapped to return a customized object of the user selection
|
|
243
|
+
this.result = mappedResults
|
|
244
|
+
.filter((a) => this.inputsValue.includes(a.value))
|
|
245
|
+
.map((a) => {
|
|
246
|
+
const { id, value, correct } = a
|
|
247
|
+
return { id, selected: value, correct }
|
|
248
|
+
})
|
|
249
|
+
|
|
250
|
+
let retro = this.retroType(this.solution, this.result)
|
|
251
|
+
|
|
252
|
+
let cAns
|
|
253
|
+
if (this.solution != null) cAns = this.computeResult(this.result)
|
|
254
|
+
else cAns = false
|
|
255
|
+
|
|
256
|
+
//Object to return validation to parent
|
|
257
|
+
return {
|
|
258
|
+
userAnswer: this.result,
|
|
259
|
+
correctAnswer: cAns,
|
|
260
|
+
retroType: retro
|
|
261
|
+
}
|
|
262
|
+
},
|
|
263
|
+
/**
|
|
264
|
+
* @description - compute the correct elements in user response
|
|
265
|
+
* compare with the solution and return wether user succeeded or failed
|
|
266
|
+
* @param {Object} result - user responses
|
|
267
|
+
* @retuns boolean
|
|
268
|
+
*/
|
|
269
|
+
computeResult(result) {
|
|
270
|
+
const res = this.result.filter((el) => el.correct)
|
|
271
|
+
|
|
272
|
+
return (
|
|
273
|
+
res.length == this.solution.length &&
|
|
274
|
+
this.solution.length == this.result.length
|
|
275
|
+
)
|
|
276
|
+
}
|
|
277
|
+
}
|
|
278
|
+
}
|
|
279
|
+
</script>
|
|
280
|
+
<style lang="scss">
|
|
281
|
+
fieldset {
|
|
282
|
+
border: inherit;
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
.custom-control-input:focus ~ .custom-control-label::before {
|
|
286
|
+
border: inherit;
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
.custom-control-input:focus ~ .custom-control-label::before {
|
|
290
|
+
box-shadow: inherit;
|
|
291
|
+
-webkit-box-shadow: inherit;
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
fieldset {
|
|
295
|
+
border: inherit;
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
.custom-checkbox {
|
|
299
|
+
width: 100%;
|
|
300
|
+
height: 100%;
|
|
301
|
+
padding-left: 0;
|
|
302
|
+
|
|
303
|
+
input {
|
|
304
|
+
width: 100%;
|
|
305
|
+
height: 100%;
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
label {
|
|
309
|
+
width: 100%;
|
|
310
|
+
height: 100%;
|
|
311
|
+
|
|
312
|
+
&:after,
|
|
313
|
+
&:before {
|
|
314
|
+
border: inherit;
|
|
315
|
+
top: 0px;
|
|
316
|
+
left: 0px;
|
|
317
|
+
height: 0;
|
|
318
|
+
width: 0;
|
|
319
|
+
background-color: inherit;
|
|
320
|
+
border: inherit;
|
|
321
|
+
}
|
|
322
|
+
}
|
|
323
|
+
}
|
|
324
|
+
</style>
|
|
@@ -0,0 +1,295 @@
|
|
|
1
|
+
<!-- About this Component--
|
|
2
|
+
* Renders a SELECT input to display choices of response for the Quiz component
|
|
3
|
+
* Related Quiz to question: DROPDOWN
|
|
4
|
+
* Receives the a data object defined by user
|
|
5
|
+
* Used by AppCompQuizNx
|
|
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="(singleDropdown, index) in inputData"
|
|
13
|
+
:key="singleDropdown.id"
|
|
14
|
+
class="dropdown-container"
|
|
15
|
+
:class="`dropdownlist-${singleDropdown.id}`"
|
|
16
|
+
>
|
|
17
|
+
<label
|
|
18
|
+
:id="`label_dropdown_${id}_${singleDropdown.id}`"
|
|
19
|
+
:for="`dropdown_${id}_${singleDropdown.id}`"
|
|
20
|
+
v-html="singleDropdown.ennonce"
|
|
21
|
+
></label>
|
|
22
|
+
|
|
23
|
+
<div class="cnt-input" :class="`${retro[index] ? retro[index] : ''}`">
|
|
24
|
+
<v-select
|
|
25
|
+
:id="`dropdown_${id}_${singleDropdown.id}`"
|
|
26
|
+
:model-value="inputsValue[singleDropdown.id]"
|
|
27
|
+
item-title="text"
|
|
28
|
+
:item-props="true"
|
|
29
|
+
:items="getItemOptions(index)"
|
|
30
|
+
:open-text="`${$t('message.dropdown_list')}_${singleDropdown.ennonce}`"
|
|
31
|
+
:aria-describedby="`${id}_${singleDropdown.id}-msg-erreur`"
|
|
32
|
+
@update:model-value="onSelectUpdate($event, index)"
|
|
33
|
+
></v-select>
|
|
34
|
+
|
|
35
|
+
<span
|
|
36
|
+
:id="`${id}_${singleDropdown.id}-msg-erreur`"
|
|
37
|
+
:key="`msg_chx_${id}_${singleDropdown.id}`"
|
|
38
|
+
class="sr-only"
|
|
39
|
+
>
|
|
40
|
+
{{ messageAccessibility[index] }}
|
|
41
|
+
</span>
|
|
42
|
+
</div>
|
|
43
|
+
</div>
|
|
44
|
+
</fieldset>
|
|
45
|
+
</div>
|
|
46
|
+
</template>
|
|
47
|
+
<script>
|
|
48
|
+
import { useQuiz } from '../composables/useQuiz'
|
|
49
|
+
import { validateObjType } from '../shared/validators'
|
|
50
|
+
export default {
|
|
51
|
+
name: 'AppCompInputDropdown',
|
|
52
|
+
/* PROPS USED FOR PARENT TO COMMUNICATE DATA TO CHILD */
|
|
53
|
+
props: {
|
|
54
|
+
modelValue: {
|
|
55
|
+
type: Array,
|
|
56
|
+
default: () => []
|
|
57
|
+
},
|
|
58
|
+
inputData: {
|
|
59
|
+
type: Array,
|
|
60
|
+
default: () => []
|
|
61
|
+
},
|
|
62
|
+
solution: {
|
|
63
|
+
type: Array,
|
|
64
|
+
default: () => []
|
|
65
|
+
},
|
|
66
|
+
id: {
|
|
67
|
+
type: String,
|
|
68
|
+
default: ''
|
|
69
|
+
}
|
|
70
|
+
},
|
|
71
|
+
emits: ['update:modelValue', 'enable-submit'],
|
|
72
|
+
setup(props) {
|
|
73
|
+
const { retroType, addRetroStyle, resetRetroStyle } = useQuiz()
|
|
74
|
+
|
|
75
|
+
return { retroType, addRetroStyle, resetRetroStyle }
|
|
76
|
+
},
|
|
77
|
+
data() {
|
|
78
|
+
return {
|
|
79
|
+
retro: [],
|
|
80
|
+
messageAccessibility: [],
|
|
81
|
+
result: [],
|
|
82
|
+
oldValue: null
|
|
83
|
+
}
|
|
84
|
+
},
|
|
85
|
+
computed: {
|
|
86
|
+
fieldsetLabel() {
|
|
87
|
+
let label
|
|
88
|
+
if (this.inputData.length === 1) {
|
|
89
|
+
label = this.$t('quizState.dropdownFieldsetSingle')
|
|
90
|
+
} else {
|
|
91
|
+
label = `${this.$t('quizState.dropdownFieldsetMulti1')} ${this.inputData.length} ${this.$t('quizState.dropdownFieldsetMulti2')}`
|
|
92
|
+
}
|
|
93
|
+
return label
|
|
94
|
+
},
|
|
95
|
+
/* INTERNAL REACTIVE VALUE THAT BIND WITH MODELVALUE PROPS */
|
|
96
|
+
inputsValue: {
|
|
97
|
+
get() {
|
|
98
|
+
return this.modelValue
|
|
99
|
+
},
|
|
100
|
+
set(newValue) {
|
|
101
|
+
this.$emit('update:modelValue', [...newValue])
|
|
102
|
+
}
|
|
103
|
+
},
|
|
104
|
+
mappedOptions() {
|
|
105
|
+
if (this.inputData.length === 0) return []
|
|
106
|
+
|
|
107
|
+
return this.inputData.map((op) => {
|
|
108
|
+
const key = Object.keys(op)[0]
|
|
109
|
+
const values = Object.values(op)[2]
|
|
110
|
+
|
|
111
|
+
const newValues = [
|
|
112
|
+
{
|
|
113
|
+
value: null,
|
|
114
|
+
disabled: true,
|
|
115
|
+
selected: true,
|
|
116
|
+
text: this.$t('message.first_option_dropdown')
|
|
117
|
+
// text: 'choisir une option'
|
|
118
|
+
},
|
|
119
|
+
...values.map((el) => ({
|
|
120
|
+
value: el,
|
|
121
|
+
text: el,
|
|
122
|
+
selected: false
|
|
123
|
+
}))
|
|
124
|
+
]
|
|
125
|
+
|
|
126
|
+
return {
|
|
127
|
+
[key]: newValues
|
|
128
|
+
}
|
|
129
|
+
})
|
|
130
|
+
}
|
|
131
|
+
},
|
|
132
|
+
watch: {
|
|
133
|
+
inputsValue: {
|
|
134
|
+
handler() {
|
|
135
|
+
if (this.$el && this.$el.id !== this.id) return
|
|
136
|
+
const filled = this.modelValue.filter((val) => val && val.trim() !== '')
|
|
137
|
+
|
|
138
|
+
this.$emit('enable-submit', filled.length === this.inputData.length)
|
|
139
|
+
},
|
|
140
|
+
deep: true,
|
|
141
|
+
immediate: true
|
|
142
|
+
}
|
|
143
|
+
},
|
|
144
|
+
created() {
|
|
145
|
+
if (import.meta.env.DEV) {
|
|
146
|
+
let errors = this.validateInputData()
|
|
147
|
+
if (errors && errors.errorList.length) {
|
|
148
|
+
return this.$bus.$emit('input-error', { e: this.id, errors })
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
},
|
|
152
|
+
mounted() {},
|
|
153
|
+
|
|
154
|
+
methods: {
|
|
155
|
+
validateInputData() {
|
|
156
|
+
let errors = null
|
|
157
|
+
let stringType = ['id', 'ennonce']
|
|
158
|
+
let arrayType = ['option']
|
|
159
|
+
let e = null
|
|
160
|
+
|
|
161
|
+
if (!this.inputData.length) return errors
|
|
162
|
+
for (let i = 0; i < this.inputData.length; i++) {
|
|
163
|
+
e = validateObjType(
|
|
164
|
+
this.inputData[i],
|
|
165
|
+
{ stringType, arrayType },
|
|
166
|
+
null,
|
|
167
|
+
`liste déroulante #${i + 1}`
|
|
168
|
+
)
|
|
169
|
+
|
|
170
|
+
if (e.errorInConsole.length && e.errorList.length) {
|
|
171
|
+
errors.errorInConsole.push(e.errorInConsole[0])
|
|
172
|
+
errors.errorList.push(e.errorList[0])
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
return errors
|
|
177
|
+
},
|
|
178
|
+
/**
|
|
179
|
+
* @description Method to retrive the options of each selectable input in the mappedOption Object
|
|
180
|
+
* @param {Number } index - number representing the index of the item in the array of inputElements
|
|
181
|
+
* @Return a collection of item representing the options for a the selectables
|
|
182
|
+
*/
|
|
183
|
+
getItemOptions(index) {
|
|
184
|
+
return Object.values(this.mappedOptions[index])[0]
|
|
185
|
+
},
|
|
186
|
+
/**
|
|
187
|
+
* @description Method to force update of this.inputsValue.
|
|
188
|
+
* this force the old value and new value of the input to be tracked by VUE.
|
|
189
|
+
* @param newValue, the new value of the input
|
|
190
|
+
* @param index index of the selected element
|
|
191
|
+
*/
|
|
192
|
+
|
|
193
|
+
onSelectUpdate(newValue, index) {
|
|
194
|
+
if (this.$el && this.$el.id !== this.id) return //prevent event from firing on other input
|
|
195
|
+
this.resetRetroStyle([this.retro, this.messageAccessibility])
|
|
196
|
+
const updated = [...this.inputsValue] //create new array from inputsValue
|
|
197
|
+
updated[index] = newValue
|
|
198
|
+
this.inputsValue = updated //trigger vue reactivity for inputsValue
|
|
199
|
+
},
|
|
200
|
+
/**
|
|
201
|
+
* @description Component validation method to validate the user input against the solution
|
|
202
|
+
* can handle logic of Css style to apply (correct/wrong Answer) and return the result
|
|
203
|
+
* @return {Object} result
|
|
204
|
+
*/
|
|
205
|
+
validateAnswer() {
|
|
206
|
+
let mappedResults
|
|
207
|
+
if (this.solution != null) {
|
|
208
|
+
mappedResults = this.inputsValue.map((a, i) => {
|
|
209
|
+
const s = this.solution[i]
|
|
210
|
+
return { selected: a, correct: s == a }
|
|
211
|
+
})
|
|
212
|
+
} else {
|
|
213
|
+
mappedResults = this.inputsValue.map((a, i) => {
|
|
214
|
+
return { selected: a, correct: false }
|
|
215
|
+
})
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
let { classRetro, mesA11y } = this.addRetroStyle(
|
|
219
|
+
this.solution,
|
|
220
|
+
mappedResults,
|
|
221
|
+
this.inputData.length
|
|
222
|
+
)
|
|
223
|
+
|
|
224
|
+
this.retro = [...classRetro]
|
|
225
|
+
this.messageAccessibility = [...mesA11y]
|
|
226
|
+
|
|
227
|
+
this.retro = classRetro
|
|
228
|
+
this.messageAccessibility = mesA11y
|
|
229
|
+
|
|
230
|
+
this.result = mappedResults.filter((a) => a.selected)
|
|
231
|
+
let tr = this.retroType(this.solution, this.result)
|
|
232
|
+
|
|
233
|
+
let cAns
|
|
234
|
+
if (this.solution != null) cAns = this.computeResult(this.result)
|
|
235
|
+
else cAns = false
|
|
236
|
+
|
|
237
|
+
return {
|
|
238
|
+
userAnswer: this.result,
|
|
239
|
+
correctAnswer: cAns,
|
|
240
|
+
retroType: tr
|
|
241
|
+
}
|
|
242
|
+
},
|
|
243
|
+
computeResult(result) {
|
|
244
|
+
const res = result.filter((el) => el.correct)
|
|
245
|
+
return res.length == this.solution.length
|
|
246
|
+
}
|
|
247
|
+
}
|
|
248
|
+
}
|
|
249
|
+
</script>
|
|
250
|
+
<style lang="scss">
|
|
251
|
+
.dropdown-container {
|
|
252
|
+
position: relative;
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
div.texteatrou {
|
|
256
|
+
display: inline !important;
|
|
257
|
+
.cnt-input {
|
|
258
|
+
display: inline;
|
|
259
|
+
|
|
260
|
+
.v-input {
|
|
261
|
+
display: inline-block !important;
|
|
262
|
+
|
|
263
|
+
.v-input__control,
|
|
264
|
+
.v-field {
|
|
265
|
+
width: 240px !important;
|
|
266
|
+
grid-area: inherit !important;
|
|
267
|
+
|
|
268
|
+
.v-field__field {
|
|
269
|
+
height: 25px !important;
|
|
270
|
+
padding: 0 !important;
|
|
271
|
+
min-height: inherit !important;
|
|
272
|
+
|
|
273
|
+
.v-field__input {
|
|
274
|
+
padding-top: 0 !important;
|
|
275
|
+
padding-bottom: 0 !important;
|
|
276
|
+
min-height: inherit !important;
|
|
277
|
+
|
|
278
|
+
.v-select__selection {
|
|
279
|
+
min-height: inherit !important;
|
|
280
|
+
}
|
|
281
|
+
}
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
.v-field__append-inner {
|
|
285
|
+
margin-right: 6px;
|
|
286
|
+
}
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
.v-input__details {
|
|
290
|
+
display: none;
|
|
291
|
+
}
|
|
292
|
+
}
|
|
293
|
+
}
|
|
294
|
+
}
|
|
295
|
+
</style>
|