fcad-core-dragon 2.1.1 → 2.2.0-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.
Files changed (164) hide show
  1. package/.editorconfig +7 -7
  2. package/.gitlab-ci.yml +106 -0
  3. package/.prettierrc +11 -11
  4. package/.vscode/extensions.json +8 -8
  5. package/.vscode/settings.json +16 -16
  6. package/CHANGELOG +529 -520
  7. package/README.md +57 -57
  8. package/artifacts/playwright-report/index.html +85 -0
  9. package/documentation/.vitepress/config.js +114 -114
  10. package/documentation/api-examples.md +49 -49
  11. package/documentation/composants/app-base-button.md +58 -58
  12. package/documentation/composants/app-base-error-display.md +59 -59
  13. package/documentation/composants/app-base-popover.md +68 -68
  14. package/documentation/composants/app-comp-audio.md +75 -75
  15. package/documentation/composants/app-comp-branch-buttons.md +111 -111
  16. package/documentation/composants/app-comp-button-progress.md +53 -53
  17. package/documentation/composants/app-comp-carousel.md +53 -53
  18. package/documentation/composants/app-comp-container.md +53 -53
  19. package/documentation/composants/app-comp-input-checkbox-next.md +42 -42
  20. package/documentation/composants/app-comp-input-dropdown-next.md +34 -34
  21. package/documentation/composants/app-comp-input-radio-next.md +39 -39
  22. package/documentation/composants/app-comp-input-text-next.md +35 -35
  23. package/documentation/composants/app-comp-input-text-table-next.md +34 -34
  24. package/documentation/composants/app-comp-input-text-to-fill-dropdown-next.md +53 -53
  25. package/documentation/composants/app-comp-input-text-to-fill-next.md +31 -31
  26. package/documentation/composants/app-comp-jauge.md +31 -31
  27. package/documentation/composants/app-comp-menu-item.md +55 -55
  28. package/documentation/composants/app-comp-menu.md +29 -29
  29. package/documentation/composants/app-comp-navigation.md +41 -41
  30. package/documentation/composants/app-comp-note-call.md +53 -53
  31. package/documentation/composants/app-comp-note-credit.md +53 -53
  32. package/documentation/composants/app-comp-play-bar-next.md +53 -53
  33. package/documentation/composants/app-comp-pop-up-next.md +93 -93
  34. package/documentation/composants/app-comp-quiz-next.md +235 -235
  35. package/documentation/composants/app-comp-quiz-recall.md +53 -53
  36. package/documentation/composants/app-comp-svg-next.md +53 -53
  37. package/documentation/composants/app-comp-table-of-content.md +50 -50
  38. package/documentation/composants/app-comp-video-player.md +82 -82
  39. package/documentation/composants.md +46 -46
  40. package/documentation/composants_critiques/ModelPageComposant.md +53 -53
  41. package/documentation/composants_critiques/app-base-module.md +43 -43
  42. package/documentation/composants_critiques/app-base-page.md +48 -48
  43. package/documentation/composants_critiques/app-base.md +311 -311
  44. package/documentation/composants_critiques/main.md +15 -15
  45. package/documentation/demarrage.md +50 -50
  46. package/documentation/deploiement.md +57 -57
  47. package/documentation/index.md +33 -33
  48. package/documentation/markdown-examples.md +85 -85
  49. package/documentation/public/vite.svg +14 -14
  50. package/documentation/public/vuejs.svg +1 -1
  51. package/documentation/public/vuetify.svg +5 -5
  52. package/eslint.config.js +60 -60
  53. package/package.json +69 -59
  54. package/playwright/index.html +12 -0
  55. package/playwright/index.js +21 -0
  56. package/playwright-ct.config.js +95 -0
  57. package/src/$locales/en.json +157 -157
  58. package/src/$locales/fr.json +120 -120
  59. package/src/assets/data/onboardingMessages.json +47 -47
  60. package/src/components/AppBase.vue +1171 -1169
  61. package/src/components/AppBaseButton.vue +90 -95
  62. package/src/components/AppBaseErrorDisplay.vue +438 -438
  63. package/src/components/AppBaseFlipCard.vue +84 -84
  64. package/src/components/AppBaseModule.vue +1639 -1634
  65. package/src/components/AppBasePage.vue +862 -866
  66. package/src/components/AppBasePopover.vue +41 -41
  67. package/src/components/AppBaseSkeleton.vue +66 -66
  68. package/src/components/AppCompAudio.vue +261 -256
  69. package/src/components/AppCompBranchButtons.vue +508 -508
  70. package/src/components/AppCompButtonProgress.vue +137 -132
  71. package/src/components/AppCompCarousel.vue +342 -336
  72. package/src/components/AppCompContainer.vue +29 -29
  73. package/src/components/AppCompInputCheckBoxNx.vue +326 -323
  74. package/src/components/AppCompInputDropdownNx.vue +302 -299
  75. package/src/components/AppCompInputRadioNx.vue +288 -284
  76. package/src/components/AppCompInputTextNx.vue +154 -153
  77. package/src/components/AppCompInputTextTableNx.vue +205 -202
  78. package/src/components/AppCompInputTextToFillDropdownNx.vue +341 -340
  79. package/src/components/AppCompInputTextToFillNx.vue +293 -313
  80. package/src/components/AppCompJauge.vue +81 -81
  81. package/src/components/AppCompMenu.vue +6 -1
  82. package/src/components/AppCompMenuItem.vue +246 -240
  83. package/src/components/AppCompNavigation.vue +977 -972
  84. package/src/components/AppCompNoteCall.vue +167 -161
  85. package/src/components/AppCompNoteCredit.vue +496 -491
  86. package/src/components/AppCompPlayBarNext.vue +2288 -2288
  87. package/src/components/AppCompPopUpNext.vue +508 -504
  88. package/src/components/AppCompQuizNext.vue +515 -510
  89. package/src/components/AppCompQuizRecall.vue +365 -350
  90. package/src/components/AppCompSVGNext.vue +346 -346
  91. package/src/components/AppCompSettingsMenu.vue +177 -172
  92. package/src/components/AppCompTableOfContent.vue +433 -427
  93. package/src/components/AppCompVideoPlayer.vue +378 -377
  94. package/src/components/AppCompViewDisplay.vue +6 -6
  95. package/src/components/BaseModule.vue +55 -55
  96. package/src/composables/useIdleDetector.js +56 -56
  97. package/src/composables/useQuiz.js +89 -89
  98. package/src/composables/useTimer.js +172 -172
  99. package/src/directives/nvdaFix.js +53 -53
  100. package/src/externalComps/ModuleView.vue +22 -22
  101. package/src/externalComps/SummaryView.vue +91 -91
  102. package/src/main.js +506 -476
  103. package/src/module/stores/appStore.js +960 -947
  104. package/src/module/xapi/ADL.js +520 -520
  105. package/src/module/xapi/Crypto/Hasher.js +241 -241
  106. package/src/module/xapi/Crypto/WordArray.js +278 -278
  107. package/src/module/xapi/Crypto/algorithms/BufferedBlockAlgorithm.js +103 -103
  108. package/src/module/xapi/Crypto/algorithms/C_algo.js +315 -315
  109. package/src/module/xapi/Crypto/algorithms/HMAC.js +9 -9
  110. package/src/module/xapi/Crypto/algorithms/SHA1.js +9 -9
  111. package/src/module/xapi/Crypto/encoders/Base.js +105 -105
  112. package/src/module/xapi/Crypto/encoders/Base64.js +99 -99
  113. package/src/module/xapi/Crypto/encoders/Hex.js +61 -61
  114. package/src/module/xapi/Crypto/encoders/Latin1.js +61 -61
  115. package/src/module/xapi/Crypto/encoders/Utf8.js +45 -45
  116. package/src/module/xapi/Crypto/index.js +53 -53
  117. package/src/module/xapi/Statement/activity.js +47 -47
  118. package/src/module/xapi/Statement/agent.js +55 -55
  119. package/src/module/xapi/Statement/group.js +26 -26
  120. package/src/module/xapi/Statement/index.js +259 -259
  121. package/src/module/xapi/Statement/statement.js +253 -253
  122. package/src/module/xapi/Statement/statementRef.js +23 -23
  123. package/src/module/xapi/Statement/substatement.js +22 -22
  124. package/src/module/xapi/Statement/verb.js +36 -36
  125. package/src/module/xapi/activitytypes.js +17 -17
  126. package/src/module/xapi/launch.js +157 -157
  127. package/src/module/xapi/utils.js +167 -167
  128. package/src/module/xapi/verbs.js +294 -294
  129. package/src/module/xapi/wrapper.js +1895 -1895
  130. package/src/module/xapi/xapiStatement.js +444 -444
  131. package/src/plugins/analytics.js +34 -34
  132. package/src/plugins/bus.js +12 -8
  133. package/src/plugins/gsap.js +17 -17
  134. package/src/plugins/helper.js +355 -358
  135. package/src/plugins/i18n.js +29 -26
  136. package/src/plugins/idb.js +227 -227
  137. package/src/plugins/save.js +37 -37
  138. package/src/plugins/scorm.js +287 -287
  139. package/src/plugins/xapi.js +11 -11
  140. package/src/public/index.html +33 -33
  141. package/src/router/index.js +57 -57
  142. package/src/router/routes.js +312 -312
  143. package/src/shared/generalfuncs.js +344 -344
  144. package/src/shared/validators.js +1018 -1018
  145. package/tests/component/AppBaseButton.spec.js +53 -0
  146. package/tests/component/pinia.spec.js +24 -0
  147. package/{src/components/tests__ → tests/unit}/AppBaseButton.spec.js +53 -53
  148. package/tests/unit/AppCompAudio.spec.js +134 -0
  149. package/tests/unit/AppCompCarousel.spec.js +54 -0
  150. package/tests/unit/AppCompInputCheckBoxNx.spec.js +59 -0
  151. package/tests/unit/AppCompInputDropdownNx.spec.js +51 -0
  152. package/tests/unit/AppCompInputRadioNx.spec.js +59 -0
  153. package/tests/unit/AppCompInputTextNx.spec.js +44 -0
  154. package/tests/unit/AppCompInputTextTableNx.spec.js +77 -0
  155. package/tests/unit/AppCompInputTextToFillDropdownNx.spec.js +60 -0
  156. package/tests/unit/AppCompInputTextToFillNx.spec.js +45 -0
  157. package/tests/unit/AppCompNoteCredit.spec.js +58 -0
  158. package/tests/unit/AppCompQuizNext.spec.js +112 -0
  159. package/tests/unit/AppCompVideoPlayer.spec.js +169 -0
  160. package/tests/unit/useQuiz.spec.js +72 -0
  161. package/{src/components/tests__ → tests/unit}/useTimer.spec.js +91 -91
  162. package/vitest.config.js +42 -19
  163. package/vitest.setup.js +96 -0
  164. package/src/components/AppBaseButton.test.js +0 -21
