fcad-core-dragon 2.0.0-beta.4 → 2.0.0-beta.6
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 +19 -0
- package/README.md +71 -71
- package/bk.scss +117 -117
- package/package.json +8 -8
- package/src/$locales/en.json +23 -23
- package/src/$locales/fr.json +22 -21
- package/src/assets/data/onboardingMessages.json +47 -47
- package/src/components/AppBase.vue +186 -116
- package/src/components/AppBaseButton.test.js +22 -0
- package/src/components/AppBaseButton.vue +13 -5
- package/src/components/AppBaseErrorDisplay.vue +438 -438
- package/src/components/AppBaseFlipCard.vue +84 -84
- package/src/components/AppBaseModule.vue +207 -128
- package/src/components/AppBasePage.vue +18 -45
- package/src/components/AppBasePopover.vue +41 -41
- package/src/components/AppCompAudio.vue +20 -17
- package/src/components/AppCompBranchButtons.vue +28 -70
- package/src/components/AppCompButtonProgress.vue +4 -9
- package/src/components/AppCompCarousel.vue +120 -90
- package/src/components/{AppCompTranscript.vue → AppCompContainer.vue} +8 -1
- package/src/components/AppCompInputCheckBoxNext.vue +5 -0
- package/src/components/AppCompInputDropdownNext.vue +50 -8
- package/src/components/AppCompInputRadioNext.vue +152 -152
- package/src/components/AppCompInputTextNext.vue +21 -2
- package/src/components/AppCompInputTextTableNext.vue +1 -0
- package/src/components/AppCompInputTextToFillDropdownNext.vue +8 -0
- package/src/components/AppCompInputTextToFillNext.vue +171 -171
- package/src/components/AppCompJauge.vue +74 -74
- package/src/components/AppCompMenu.vue +13 -7
- package/src/components/AppCompMenuItem.vue +228 -228
- package/src/components/AppCompNavigation.vue +43 -30
- package/src/components/AppCompNoteCall.vue +64 -38
- package/src/components/AppCompNoteCredit.vue +303 -105
- package/src/components/AppCompPlayBarNext.vue +25 -12
- package/src/components/AppCompPlayBarProgress.vue +82 -82
- package/src/components/AppCompPopUpNext.vue +1 -4
- package/src/components/AppCompQuizNext.vue +8 -4
- package/src/components/AppCompQuizRecall.vue +44 -22
- package/src/components/AppCompSVGNext.vue +2 -3
- package/src/components/AppCompSettingsMenu.vue +172 -172
- package/src/components/AppCompTableOfContent.vue +61 -62
- package/src/components/AppCompVideoPlayer.vue +17 -15
- package/src/components/AppCompViewDisplay.vue +6 -6
- package/src/components/BaseModule.vue +1 -18
- package/src/components/tests__/AppBaseButton.spec.js +53 -0
- package/src/composables/useQuiz.js +206 -206
- package/src/externalComps/ModuleView.vue +22 -22
- package/src/externalComps/SummaryView.vue +91 -91
- package/src/main.js +37 -32
- package/src/mixins/$mediaMixins.js +819 -819
- package/src/mixins/timerMixin.js +155 -155
- package/src/module/stores/appStore.js +59 -6
- package/src/module/xapi/ADL.js +144 -4
- 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/wrapper copy.js +1963 -0
- package/src/module/xapi/wrapper.js +121 -188
- 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/helper.js +52 -12
- package/src/plugins/i18n.js +44 -44
- 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 +8 -2
- package/src/router/routes.js +312 -312
- package/src/shared/generalfuncs.js +210 -210
- package/src/shared/validators.js +38 -179
- package/vitest.config.js +19 -0
- package/src/components/AppCompPlayBar.vue +0 -1218
|
@@ -1,41 +1,41 @@
|
|
|
1
|
-
<!--
|
|
2
|
-
@ Description: This component is used to create a popover that is accessible via keyboard navigation (tab + space/enter)
|
|
3
|
-
-->
|
|
4
|
-
<template>
|
|
5
|
-
<v-tooltip
|
|
6
|
-
ref="tooltip"
|
|
7
|
-
v-bind="$attrs"
|
|
8
|
-
v-model="show"
|
|
9
|
-
transition="false"
|
|
10
|
-
:open-on-click="true"
|
|
11
|
-
:persistent="false"
|
|
12
|
-
>
|
|
13
|
-
<slot></slot>
|
|
14
|
-
</v-tooltip>
|
|
15
|
-
</template>
|
|
16
|
-
|
|
17
|
-
<script>
|
|
18
|
-
export default {
|
|
19
|
-
name: 'AppBasePopover',
|
|
20
|
-
data() {
|
|
21
|
-
return {
|
|
22
|
-
show: false,
|
|
23
|
-
alertContainer: null
|
|
24
|
-
}
|
|
25
|
-
},
|
|
26
|
-
watch: {
|
|
27
|
-
show: {
|
|
28
|
-
handler(newValue) {
|
|
29
|
-
if (newValue) {
|
|
30
|
-
const content = this.$refs.tooltip.contentEl.textContent
|
|
31
|
-
this.alertContainer.textContent = ''
|
|
32
|
-
this.alertContainer.textContent = content
|
|
33
|
-
}
|
|
34
|
-
}
|
|
35
|
-
}
|
|
36
|
-
},
|
|
37
|
-
mounted() {
|
|
38
|
-
this.alertContainer = document.getElementById('hiddenAlertContainer')
|
|
39
|
-
}
|
|
40
|
-
}
|
|
41
|
-
</script>
|
|
1
|
+
<!--
|
|
2
|
+
@ Description: This component is used to create a popover that is accessible via keyboard navigation (tab + space/enter)
|
|
3
|
+
-->
|
|
4
|
+
<template>
|
|
5
|
+
<v-tooltip
|
|
6
|
+
ref="tooltip"
|
|
7
|
+
v-bind="$attrs"
|
|
8
|
+
v-model="show"
|
|
9
|
+
transition="false"
|
|
10
|
+
:open-on-click="true"
|
|
11
|
+
:persistent="false"
|
|
12
|
+
>
|
|
13
|
+
<slot></slot>
|
|
14
|
+
</v-tooltip>
|
|
15
|
+
</template>
|
|
16
|
+
|
|
17
|
+
<script>
|
|
18
|
+
export default {
|
|
19
|
+
name: 'AppBasePopover',
|
|
20
|
+
data() {
|
|
21
|
+
return {
|
|
22
|
+
show: false,
|
|
23
|
+
alertContainer: null
|
|
24
|
+
}
|
|
25
|
+
},
|
|
26
|
+
watch: {
|
|
27
|
+
show: {
|
|
28
|
+
handler(newValue) {
|
|
29
|
+
if (newValue) {
|
|
30
|
+
const content = this.$refs.tooltip.contentEl.textContent
|
|
31
|
+
this.alertContainer.textContent = ''
|
|
32
|
+
this.alertContainer.textContent = content
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
},
|
|
37
|
+
mounted() {
|
|
38
|
+
this.alertContainer = document.getElementById('hiddenAlertContainer')
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
</script>
|
|
@@ -34,7 +34,7 @@
|
|
|
34
34
|
<audio
|
|
35
35
|
:id="id"
|
|
36
36
|
ref="m-audio"
|
|
37
|
-
@loadedmetadata="
|
|
37
|
+
@loadedmetadata="updateMediaDataAudio($event.target)"
|
|
38
38
|
>
|
|
39
39
|
<source
|
|
40
40
|
v-for="(aSource, index) in mSources"
|
|
@@ -128,39 +128,42 @@ export default {
|
|
|
128
128
|
|
|
129
129
|
methods: {
|
|
130
130
|
...mapActions(useAppStore, ['updateCurrentMediaElements']),
|
|
131
|
+
/**
|
|
132
|
+
* @description search the DOM for medias with duplicated ID declaration in the page
|
|
133
|
+
* @return return Array of all DOM MediaElement with same id
|
|
134
|
+
*/
|
|
135
|
+
checkDuplicatedID() {
|
|
136
|
+
const mediaList = document.querySelectorAll('.__media-container')
|
|
137
|
+
const duplicate = Array.from(mediaList).filter((media) =>
|
|
138
|
+
media.id.includes(this.id)
|
|
139
|
+
)
|
|
140
|
+
return duplicate
|
|
141
|
+
},
|
|
131
142
|
/**
|
|
132
143
|
* @description update the information for the mediaElement in the store
|
|
133
144
|
* @param {htmlElement} e
|
|
134
145
|
* @fires update-page to AppBaseModule.vue
|
|
135
146
|
*/
|
|
136
|
-
|
|
147
|
+
updateMediaDataAudio(e) {
|
|
137
148
|
//dispatch loading status of for this component
|
|
138
|
-
this.$bus.$emit('set-comp-status', '
|
|
149
|
+
this.$bus.$emit('set-comp-status', 'AppCompAudioPlayer', 'loading')
|
|
139
150
|
this.$bus.$emit('update-media-duration')
|
|
140
151
|
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
const hasEntry = mElements.findLastIndex(
|
|
145
|
-
(media) => media.id === e.target.id
|
|
146
|
-
)
|
|
147
|
-
|
|
148
|
-
if (hasEntry !== -1) {
|
|
149
|
-
// Should report Error to Console and Component template about this media
|
|
150
|
-
const errmsg = `Cet élément a le même ID q'un autre media. Vous ne devez pas avoir de médias avec le même ID dans une page.`
|
|
152
|
+
if (this.checkDuplicatedID().length > 1) {
|
|
153
|
+
const errmsg = `Cet élément a le même ID <b>${this.id}</b>q'un autre media. Vous ne devez pas avoir de médias avec le même ID dans une page.`
|
|
151
154
|
|
|
152
155
|
console.warn(
|
|
153
|
-
`%c WARNING!>>> You cannot use the same ID in your media elements for your page.`,
|
|
156
|
+
`%c WARNING!>>> You cannot use the same ID (${this.id}) in your media elements for your page.`,
|
|
154
157
|
'background: orange; color: white; display: block; border-radius:5px; margin:5px;'
|
|
155
158
|
)
|
|
156
159
|
|
|
157
|
-
this.$bus.$emit('set-comp-status', '
|
|
160
|
+
this.$bus.$emit('set-comp-status', 'AppCompAudioPlayer', 'ready')
|
|
158
161
|
return this.hasErr.push(errmsg)
|
|
159
162
|
}
|
|
160
163
|
|
|
161
|
-
this.updateCurrentMediaElements(e
|
|
164
|
+
this.updateCurrentMediaElements(e).then(() => {
|
|
162
165
|
this.isSet = true
|
|
163
|
-
this.$bus.$emit('set-comp-status', '
|
|
166
|
+
this.$bus.$emit('set-comp-status', 'AppCompAudioPlayer', 'ready')
|
|
164
167
|
})
|
|
165
168
|
},
|
|
166
169
|
errorHandling(e) {
|
|
@@ -38,7 +38,7 @@ Si la composante est appelée sans prop, les boutons par défaut seront génér
|
|
|
38
38
|
<v-row v-else>
|
|
39
39
|
<v-col
|
|
40
40
|
v-for="branch of branchsStateData"
|
|
41
|
-
:id="branch.id"
|
|
41
|
+
:id="`branch_${branch.id}`"
|
|
42
42
|
:key="branch.id"
|
|
43
43
|
class="branch-btn"
|
|
44
44
|
>
|
|
@@ -65,24 +65,18 @@ Si la composante est appelée sans prop, les boutons par défaut seront génér
|
|
|
65
65
|
v-else-if="isCard && cards.length"
|
|
66
66
|
class="branch-btn-wrapper branch-btn-card"
|
|
67
67
|
>
|
|
68
|
-
<v-card class="mx-auto" max-width="
|
|
69
|
-
<v-img :src="branch.imgFile" :alt="`${branch.imgAlt}`" cover
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
</v-img>
|
|
81
|
-
|
|
82
|
-
<v-card-title>{{ branch.title }}</v-card-title>
|
|
83
|
-
<v-card-text>
|
|
84
|
-
{{ branch.text }}
|
|
85
|
-
</v-card-text>
|
|
68
|
+
<v-card class="mx-auto" max-width="15rem">
|
|
69
|
+
<v-img :src="branch.imgFile" :alt="`${branch.imgAlt}`" cover />
|
|
70
|
+
|
|
71
|
+
<v-card-title v-html="branch.title"></v-card-title>
|
|
72
|
+
<v-card-text v-html="branch.text"></v-card-text>
|
|
73
|
+
|
|
74
|
+
<app-comp-button-progress
|
|
75
|
+
:set-target="sidebar"
|
|
76
|
+
:percent="branch.progression ? branch.progression : 0"
|
|
77
|
+
:branch-data="branch"
|
|
78
|
+
:btn-title="getMatchingElement(branch.id).btnTitle"
|
|
79
|
+
></app-comp-button-progress>
|
|
86
80
|
</v-card>
|
|
87
81
|
</div>
|
|
88
82
|
<div v-else class="branch-btn-wrapper">
|
|
@@ -98,7 +92,6 @@ Si la composante est appelée sans prop, les boutons par défaut seront génér
|
|
|
98
92
|
</v-row>
|
|
99
93
|
</template>
|
|
100
94
|
<script>
|
|
101
|
-
// ...
|
|
102
95
|
import { mapState } from 'pinia'
|
|
103
96
|
import { useAppStore } from '../module/stores/appStore'
|
|
104
97
|
import AppCompButtonProgress from './AppCompButtonProgress.vue'
|
|
@@ -141,35 +134,6 @@ export default {
|
|
|
141
134
|
break
|
|
142
135
|
}
|
|
143
136
|
}
|
|
144
|
-
//Start validation of required keys
|
|
145
|
-
if (isValid) {
|
|
146
|
-
const requiredKeys = ['imgFile', 'title', 'text', 'brchName']
|
|
147
|
-
|
|
148
|
-
for (let el of dataArray) {
|
|
149
|
-
//Validate that require key exist for each element in card
|
|
150
|
-
for (let k of requiredKeys) {
|
|
151
|
-
let index = dataArray.indexOf(el)
|
|
152
|
-
|
|
153
|
-
if (!el[k]) {
|
|
154
|
-
isValid = false
|
|
155
|
-
errMsg = `\n 💥 Missing 👉 ${k} 👈 in object ${
|
|
156
|
-
index + 1
|
|
157
|
-
} in ➡ card \n 🚩Allowed indexes are: 👉 ${requiredKeys}`
|
|
158
|
-
break
|
|
159
|
-
} else {
|
|
160
|
-
if (el[k].constructor !== String) {
|
|
161
|
-
errMsg = `\n 💥 Invalid 👉 ${k} 👈 declaration in ➡ object ${
|
|
162
|
-
index + 1
|
|
163
|
-
} ➡ of card. \n 🚩 Must be of type {String}`
|
|
164
|
-
isValid = false
|
|
165
|
-
break
|
|
166
|
-
}
|
|
167
|
-
}
|
|
168
|
-
if (!isValid) break
|
|
169
|
-
}
|
|
170
|
-
if (!isValid) break
|
|
171
|
-
}
|
|
172
|
-
}
|
|
173
137
|
}
|
|
174
138
|
|
|
175
139
|
if (errMsg) console.error(`🧱 app-comp-branch-buttons \n ${errMsg}`)
|
|
@@ -304,7 +268,6 @@ export default {
|
|
|
304
268
|
let { state = 'new', progression = 0 } = this.getBranchProgression(
|
|
305
269
|
branchData.id
|
|
306
270
|
)
|
|
307
|
-
// if (!state || !progression) return
|
|
308
271
|
|
|
309
272
|
branchData.state = state
|
|
310
273
|
branchData.progression = progression
|
|
@@ -323,7 +286,11 @@ export default {
|
|
|
323
286
|
btnTitle
|
|
324
287
|
}
|
|
325
288
|
|
|
326
|
-
if (
|
|
289
|
+
if (
|
|
290
|
+
typeof branchData.imgAlt === 'undefined' &&
|
|
291
|
+
branchData.imgFile === 'undefined' &&
|
|
292
|
+
import.meta.env.DEV
|
|
293
|
+
) {
|
|
327
294
|
branchData.imgAlt = ''
|
|
328
295
|
console.warn(
|
|
329
296
|
`Bouton d’embranchement: ALT image sans valeur définie pour ${branchData.id}`
|
|
@@ -413,27 +380,8 @@ export default {
|
|
|
413
380
|
}
|
|
414
381
|
//Start validation of required keys
|
|
415
382
|
if (isValid) {
|
|
416
|
-
const requiredKeys = ['imgFile', 'title', 'text', 'brchName']
|
|
417
|
-
|
|
418
383
|
for (let el of this.cards) {
|
|
419
384
|
let index = this.cards.indexOf(el)
|
|
420
|
-
//Validate that require key exist for each element in card
|
|
421
|
-
for (let k of requiredKeys) {
|
|
422
|
-
if (!el[k]) {
|
|
423
|
-
isValid = false
|
|
424
|
-
errMsg = `l'Attribut 👉 ${k} 👈 pour la carte ${index + 1} n'as pas été défini`
|
|
425
|
-
break
|
|
426
|
-
} else {
|
|
427
|
-
if (el[k].constructor !== String) {
|
|
428
|
-
errMsg = `l'Attribut 👉 ${k} 👈 pour la carte ${
|
|
429
|
-
index + 1
|
|
430
|
-
} doit être de type {String}`
|
|
431
|
-
isValid = false
|
|
432
|
-
break
|
|
433
|
-
}
|
|
434
|
-
}
|
|
435
|
-
if (!isValid) break
|
|
436
|
-
}
|
|
437
385
|
|
|
438
386
|
// Start validation of existing branch for this element
|
|
439
387
|
let searchEl = this.branchs.find((b) =>
|
|
@@ -544,9 +492,19 @@ export default {
|
|
|
544
492
|
<style lang="scss">
|
|
545
493
|
.branch-btn-wrapper {
|
|
546
494
|
position: relative;
|
|
495
|
+
|
|
547
496
|
.branch-btn-custom {
|
|
548
497
|
background-color: transparent;
|
|
549
498
|
border: none;
|
|
550
499
|
}
|
|
551
500
|
}
|
|
501
|
+
|
|
502
|
+
.v-card {
|
|
503
|
+
position: relative;
|
|
504
|
+
.button-progress-wrapper {
|
|
505
|
+
position: absolute;
|
|
506
|
+
right: 55px;
|
|
507
|
+
top: 15px;
|
|
508
|
+
}
|
|
509
|
+
}
|
|
552
510
|
</style>
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
<template>
|
|
2
2
|
<div class="button-progress-wrapper">
|
|
3
|
-
<button
|
|
3
|
+
<app-base-button
|
|
4
4
|
:id="`btn_branch_${branchData.id}`"
|
|
5
5
|
:target-ref="branchData.id"
|
|
6
6
|
:title="btnTitle || $t('text.place_holder.for_title_btn_progress')"
|
|
@@ -17,11 +17,13 @@
|
|
|
17
17
|
<svg v-if="branchData.state != status.COMPLETE">
|
|
18
18
|
<use href="#navigate-next-icon"></use>
|
|
19
19
|
</svg>
|
|
20
|
-
</button>
|
|
20
|
+
</app-base-button>
|
|
21
21
|
</div>
|
|
22
22
|
</template>
|
|
23
23
|
<script>
|
|
24
|
+
import AppBaseButton from './AppBaseButton.vue'
|
|
24
25
|
export default {
|
|
26
|
+
components: { AppBaseButton },
|
|
25
27
|
props: {
|
|
26
28
|
mini: {
|
|
27
29
|
type: Boolean,
|
|
@@ -63,7 +65,6 @@ export default {
|
|
|
63
65
|
},
|
|
64
66
|
|
|
65
67
|
created() {
|
|
66
|
-
// if (isNaN(this.percent)) this.percent = 0
|
|
67
68
|
this.$bus.$on('branching-hidden', this.resetIsActive)
|
|
68
69
|
},
|
|
69
70
|
|
|
@@ -98,12 +99,6 @@ export default {
|
|
|
98
99
|
.button-progress-wrapper {
|
|
99
100
|
&:not(.card-btn) {
|
|
100
101
|
.branch-btn-default {
|
|
101
|
-
// left: 4px;
|
|
102
|
-
// top: 4px;
|
|
103
|
-
// min-width: 42px;
|
|
104
|
-
// min-height: 42px;
|
|
105
|
-
right: 15px !important;
|
|
106
|
-
top: -15px !important;
|
|
107
102
|
min-width: 42px;
|
|
108
103
|
min-height: 42px;
|
|
109
104
|
}
|
|
@@ -3,82 +3,95 @@
|
|
|
3
3
|
@ What it does: Create an html element including an array of slides. These slides are objects including required property (imgSrc) and optional properties (imgAlt, Title, Hypertext).
|
|
4
4
|
-->
|
|
5
5
|
|
|
6
|
-
<template>
|
|
6
|
+
<template v-if="slides">
|
|
7
7
|
<section id="carousel" :aria-label="$t('text.carousel')">
|
|
8
|
-
<
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
next: currentSlide <= index
|
|
18
|
-
}"
|
|
19
|
-
role="group"
|
|
20
|
-
:aria-hidden="!(currentSlide == index + 1)"
|
|
21
|
-
>
|
|
22
|
-
<span class="sr-only">
|
|
23
|
-
{{
|
|
24
|
-
$t('text.slide') +
|
|
25
|
-
' ' +
|
|
26
|
-
(index + 1) +
|
|
27
|
-
' ' +
|
|
28
|
-
$t('text.of') +
|
|
29
|
-
' ' +
|
|
30
|
-
slideLength
|
|
31
|
-
}}
|
|
32
|
-
</span>
|
|
8
|
+
<app-base-error-display
|
|
9
|
+
v-if="errorsSlider.length"
|
|
10
|
+
:error-group="'component'"
|
|
11
|
+
:error-title="'ERREUR: CRÉATION CAROUSEL'"
|
|
12
|
+
:errors-list="errorsSlider"
|
|
13
|
+
></app-base-error-display>
|
|
14
|
+
<template v-else>
|
|
15
|
+
<div class="carousel-inner">
|
|
16
|
+
<div id="mycarousel-slides" class="carousel-slides" aria-live="polite">
|
|
33
17
|
<div
|
|
34
|
-
|
|
35
|
-
:
|
|
18
|
+
v-for="(slide, index) in slides"
|
|
19
|
+
:key="index"
|
|
20
|
+
class="carousel-slide"
|
|
21
|
+
:class="{
|
|
22
|
+
current: currentSlide == index + 1,
|
|
23
|
+
prev: currentSlide >= index + 2,
|
|
24
|
+
next: currentSlide <= index
|
|
25
|
+
}"
|
|
26
|
+
role="group"
|
|
27
|
+
:aria-hidden="!(currentSlide == index + 1)"
|
|
36
28
|
>
|
|
37
|
-
<
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
29
|
+
<span class="sr-only">
|
|
30
|
+
{{
|
|
31
|
+
$t('text.slide') +
|
|
32
|
+
' ' +
|
|
33
|
+
(index + 1) +
|
|
34
|
+
' ' +
|
|
35
|
+
$t('text.of') +
|
|
36
|
+
' ' +
|
|
37
|
+
slideLength
|
|
38
|
+
}}
|
|
39
|
+
</span>
|
|
40
|
+
<div
|
|
41
|
+
class="carousel-image"
|
|
42
|
+
:class="{ 'full-width': !(slide.title || slide.hypertext) }"
|
|
43
|
+
>
|
|
44
|
+
<img
|
|
45
|
+
:src="slide.imgSrc"
|
|
46
|
+
:alt="slide.imgAlt"
|
|
47
|
+
:aria-hidden="
|
|
48
|
+
slide.imgAlt == ' ' || !slide.imgAlt ? true : false
|
|
49
|
+
"
|
|
50
|
+
/>
|
|
51
|
+
</div>
|
|
52
|
+
<div v-if="slide.title || slide.hypertext" class="carousel-text">
|
|
53
|
+
<h3 v-if="slide.title">
|
|
54
|
+
{{ slide.title }}
|
|
55
|
+
</h3>
|
|
56
|
+
<div v-html="slide.hypertext"></div>
|
|
57
|
+
</div>
|
|
48
58
|
</div>
|
|
49
59
|
</div>
|
|
60
|
+
|
|
61
|
+
<div class="carousel-controls">
|
|
62
|
+
<app-base-button
|
|
63
|
+
id="carousel-btn-prev"
|
|
64
|
+
class="carousel-btn"
|
|
65
|
+
:aria-label="$t('button.carousel_prev')"
|
|
66
|
+
aria-controls="mycarousel-slides"
|
|
67
|
+
:title="$t('button.carousel_prev')"
|
|
68
|
+
:aria-disabled="disablePrev"
|
|
69
|
+
:is-disabled="disablePrev"
|
|
70
|
+
:disabled="disablePrev"
|
|
71
|
+
@click="prevSlide()"
|
|
72
|
+
>
|
|
73
|
+
<svg>
|
|
74
|
+
<use href="#fleche-gauche-icon"></use>
|
|
75
|
+
</svg>
|
|
76
|
+
</app-base-button>
|
|
77
|
+
<app-base-button
|
|
78
|
+
id="carousel-btn-next"
|
|
79
|
+
class="carousel-btn"
|
|
80
|
+
:aria-label="$t('button.carousel_next')"
|
|
81
|
+
aria-controls="mycarousel-slides"
|
|
82
|
+
:title="$t('button.carousel_next')"
|
|
83
|
+
:aria-disabled="disableNext"
|
|
84
|
+
:is-disabled="disableNext"
|
|
85
|
+
:disabled="disableNext"
|
|
86
|
+
@click="nextSlide()"
|
|
87
|
+
>
|
|
88
|
+
<svg>
|
|
89
|
+
<use href="#fleche-droite-icon"></use>
|
|
90
|
+
</svg>
|
|
91
|
+
</app-base-button>
|
|
92
|
+
</div>
|
|
50
93
|
</div>
|
|
51
|
-
|
|
52
|
-
<app-base-button
|
|
53
|
-
id="carousel-btn-prev"
|
|
54
|
-
class="carousel-btn"
|
|
55
|
-
:aria-label="$t('button.carousel_prev')"
|
|
56
|
-
aria-controls="mycarousel-slides"
|
|
57
|
-
:title="$t('button.carousel_prev')"
|
|
58
|
-
:disabled="disablePrev"
|
|
59
|
-
:aria-disabled="disablePrev"
|
|
60
|
-
@click="prevSlide()"
|
|
61
|
-
>
|
|
62
|
-
<svg>
|
|
63
|
-
<use href="#fleche-gauche-icon"></use>
|
|
64
|
-
</svg>
|
|
65
|
-
</app-base-button>
|
|
66
|
-
<app-base-button
|
|
67
|
-
id="carousel-btn-next"
|
|
68
|
-
class="carousel-btn"
|
|
69
|
-
:aria-label="$t('button.carousel_next')"
|
|
70
|
-
aria-controls="mycarousel-slides"
|
|
71
|
-
:title="$t('button.carousel_next')"
|
|
72
|
-
:disabled="disableNext"
|
|
73
|
-
:aria-disabled="disableNext"
|
|
74
|
-
@click="nextSlide()"
|
|
75
|
-
>
|
|
76
|
-
<svg>
|
|
77
|
-
<use href="#fleche-droite-icon"></use>
|
|
78
|
-
</svg>
|
|
79
|
-
</app-base-button>
|
|
80
|
-
</div>
|
|
81
|
-
</div>
|
|
94
|
+
</template>
|
|
82
95
|
<div class="carousel-index">
|
|
83
96
|
<p aria-hidden="true">{{ currentSlide }}/{{ slideLength }}</p>
|
|
84
97
|
</div>
|
|
@@ -86,16 +99,20 @@
|
|
|
86
99
|
</template>
|
|
87
100
|
|
|
88
101
|
<script>
|
|
102
|
+
import AppBaseErrorDisplay from './AppBaseErrorDisplay.vue'
|
|
89
103
|
export default {
|
|
90
104
|
name: 'AppCompSlider',
|
|
105
|
+
components: { AppBaseErrorDisplay },
|
|
91
106
|
props: {
|
|
92
|
-
slides: { type: Array, required: true } //Array of slides {imgSrc, imgAlt, title, hypertext}
|
|
107
|
+
slides: { type: Array, required: true }, //Array of slides {imgSrc, imgAlt, title, hypertext}
|
|
108
|
+
name: { type: String, default: 'toto cool' }
|
|
93
109
|
},
|
|
94
110
|
data() {
|
|
95
111
|
return {
|
|
96
|
-
currentSlide:
|
|
112
|
+
currentSlide: null, //Slide management
|
|
97
113
|
requiredProperties: ['imgSrc'], //For slides validation
|
|
98
|
-
optionalProperties: ['title', 'imgAlt', 'hypertext'] //For slides validation
|
|
114
|
+
optionalProperties: ['title', 'imgAlt', 'hypertext'], //For slides validation
|
|
115
|
+
errorsSlider: []
|
|
99
116
|
}
|
|
100
117
|
},
|
|
101
118
|
computed: {
|
|
@@ -106,26 +123,14 @@ export default {
|
|
|
106
123
|
return !(this.currentSlide < this.slideLength)
|
|
107
124
|
},
|
|
108
125
|
slideLength() {
|
|
109
|
-
return this.slides.length
|
|
126
|
+
return this.slides ? this.slides.length : 0
|
|
110
127
|
}
|
|
111
128
|
},
|
|
129
|
+
created() {
|
|
130
|
+
this.currentSlide = this.slides && this.slides.length ? 1 : 0
|
|
131
|
+
},
|
|
112
132
|
mounted() {
|
|
113
|
-
|
|
114
|
-
if (Array.isArray(this.slides) && this.slideLength > 0) {
|
|
115
|
-
//Validate properties for all the slides
|
|
116
|
-
for (const slide of this.slides) {
|
|
117
|
-
this.validateProperties(
|
|
118
|
-
this.requiredProperties,
|
|
119
|
-
this.optionalProperties,
|
|
120
|
-
slide
|
|
121
|
-
)
|
|
122
|
-
}
|
|
123
|
-
} else {
|
|
124
|
-
console.warn(
|
|
125
|
-
`%c WARNING!>>> AppCompSlider : slides must be an array of at least one item`,
|
|
126
|
-
'background: orange; color: white; display: block; margin:5px;'
|
|
127
|
-
)
|
|
128
|
-
}
|
|
133
|
+
this.validateCarousel()
|
|
129
134
|
},
|
|
130
135
|
methods: {
|
|
131
136
|
prevSlide() {
|
|
@@ -138,6 +143,27 @@ export default {
|
|
|
138
143
|
this.currentSlide++
|
|
139
144
|
}
|
|
140
145
|
},
|
|
146
|
+
validateCarousel() {
|
|
147
|
+
//Validating slides
|
|
148
|
+
|
|
149
|
+
if (Array.isArray(this.slides) && this.slideLength > 0) {
|
|
150
|
+
//Validate properties for all the slides
|
|
151
|
+
for (const slide of this.slides) {
|
|
152
|
+
this.validateProperties(
|
|
153
|
+
this.requiredProperties,
|
|
154
|
+
this.optionalProperties,
|
|
155
|
+
slide
|
|
156
|
+
)
|
|
157
|
+
}
|
|
158
|
+
} else {
|
|
159
|
+
let msg = `Le corrousel doit avoit au moins un element.`
|
|
160
|
+
this.errorsSlider.push(msg)
|
|
161
|
+
console.warn(
|
|
162
|
+
`%c WARNING!>>> AppCompSlider : slides must be an array of at least one item`,
|
|
163
|
+
'background: orange; color: white; display: block; margin:5px;'
|
|
164
|
+
)
|
|
165
|
+
}
|
|
166
|
+
},
|
|
141
167
|
//Validate the properties of a specific object (currentObject)
|
|
142
168
|
validateProperties(requiredProperties, optionalProperties, currentObject) {
|
|
143
169
|
let allProperties = requiredProperties.concat(optionalProperties)
|
|
@@ -154,6 +180,8 @@ export default {
|
|
|
154
180
|
`%c WARNING!>>> AppCompSlider : slides ${wrongProperties} invalid. Required properties: ${requiredProperties} . Optional properties: ${optionalProperties}`,
|
|
155
181
|
'background: orange; color: white; display: block; margin:5px;'
|
|
156
182
|
)
|
|
183
|
+
let msg = `Attribut(s) invalide(s): <i>${wrongProperties}</i> . Consultez la console pour plus de details.`
|
|
184
|
+
this.errorsSlider.push(msg)
|
|
157
185
|
}
|
|
158
186
|
//Validate if all required properties are present in currentObject
|
|
159
187
|
if (missingRequired.length > 0) {
|
|
@@ -161,6 +189,8 @@ export default {
|
|
|
161
189
|
`%c WARNING!>>> AppCompQuizSlider : slides missing required ${missingRequired} property. Required properties: ${requiredProperties} . Optional properties: ${optionalProperties}`,
|
|
162
190
|
'background: orange; color: white; display: block; margin:5px;'
|
|
163
191
|
)
|
|
192
|
+
let msg = `Une/certaines propriété(s) sont manquante(s). Consultez la console pour plus de details.`
|
|
193
|
+
this.errorsSlider.push(msg)
|
|
164
194
|
}
|
|
165
195
|
},
|
|
166
196
|
//Get all the invalids properties from the object
|
|
@@ -1,13 +1,20 @@
|
|
|
1
1
|
<template>
|
|
2
|
-
<div id="
|
|
2
|
+
<div :id="id" v-html="content"></div>
|
|
3
3
|
</template>
|
|
4
4
|
<script>
|
|
5
5
|
export default {
|
|
6
6
|
props: {
|
|
7
|
+
id: {
|
|
8
|
+
type: String,
|
|
9
|
+
default: 'shadow-container'
|
|
10
|
+
},
|
|
7
11
|
content: {
|
|
8
12
|
type: String,
|
|
9
13
|
default: `Hello Wolrd!`
|
|
10
14
|
}
|
|
15
|
+
},
|
|
16
|
+
mounted() {
|
|
17
|
+
console.log('😶🌫️ ', this.$props.id)
|
|
11
18
|
}
|
|
12
19
|
}
|
|
13
20
|
</script>
|