fcad-core-dragon 2.0.1 → 2.0.2-beta.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 +33 -33
- package/.eslintignore +29 -29
- package/.eslintrc.cjs +81 -81
- package/CHANGELOG +17 -1
- package/bk.scss +117 -117
- package/package.json +1 -1
- package/src/$locales/en.json +18 -4
- package/src/$locales/fr.json +17 -3
- package/src/assets/data/onboardingMessages.json +47 -47
- package/src/components/AppBase.vue +36 -341
- package/src/components/AppBaseErrorDisplay.vue +438 -438
- package/src/components/AppBaseFlipCard.vue +84 -84
- package/src/components/AppBaseModule.vue +16 -21
- package/src/components/AppBasePage.vue +45 -14
- package/src/components/AppBasePopover.vue +41 -41
- 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/AppCompPlayBarProgress.vue +82 -82
- package/src/components/AppCompPopUpNext.vue +6 -6
- package/src/components/AppCompQuiz.vue +500 -0
- package/src/components/AppCompQuizRecall.vue +113 -66
- package/src/components/AppCompSettingsMenu.vue +172 -172
- package/src/components/AppCompTableOfContent.vue +39 -10
- package/src/components/AppCompVideoPlayer.vue +1 -1
- package/src/components/AppCompViewDisplay.vue +6 -6
- package/src/composables/useQuiz.js +62 -179
- package/src/directives/nvdaFix.js +53 -0
- package/src/externalComps/ModuleView.vue +22 -22
- package/src/externalComps/SummaryView.vue +91 -91
- package/src/main.js +227 -30
- package/src/mixins/$mediaMixins.js +1 -11
- package/src/module/stores/appStore.js +29 -11
- 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/utils.js +167 -167
- package/src/module/xapi/verbs.js +294 -294
- package/src/module/xapi/xapiStatement.js +444 -444
- package/src/plugins/bus.js +8 -8
- package/src/plugins/gsap.js +14 -14
- package/src/plugins/i18n.js +44 -44
- package/src/plugins/idb.js +1 -1
- 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/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,264 @@
|
|
|
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" role="group">
|
|
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
|
+
aria-hidden="true"
|
|
41
|
+
class="radio-contenu"
|
|
42
|
+
v-html="choixReponse.value"
|
|
43
|
+
/>
|
|
44
|
+
<span
|
|
45
|
+
:id="`${id}_${choixReponse.id}-msg-erreur`"
|
|
46
|
+
:key="`msg_chx_${id}_${choixReponse.id}`"
|
|
47
|
+
class="sr-only"
|
|
48
|
+
>
|
|
49
|
+
{{ messageAccessibility[index] }}
|
|
50
|
+
</span>
|
|
51
|
+
</label>
|
|
52
|
+
</div>
|
|
53
|
+
</template>
|
|
54
|
+
</fieldset>
|
|
55
|
+
</div>
|
|
56
|
+
</template>
|
|
57
|
+
<script>
|
|
58
|
+
import { useQuiz } from '../composables/useQuiz'
|
|
59
|
+
import { validateObjType } from '../shared/validators'
|
|
60
|
+
export default {
|
|
61
|
+
name: 'AppCompInputRadioNx',
|
|
62
|
+
/* PROPS USED FOR PARENT TO COMMUNICATE DATA TO CHILD */
|
|
63
|
+
props: {
|
|
64
|
+
modelValue: {
|
|
65
|
+
type: Array,
|
|
66
|
+
default: () => []
|
|
67
|
+
},
|
|
68
|
+
inputData: {
|
|
69
|
+
type: Array,
|
|
70
|
+
default: () => []
|
|
71
|
+
},
|
|
72
|
+
|
|
73
|
+
solution: {
|
|
74
|
+
type: Array,
|
|
75
|
+
default: () => []
|
|
76
|
+
},
|
|
77
|
+
|
|
78
|
+
id: {
|
|
79
|
+
type: String,
|
|
80
|
+
default: ''
|
|
81
|
+
}
|
|
82
|
+
},
|
|
83
|
+
/* EVENT EMITTED WHEN MODELVALUE CHANGES|UPDATE */
|
|
84
|
+
emits: ['update:modelValue', 'enable-submit'],
|
|
85
|
+
|
|
86
|
+
setup(props) {
|
|
87
|
+
const { retroType, addRetroStyle, resetRetroStyle } = useQuiz()
|
|
88
|
+
|
|
89
|
+
return { retroType, addRetroStyle, resetRetroStyle }
|
|
90
|
+
},
|
|
91
|
+
data() {
|
|
92
|
+
return {
|
|
93
|
+
quizInputDataValue: [],
|
|
94
|
+
result: null,
|
|
95
|
+
retro: [],
|
|
96
|
+
messageAccessibility: []
|
|
97
|
+
}
|
|
98
|
+
},
|
|
99
|
+
computed: {
|
|
100
|
+
fieldsetLabel() {
|
|
101
|
+
return `${this.$t('quizState.radioFieldset')} ${this.inputData.length} ${this.$t('quizState.options')}`
|
|
102
|
+
},
|
|
103
|
+
/* INTERNAL REACTIVE VALUE THAT BIND WITH MODELVALUE PROPS */
|
|
104
|
+
inputValue: {
|
|
105
|
+
get() {
|
|
106
|
+
return this.modelValue
|
|
107
|
+
},
|
|
108
|
+
|
|
109
|
+
set(newValue) {
|
|
110
|
+
this.$emit('update:modelValue', [...newValue])
|
|
111
|
+
}
|
|
112
|
+
},
|
|
113
|
+
/**
|
|
114
|
+
* internal reactive value that map the options to add SELECTED attribute on each option
|
|
115
|
+
* this attribute value can be changed when user select the option
|
|
116
|
+
*/
|
|
117
|
+
mappedOptions() {
|
|
118
|
+
const options = this.inputData.map(
|
|
119
|
+
(op) => (op = { ...op, selected: false })
|
|
120
|
+
)
|
|
121
|
+
|
|
122
|
+
return options
|
|
123
|
+
},
|
|
124
|
+
selected: {
|
|
125
|
+
/**
|
|
126
|
+
* Return the value of the selected element in the modelValue
|
|
127
|
+
*/
|
|
128
|
+
get() {
|
|
129
|
+
// return this.modelValue
|
|
130
|
+
const selectedItem = this.modelValue.find((item) => {
|
|
131
|
+
return item.selected
|
|
132
|
+
})
|
|
133
|
+
|
|
134
|
+
return selectedItem ? selectedItem.value : ''
|
|
135
|
+
},
|
|
136
|
+
/**
|
|
137
|
+
* Modify the value of a item in the array model and return the array
|
|
138
|
+
*/
|
|
139
|
+
set(newValue) {
|
|
140
|
+
this.resetRetroStyle([this.retro, this.messageAccessibility])
|
|
141
|
+
|
|
142
|
+
// create change THE SELECTED attribute OF THE selected option
|
|
143
|
+
const updatedModelValue = this.mappedOptions
|
|
144
|
+
.map((option) => ({
|
|
145
|
+
...option,
|
|
146
|
+
selected: option.value === newValue
|
|
147
|
+
}))
|
|
148
|
+
.filter((e) => e.selected)
|
|
149
|
+
|
|
150
|
+
this.inputValue = updatedModelValue // trigger the reactivity of the inputVAlue
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
},
|
|
154
|
+
watch: {
|
|
155
|
+
inputValue: {
|
|
156
|
+
handler(newValue, oldValue) {
|
|
157
|
+
if (this.$el && this.$el.id !== this.id) return
|
|
158
|
+
|
|
159
|
+
const selectedItem = this.modelValue.some((e) => e.selected)
|
|
160
|
+
this.$emit('enable-submit', selectedItem)
|
|
161
|
+
},
|
|
162
|
+
immediate: true,
|
|
163
|
+
deep: true
|
|
164
|
+
}
|
|
165
|
+
},
|
|
166
|
+
|
|
167
|
+
created() {
|
|
168
|
+
if (import.meta.env.DEV) {
|
|
169
|
+
let errors = this.validateInputData()
|
|
170
|
+
|
|
171
|
+
if (errors && errors.errorList.length)
|
|
172
|
+
return this.$bus.$emit('input-error', { e: this.id, errors })
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
this.quizInputDataValue = this.inputData
|
|
176
|
+
},
|
|
177
|
+
mounted() {},
|
|
178
|
+
|
|
179
|
+
methods: {
|
|
180
|
+
/**
|
|
181
|
+
* @description validate the raw data received by the component to render is view
|
|
182
|
+
* @returns {Object} errors - errorList: to display in view and errorConsole, to be displayed in console
|
|
183
|
+
*/
|
|
184
|
+
validateInputData() {
|
|
185
|
+
let errors = null //array for errors dectected
|
|
186
|
+
let stringType = ['id', 'value']
|
|
187
|
+
|
|
188
|
+
if (!this.inputData.length) return errors
|
|
189
|
+
for (let i = 0; i < this.inputData.length; i++) {
|
|
190
|
+
errors = validateObjType(
|
|
191
|
+
this.inputData[i],
|
|
192
|
+
{ stringType },
|
|
193
|
+
null,
|
|
194
|
+
`choix_reponse #${i + 1}`
|
|
195
|
+
)
|
|
196
|
+
const { errorList, errorInConsole } = errors
|
|
197
|
+
|
|
198
|
+
if (errorList.length || errorInConsole.length) return errors
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
return errors
|
|
202
|
+
},
|
|
203
|
+
/**
|
|
204
|
+
* @description Component validation method to validate the user input against the solution
|
|
205
|
+
* can handle logic of Css style to apply (correct/wrong Answer)
|
|
206
|
+
*/
|
|
207
|
+
validateAnswer() {
|
|
208
|
+
//Validate the user answer
|
|
209
|
+
let validatedResults
|
|
210
|
+
if (this.solution != null) {
|
|
211
|
+
validatedResults = this.inputValue.map(
|
|
212
|
+
(a) =>
|
|
213
|
+
(a = {
|
|
214
|
+
...a,
|
|
215
|
+
correct: this.solution.includes(a.id)
|
|
216
|
+
})
|
|
217
|
+
)
|
|
218
|
+
} else {
|
|
219
|
+
validatedResults = this.inputValue.map(
|
|
220
|
+
(a) =>
|
|
221
|
+
(a = {
|
|
222
|
+
...a,
|
|
223
|
+
correct: null
|
|
224
|
+
})
|
|
225
|
+
)
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
const mappedResults = this.mappedOptions.map((a) => {
|
|
229
|
+
if (this.solution != null) {
|
|
230
|
+
if (a.id == validatedResults[0].id) a = validatedResults[0]
|
|
231
|
+
a.correct = this.solution.includes(a.id)
|
|
232
|
+
return a
|
|
233
|
+
} else {
|
|
234
|
+
if (a.id == validatedResults[0].id) a = validatedResults[0]
|
|
235
|
+
a.correct = null
|
|
236
|
+
return a
|
|
237
|
+
}
|
|
238
|
+
})
|
|
239
|
+
|
|
240
|
+
//Set the valition style
|
|
241
|
+
let { classRetro, mesA11y } = this.addRetroStyle(
|
|
242
|
+
this.solution,
|
|
243
|
+
mappedResults,
|
|
244
|
+
this.inputData.length
|
|
245
|
+
)
|
|
246
|
+
this.retro = classRetro
|
|
247
|
+
this.messageAccessibility = mesA11y
|
|
248
|
+
|
|
249
|
+
//Retrive the user response from validation
|
|
250
|
+
this.result = mappedResults.filter((a) => a.selected)
|
|
251
|
+
|
|
252
|
+
let retro = this.retroType(this.solution, this.result)
|
|
253
|
+
|
|
254
|
+
const { correct, ...answer } = this.result[0]
|
|
255
|
+
|
|
256
|
+
return {
|
|
257
|
+
userAnswer: answer,
|
|
258
|
+
correctAnswer: correct,
|
|
259
|
+
retroType: retro
|
|
260
|
+
}
|
|
261
|
+
}
|
|
262
|
+
}
|
|
263
|
+
}
|
|
264
|
+
</script>
|
|
@@ -0,0 +1,148 @@
|
|
|
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>
|
|
@@ -0,0 +1,198 @@
|
|
|
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
|
+
</style>
|