@@ -1,336 +1,342 @@
1
- <!--
2
- @ Description: This component is used to display a carousel.
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
- -->
5
-
6
- <template v-if="slides">
7
- <section id="carousel" :aria-label="$t('text.carousel')">
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">
17
- <div
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)"
28
- >
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
- <p v-if="slide.title" class="carousel-slide-title">
54
- {{ slide.title }}
55
- </p>
56
- <div v-html="slide.hypertext"></div>
57
- </div>
58
- </div>
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 aria-hidden="true" focusable="false">
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 aria-hidden="true" focusable="false">
89
- <use href="#fleche-droite-icon"></use>
90
- </svg>
91
- </app-base-button>
92
- </div>
93
- </div>
94
- </template>
95
- <div class="carousel-index">
96
- <p aria-hidden="true">{{ sliderPagination }}</p>
97
- </div>
98
- </section>
99
- </template>
100
-
101
- <script>
102
- import AppBaseErrorDisplay from './AppBaseErrorDisplay.vue'
103
- import { mapState } from 'pinia'
104
- import { useAppStore } from '../module/stores/appStore'
105
- export default {
106
- name: 'AppCompSlider',
107
- components: { AppBaseErrorDisplay },
108
- props: {
109
- slides: { type: Array, required: true }, //Array of slides {imgSrc, imgAlt, title, hypertext}
110
- name: { type: String, default: 'Sooo cool' }
111
- },
112
- data() {
113
- return {
114
- currentSlide: null, //Slide management
115
- requiredProperties: ['imgSrc'], //For slides validation
116
- optionalProperties: ['title', 'imgAlt', 'hypertext'], //For slides validation
117
- errorsSlider: []
118
- }
119
- },
120
- computed: {
121
- ...mapState(useAppStore, ['getAppConfigs']),
122
- disablePrev() {
123
- return !(this.currentSlide > 1)
124
- },
125
- disableNext() {
126
- return !(this.currentSlide < this.slideLength)
127
- },
128
- slideLength() {
129
- return this.slides ? this.slides.length : 0
130
- },
131
- sliderPagination() {
132
- return this.getAppConfigs.lang.toLowerCase() == 'en'
133
- ? `${this.currentSlide}/${this.slideLength}`
134
- : `${this.currentSlide} / ${this.slideLength}`
135
- }
136
- },
137
- created() {
138
- this.currentSlide = this.slides && this.slides.length ? 1 : 0
139
- },
140
- mounted() {
141
- this.validateCarousel()
142
- },
143
- methods: {
144
- prevSlide() {
145
- if (this.currentSlide > 1) {
146
- this.currentSlide--
147
- }
148
- },
149
- nextSlide() {
150
- if (this.currentSlide < this.slideLength) {
151
- this.currentSlide++
152
- }
153
- },
154
- validateCarousel() {
155
- //Validating slides
156
-
157
- if (Array.isArray(this.slides) && this.slideLength > 0) {
158
- //Validate properties for all the slides
159
- for (const slide of this.slides) {
160
- this.validateProperties(
161
- this.requiredProperties,
162
- this.optionalProperties,
163
- slide
164
- )
165
- }
166
- } else {
167
- let msg = `Le corrousel doit avoit au moins un element.`
168
- this.errorsSlider.push(msg)
169
- console.warn(
170
- `%c WARNING!>>> AppCompSlider : slides must be an array of at least one item`,
171
- 'background: orange; color: white; display: block; margin:5px;'
172
- )
173
- }
174
- },
175
- //Validate the properties of a specific object (currentObject)
176
- validateProperties(requiredProperties, optionalProperties, currentObject) {
177
- let allProperties = requiredProperties.concat(optionalProperties)
178
- let currentProperties = Object.keys(currentObject)
179
- let wrongProperties = this.checkerWrong(allProperties, currentProperties)
180
- let missingRequired = this.checkerMissing(
181
- requiredProperties,
182
- currentProperties
183
- )
184
-
185
- //Validate if all properties in currentObject are valid
186
- if (wrongProperties.length > 0) {
187
- console.warn(
188
- `%c WARNING!>>> AppCompSlider : slides ${wrongProperties} invalid. Required properties: ${requiredProperties} . Optional properties: ${optionalProperties}`,
189
- 'background: orange; color: white; display: block; margin:5px;'
190
- )
191
- let msg = `Attribut(s) invalide(s): <i>${wrongProperties}</i> . Consultez la console pour plus de details.`
192
- this.errorsSlider.push(msg)
193
- }
194
- //Validate if all required properties are present in currentObject
195
- if (missingRequired.length > 0) {
196
- console.warn(
197
- `%c WARNING!>>> AppCompQuizSlider : slides missing required ${missingRequired} property. Required properties: ${requiredProperties} . Optional properties: ${optionalProperties}`,
198
- 'background: orange; color: white; display: block; margin:5px;'
199
- )
200
- let msg = `Une/certaines propriété(s) sont manquante(s). Consultez la console pour plus de details.`
201
- this.errorsSlider.push(msg)
202
- }
203
- },
204
- //Get all the invalids properties from the object
205
- /*Add element from target array that are NOT in arr to the wrongArray*/
206
- checkerWrong(arr, target) {
207
- let wrongArray = []
208
- target.every((v) => {
209
- if (arr.includes(v)) {
210
- return true
211
- } else {
212
- wrongArray.push(v)
213
- return true
214
- }
215
- })
216
- return wrongArray
217
- },
218
- //Get all the required properties that are missing from the object
219
- /*Add element from the arr that are NOT in target arr to the missingArray*/
220
- checkerMissing(arr, target) {
221
- let missingArray = []
222
- arr.every((v) => {
223
- if (target.includes(v)) {
224
- return true
225
- } else {
226
- missingArray.push(v)
227
- return true
228
- }
229
- })
230
- return missingArray
231
- }
232
- }
233
- }
234
- </script>
235
- <style lang="scss">
236
- #carousel {
237
- width: max(100%, 350px);
238
- }
239
-
240
- .carousel-inner {
241
- position: relative;
242
- height: 100%;
243
- width: 100%;
244
- display: flex;
245
- justify-content: center;
246
- overflow: initial;
247
- }
248
-
249
- .carousel-slides {
250
- position: relative;
251
- height: 100%;
252
- width: calc(
253
- 100% - 136px
254
- ); //add 68px each side of the carousel for the buttons
255
- display: flex;
256
- flex-direction: row;
257
- overflow: hidden;
258
- }
259
-
260
- .carousel-slide {
261
- min-width: 100%;
262
- width: 100%;
263
- display: flex;
264
- flex-direction: row;
265
- opacity: 0.5;
266
- transition: all 0.6s ease-out;
267
- position: relative;
268
- @media screen and (max-width: 1000px) {
269
- flex-direction: column;
270
- }
271
- &.current {
272
- opacity: 1;
273
- }
274
- &.prev {
275
- margin-left: -100%;
276
- }
277
- }
278
-
279
- .carousel-image {
280
- width: 60%;
281
- height: 100%;
282
- display: flex;
283
- justify-content: center;
284
- &.full-width {
285
- width: 100%;
286
- }
287
- @media screen and (max-width: 1000px) {
288
- width: 100%;
289
- max-height: 250px;
290
- }
291
- img {
292
- max-width: 100%;
293
- width: 100%;
294
- min-height: 300px;
295
- object-fit: contain;
296
- @media screen and (max-width: 1000px) {
297
- min-height: 250px;
298
- max-height: 250px;
299
- }
300
- }
301
- }
302
-
303
- .carousel-text {
304
- width: 40%;
305
- height: 100%;
306
- padding: 48px 24px 32px 24px;
307
-
308
- @media screen and (max-width: 1000px) {
309
- width: 100%;
310
- }
311
- }
312
-
313
- .carousel-index {
314
- margin-top: 20px;
315
- display: flex;
316
- justify-content: center;
317
- }
318
-
319
- .carousel-controls {
320
- position: absolute;
321
- width: 75%;
322
- top: calc(50% - 25px); //Center buttons
323
-
324
- @media screen and (min-width: 510px) {
325
- width: 100%;
326
- }
327
- & {
328
- display: flex;
329
- flex-direction: row;
330
- justify-content: space-between;
331
- z-index: 3;
332
- user-select: none;
333
- min-width: 300px;
334
- }
335
- }
336
- </style>
1
+ <!--
2
+ @ Description: This component is used to display a carousel.
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
+ -->
5
+
6
+ <template v-if="slides">
7
+ <section id="carousel" :aria-label="$t('text.carousel')">
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">
17
+ <div
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)"
28
+ >
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
+ <p v-if="slide.title" class="carousel-slide-title">
54
+ {{ slide.title }}
55
+ </p>
56
+ <div v-html="slide.hypertext"></div>
57
+ </div>
58
+ </div>
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 aria-hidden="true" focusable="false">
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 aria-hidden="true" focusable="false">
89
+ <use href="#fleche-droite-icon"></use>
90
+ </svg>
91
+ </app-base-button>
92
+ </div>
93
+ </div>
94
+ </template>
95
+ <div class="carousel-index">
96
+ <p aria-hidden="true">{{ sliderPagination }}</p>
97
+ </div>
98
+ </section>
99
+ </template>
100
+
101
+ <script>
102
+ import AppBaseErrorDisplay from './AppBaseErrorDisplay.vue'
103
+ import { mapState } from 'pinia'
104
+ import { useAppStore } from '../module/stores/appStore'
105
+ import { useI18n } from 'vue-i18n'
106
+
107
+ export default {
108
+ name: 'AppCompSlider',
109
+ components: { AppBaseErrorDisplay },
110
+ props: {
111
+ slides: { type: Array, required: true }, //Array of slides {imgSrc, imgAlt, title, hypertext}
112
+ name: { type: String, default: 'Sooo cool' }
113
+ },
114
+ setup() {
115
+ const { t } = useI18n()
116
+ return { t }
117
+ },
118
+ data() {
119
+ return {
120
+ currentSlide: null, //Slide management
121
+ requiredProperties: ['imgSrc'], //For slides validation
122
+ optionalProperties: ['title', 'imgAlt', 'hypertext'], //For slides validation
123
+ errorsSlider: []
124
+ }
125
+ },
126
+ computed: {
127
+ ...mapState(useAppStore, ['getAppConfigs']),
128
+ disablePrev() {
129
+ return !(this.currentSlide > 1)
130
+ },
131
+ disableNext() {
132
+ return !(this.currentSlide < this.slideLength)
133
+ },
134
+ slideLength() {
135
+ return this.slides ? this.slides.length : 0
136
+ },
137
+ sliderPagination() {
138
+ return this.getAppConfigs.lang.toLowerCase() == 'en'
139
+ ? `${this.currentSlide}/${this.slideLength}`
140
+ : `${this.currentSlide} / ${this.slideLength}`
141
+ }
142
+ },
143
+ created() {
144
+ this.currentSlide = this.slides && this.slides.length ? 1 : 0
145
+ },
146
+ mounted() {
147
+ this.validateCarousel()
148
+ },
149
+ methods: {
150
+ prevSlide() {
151
+ if (this.currentSlide > 1) {
152
+ this.currentSlide--
153
+ }
154
+ },
155
+ nextSlide() {
156
+ if (this.currentSlide < this.slideLength) {
157
+ this.currentSlide++
158
+ }
159
+ },
160
+ validateCarousel() {
161
+ //Validating slides
162
+
163
+ if (Array.isArray(this.slides) && this.slideLength > 0) {
164
+ //Validate properties for all the slides
165
+ for (const slide of this.slides) {
166
+ this.validateProperties(
167
+ this.requiredProperties,
168
+ this.optionalProperties,
169
+ slide
170
+ )
171
+ }
172
+ } else {
173
+ let msg = `Le corrousel doit avoit au moins un element.`
174
+ this.errorsSlider.push(msg)
175
+ console.warn(
176
+ `%c WARNING!>>> AppCompSlider : slides must be an array of at least one item`,
177
+ 'background: orange; color: white; display: block; margin:5px;'
178
+ )
179
+ }
180
+ },
181
+ //Validate the properties of a specific object (currentObject)
182
+ validateProperties(requiredProperties, optionalProperties, currentObject) {
183
+ let allProperties = requiredProperties.concat(optionalProperties)
184
+ let currentProperties = Object.keys(currentObject)
185
+ let wrongProperties = this.checkerWrong(allProperties, currentProperties)
186
+ let missingRequired = this.checkerMissing(
187
+ requiredProperties,
188
+ currentProperties
189
+ )
190
+
191
+ //Validate if all properties in currentObject are valid
192
+ if (wrongProperties.length > 0) {
193
+ console.warn(
194
+ `%c WARNING!>>> AppCompSlider : slides ${wrongProperties} invalid. Required properties: ${requiredProperties} . Optional properties: ${optionalProperties}`,
195
+ 'background: orange; color: white; display: block; margin:5px;'
196
+ )
197
+ let msg = `Attribut(s) invalide(s): <i>${wrongProperties}</i> . Consultez la console pour plus de details.`
198
+ this.errorsSlider.push(msg)
199
+ }
200
+ //Validate if all required properties are present in currentObject
201
+ if (missingRequired.length > 0) {
202
+ console.warn(
203
+ `%c WARNING!>>> AppCompQuizSlider : slides missing required ${missingRequired} property. Required properties: ${requiredProperties} . Optional properties: ${optionalProperties}`,
204
+ 'background: orange; color: white; display: block; margin:5px;'
205
+ )
206
+ let msg = `Une/certaines propriété(s) sont manquante(s). Consultez la console pour plus de details.`
207
+ this.errorsSlider.push(msg)
208
+ }
209
+ },
210
+ //Get all the invalids properties from the object
211
+ /*Add element from target array that are NOT in arr to the wrongArray*/
212
+ checkerWrong(arr, target) {
213
+ let wrongArray = []
214
+ target.every((v) => {
215
+ if (arr.includes(v)) {
216
+ return true
217
+ } else {
218
+ wrongArray.push(v)
219
+ return true
220
+ }
221
+ })
222
+ return wrongArray
223
+ },
224
+ //Get all the required properties that are missing from the object
225
+ /*Add element from the arr that are NOT in target arr to the missingArray*/
226
+ checkerMissing(arr, target) {
227
+ let missingArray = []
228
+ arr.every((v) => {
229
+ if (target.includes(v)) {
230
+ return true
231
+ } else {
232
+ missingArray.push(v)
233
+ return true
234
+ }
235
+ })
236
+ return missingArray
237
+ }
238
+ }
239
+ }
240
+ </script>
241
+ <style lang="scss">
242
+ #carousel {
243
+ width: max(100%, 350px);
244
+ }
245
+
246
+ .carousel-inner {
247
+ position: relative;
248
+ height: 100%;
249
+ width: 100%;
250
+ display: flex;
251
+ justify-content: center;
252
+ overflow: initial;
253
+ }
254
+
255
+ .carousel-slides {
256
+ position: relative;
257
+ height: 100%;
258
+ width: calc(
259
+ 100% - 136px
260
+ ); //add 68px each side of the carousel for the buttons
261
+ display: flex;
262
+ flex-direction: row;
263
+ overflow: hidden;
264
+ }
265
+
266
+ .carousel-slide {
267
+ min-width: 100%;
268
+ width: 100%;
269
+ display: flex;
270
+ flex-direction: row;
271
+ opacity: 0.5;
272
+ transition: all 0.6s ease-out;
273
+ position: relative;
274
+ @media screen and (max-width: 1000px) {
275
+ flex-direction: column;
276
+ }
277
+ &.current {
278
+ opacity: 1;
279
+ }
280
+ &.prev {
281
+ margin-left: -100%;
282
+ }
283
+ }
284
+
285
+ .carousel-image {
286
+ width: 60%;
287
+ height: 100%;
288
+ display: flex;
289
+ justify-content: center;
290
+ &.full-width {
291
+ width: 100%;
292
+ }
293
+ @media screen and (max-width: 1000px) {
294
+ width: 100%;
295
+ max-height: 250px;
296
+ }
297
+ img {
298
+ max-width: 100%;
299
+ width: 100%;
300
+ min-height: 300px;
301
+ object-fit: contain;
302
+ @media screen and (max-width: 1000px) {
303
+ min-height: 250px;
304
+ max-height: 250px;
305
+ }
306
+ }
307
+ }
308
+
309
+ .carousel-text {
310
+ width: 40%;
311
+ height: 100%;
312
+ padding: 48px 24px 32px 24px;
313
+
314
+ @media screen and (max-width: 1000px) {
315
+ width: 100%;
316
+ }
317
+ }
318
+
319
+ .carousel-index {
320
+ margin-top: 20px;
321
+ display: flex;
322
+ justify-content: center;
323
+ }
324
+
325
+ .carousel-controls {
326
+ position: absolute;
327
+ width: 75%;
328
+ top: calc(50% - 25px); //Center buttons
329
+
330
+ @media screen and (min-width: 510px) {
331
+ width: 100%;
332
+ }
333
+ & {
334
+ display: flex;
335
+ flex-direction: row;
336
+ justify-content: space-between;
337
+ z-index: 3;
338
+ user-select: none;
339
+ min-width: 300px;
340
+ }
341
+ }
342
+ </style>