fcad-core-dragon 2.1.0 → 2.1.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (160) hide show
  1. package/.editorconfig +7 -7
  2. package/.gitlab-ci.yml +124 -0
  3. package/.prettierrc +11 -11
  4. package/.vscode/extensions.json +8 -8
  5. package/.vscode/settings.json +46 -16
  6. package/CHANGELOG +520 -520
  7. package/README.md +57 -57
  8. package/documentation/.vitepress/config.js +114 -114
  9. package/documentation/api-examples.md +49 -49
  10. package/documentation/composants/app-base-button.md +58 -58
  11. package/documentation/composants/app-base-error-display.md +59 -59
  12. package/documentation/composants/app-base-popover.md +68 -68
  13. package/documentation/composants/app-comp-audio.md +75 -75
  14. package/documentation/composants/app-comp-branch-buttons.md +111 -111
  15. package/documentation/composants/app-comp-button-progress.md +53 -53
  16. package/documentation/composants/app-comp-carousel.md +53 -53
  17. package/documentation/composants/app-comp-container.md +53 -53
  18. package/documentation/composants/app-comp-input-checkbox-next.md +42 -42
  19. package/documentation/composants/app-comp-input-dropdown-next.md +34 -34
  20. package/documentation/composants/app-comp-input-radio-next.md +39 -39
  21. package/documentation/composants/app-comp-input-text-next.md +35 -35
  22. package/documentation/composants/app-comp-input-text-table-next.md +34 -34
  23. package/documentation/composants/app-comp-input-text-to-fill-dropdown-next.md +53 -53
  24. package/documentation/composants/app-comp-input-text-to-fill-next.md +31 -31
  25. package/documentation/composants/app-comp-jauge.md +31 -31
  26. package/documentation/composants/app-comp-menu-item.md +55 -55
  27. package/documentation/composants/app-comp-menu.md +29 -29
  28. package/documentation/composants/app-comp-navigation.md +41 -41
  29. package/documentation/composants/app-comp-note-call.md +53 -53
  30. package/documentation/composants/app-comp-note-credit.md +53 -53
  31. package/documentation/composants/app-comp-play-bar-next.md +53 -53
  32. package/documentation/composants/app-comp-pop-up-next.md +93 -93
  33. package/documentation/composants/app-comp-quiz-next.md +235 -235
  34. package/documentation/composants/app-comp-quiz-recall.md +53 -53
  35. package/documentation/composants/app-comp-svg-next.md +53 -53
  36. package/documentation/composants/app-comp-table-of-content.md +50 -50
  37. package/documentation/composants/app-comp-video-player.md +82 -82
  38. package/documentation/composants.md +46 -46
  39. package/documentation/composants_critiques/ModelPageComposant.md +53 -53
  40. package/documentation/composants_critiques/app-base-module.md +43 -43
  41. package/documentation/composants_critiques/app-base-page.md +48 -48
  42. package/documentation/composants_critiques/app-base.md +311 -311
  43. package/documentation/composants_critiques/main.md +15 -15
  44. package/documentation/demarrage.md +50 -50
  45. package/documentation/deploiement.md +57 -57
  46. package/documentation/index.md +33 -33
  47. package/documentation/markdown-examples.md +85 -85
  48. package/documentation/public/vite.svg +14 -14
  49. package/documentation/public/vuejs.svg +1 -1
  50. package/documentation/public/vuetify.svg +5 -5
  51. package/eslint.config.js +60 -60
  52. package/junit-report.xml +182 -0
  53. package/package.json +66 -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 +3 -2
  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 +325 -323
  74. package/src/components/AppCompInputDropdownNx.vue +302 -299
  75. package/src/components/AppCompInputRadioNx.vue +287 -284
  76. package/src/components/AppCompInputTextNx.vue +156 -153
  77. package/src/components/AppCompInputTextTableNx.vue +205 -202
  78. package/src/components/AppCompInputTextToFillDropdownNx.vue +343 -340
  79. package/src/components/AppCompInputTextToFillNx.vue +316 -313
  80. package/src/components/AppCompJauge.vue +81 -81
  81. package/src/components/AppCompMenu.vue +6 -2
  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 +2290 -2288
  87. package/src/components/AppCompPopUpNext.vue +508 -504
  88. package/src/components/AppCompQuizNext.vue +515 -510
  89. package/src/components/AppCompQuizRecall.vue +355 -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 +377 -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 +493 -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 -15
  134. package/src/plugins/helper.js +355 -358
  135. package/src/plugins/i18n.js +27 -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/AppCompInputCheckBoxNx.spec.js +59 -0
  149. package/tests/unit/AppCompInputDropdownNx.spec.js +51 -0
  150. package/tests/unit/AppCompInputRadioNx.spec.js +59 -0
  151. package/tests/unit/AppCompInputTextNx.spec.js +44 -0
  152. package/tests/unit/AppCompInputTextTableNx.spec.js +77 -0
  153. package/tests/unit/AppCompInputTextToFillDropdownNx.spec.js +60 -0
  154. package/tests/unit/AppCompInputTextToFillNx.spec.js +45 -0
  155. package/tests/unit/AppCompQuizNext.spec.js +114 -0
  156. package/tests/unit/AppCompVideoPlayer.spec.js +177 -0
  157. package/{src/components/tests__ → tests/unit}/useTimer.spec.js +91 -91
  158. package/vitest.config.js +28 -19
  159. package/vitest.setup.js +28 -0
  160. package/src/components/AppBaseButton.test.js +0 -21
@@ -1,491 +1,496 @@
1
- <!--
2
- ----------------- MUST ADD ERROR GESTION ------------------
3
- ----------------- MUST ADD css ------------------
4
-
5
- @ Description:
6
- @ What it does:
7
- -->
8
- <template>
9
- <div id="noteCredit" :class="{ openW: showWidget }">
10
- <app-base-button
11
- id="close-nc"
12
- :title="$t('button.closePopUp')"
13
- :aria-label="$t('button.closePopUp')"
14
- @click="close()"
15
- >
16
- <svg aria-hidden="true" focusable="false">
17
- <use href="#close-square-icon" />
18
- </svg>
19
- </app-base-button>
20
- <div class="box-nc">
21
- <app-base-error-display
22
- v-if="errorData.length"
23
- :error-group="'component'"
24
- :error-title="'ERREUR: NOTES ET CREDITS'"
25
- :errors-list="errorData"
26
- :error-text="`Vous avez une/des erreur(s) dans la création des notes/crédits.`"
27
- ></app-base-error-display>
28
- <template v-else>
29
- <div v-if="notes" class="ctn-note">
30
- <p class="t-note">{{ $t('text.title_note') }}</p>
31
- <div id="notes-list">
32
- <template
33
- v-for="(note, index) of notes"
34
- :key="`nt_${note.page_ref}__${note.id.substring(3)}`"
35
- >
36
- <div class="note-item">
37
- <span>{{ index + 1 }}.</span>
38
- <app-base-button
39
- :id="`nt_${note.page_ref}__${note.id.substring(3)}`"
40
- type="button"
41
- :title="$t('text.title_link_bas')"
42
- class="backToNoteRef"
43
- :data-ref="`rnt_${note.page_ref}__${note.id.substring(3)}`"
44
- :note-ref="`rnt_${note.page_ref}__${note.id.substring(3)}`"
45
- :pageref="note.page_ref"
46
- :class="
47
- note.hasError ? 'widget-note note-error' : 'widget-note'
48
- "
49
- @click="
50
- handleNoteDisplay(
51
- `nt_${note.page_ref}__${note.id.substring(3)}`
52
- )
53
- "
54
- >
55
- <div
56
- class="btref-content"
57
- data-content=""
58
- v-html="`${note.text}`"
59
- ></div>
60
- </app-base-button>
61
- </div>
62
- </template>
63
- </div>
64
- </div>
65
- <div v-if="credits" class="ctn-credit">
66
- <p class="t-crdt">{{ $t('text.title_credit') }}</p>
67
-
68
- <ul id="credits-list">
69
- <li
70
- v-for="(credit, index) of credits"
71
- :key="`credit_${index + 1}`"
72
- :ref="`#nt_${index + 1}`"
73
- v-html="credit"
74
- ></li>
75
- </ul>
76
- </div>
77
- </template>
78
- </div>
79
- </div>
80
- </template>
81
- <script>
82
- import { mapState, mapActions } from 'pinia'
83
- import { useAppStore } from '../module/stores/appStore'
84
- import { validateObjType } from '../shared/validators'
85
-
86
- export default {
87
- props: {},
88
- data() {
89
- return {
90
- current: false,
91
- showWidget: false,
92
- sideBarIsOpen: null,
93
- prevNote: null,
94
- shouldDeactivate: false,
95
- errorData: [],
96
- notes: null,
97
- credits: null
98
- }
99
- },
100
- computed: {
101
- ...mapState(useAppStore, [
102
- 'getDataNoteCredit',
103
- 'getAllActivities',
104
- 'getCurrentPage',
105
- 'getNotes',
106
- 'getCredits'
107
- ])
108
- },
109
- created() {},
110
- beforeUnmount() {
111
- this.$bus.$off('credit-note', this.onCreditNote)
112
- this.$bus.$off('toggle-widget', this.onToggleWidget)
113
- this.$bus.$off('close-widget', this.close)
114
- this.$bus.$off('note-to-show', this.onNoteToShow)
115
- this.$bus.$off('side-bar-open', this.onSidebarVisible)
116
- },
117
- mounted() {
118
- // close or open widget depending which btn was click
119
- this.$bus.$on('toggle-widget', this.onToggleWidget)
120
- //close widget
121
- this.$bus.$on('close-widget', this.close)
122
- // show widget when note click and higligth note
123
- this.$bus.$on('note-to-show', this.onNoteToShow)
124
- this.$bus.$on('side-bar-open', this.onSidebarVisible)
125
- },
126
- methods: {
127
- ...mapActions(useAppStore, ['updateWidgetOpen']),
128
- getPageNotes(data) {
129
- if (!data) return
130
- const _notes = data
131
- let formatedNotes = []
132
- _notes.forEach((g) => {
133
- let count = 0
134
- Object.values(g)[0].map((n) => {
135
- count++
136
- if (
137
- this.validateData('note', {
138
- n,
139
- details: `${count} ${Object.keys(g)[0]} `
140
- })
141
- )
142
- return
143
- n['page_ref'] = `${Object.keys(g)[0]}`
144
- })
145
- //Add all the note to the temp array
146
- formatedNotes.push(...Object.values(g)[0])
147
- })
148
-
149
- return formatedNotes
150
- },
151
-
152
- getPageCredits(data) {
153
- if (!data) return
154
-
155
- let credits = data
156
- let count = 0
157
- for (const c of credits) {
158
- count++
159
- if (this.validateData('credit', { c, details: count })) return
160
- }
161
-
162
- return credits
163
- },
164
- setPageNotesAndCredits(data) {
165
- const { notes, credits } = data
166
- this.notes = this.getPageNotes(notes)
167
- this.credits = this.getPageCredits(credits)
168
- },
169
- /**
170
- * @description method to validate that the data for the note and credit.
171
- * @param {String} ctx context of the validation : credit | note.
172
- * @param {String| Object} data the data to be validate- Object type for note and String type for credit
173
- * @return {boolean}
174
- */
175
- validateData(ctx, data) {
176
- let errConsole = []
177
- if (this.errorData.length) this.errorData = [] //reset the error tracker
178
-
179
- switch (ctx) {
180
- case 'note': {
181
- if (data.constructor !== Object) {
182
- this.errorData.push(
183
- "Mauvais d'éclaration de la note. Doit être de type <i>Objet</i>"
184
- )
185
- throw Error(
186
- `Unexpected definition the note. Expecting Object but received ${typeof data}`
187
- )
188
- }
189
-
190
- let stringType = ['id', 'text'] //expected attribute in the note declaration
191
-
192
- let { errorInConsole, errorList } = validateObjType(
193
- data.n,
194
- {
195
- stringType
196
- },
197
- null,
198
- `Note ${data.details}`
199
- )
200
-
201
- if (errorInConsole.length) errConsole.push(...errorInConsole)
202
- if (errorList.length) this.errorData.push(...errorList)
203
- break
204
- }
205
- case 'credit':
206
- if (data.c.constructor !== String) {
207
- this.errorData.push(
208
- `Mauvaise déclaration du <b>crédit ${data.details}</b>. Doit être de type <i>String</i>`
209
- )
210
- console.warn(
211
- `\n 💥 Invalid type declaration for 👉\x1B[1mcredit ${data.details} 👈\x1B[0m. Expecting type String \uD83D\uDEABtype ${typeof data.c}`
212
- )
213
- }
214
- }
215
- if (errConsole.length) {
216
- errConsole.forEach((err) => console.warn(err))
217
- }
218
- return this.errorData.length
219
- },
220
- onNoteToShow(el) {
221
- this.showWidget = true
222
- this.updateWidgetOpen(true)
223
-
224
- if (el) {
225
- let ref = el.getAttribute('data-Ref')
226
-
227
- const reg1 = /.*(P\d{2}(_E\d{2})?).*/gm //reg for the content of data-ref
228
- const id = `$1`
229
- const pageRef = ref.replace(reg1, id) //replace all to page id
230
-
231
- if (this.sideBarIsOpen && pageRef.length == 3)
232
- this.$bus.$emit('close-sidebar', 'ctxBranching') //send close signal if sibebar is open and note to focus is in parent page
233
-
234
- this.removehighlight()
235
-
236
- let elAtt = document.getElementsByClassName('widget-note')
237
- let arr = Array.from(elAtt)
238
-
239
- for (let element of arr) {
240
- if (ref == element.getAttribute('id')) {
241
- element.setAttribute('datacurrent', true)
242
- element.focus()
243
- }
244
- }
245
- }
246
- },
247
-
248
- onToggleWidget(data) {
249
- if (data.type !== 'noteCredit') return (this.showWidget = false)
250
- const { notes, credits } = data.content
251
-
252
- this.setPageNotesAndCredits({ notes, credits })
253
- this.showWidget = !this.showWidget
254
- this.updateWidgetOpen(this.showWidget) //update data in store
255
- },
256
- close() {
257
- let elNote = document.getElementsByClassName('widget-note')
258
- let elCall = document.getElementsByClassName('callEndNote')
259
-
260
- let arrNote = Array.from(elNote)
261
- let arrCall = Array.from(elCall)
262
-
263
- for (let element of arrNote) {
264
- element.setAttribute('datacurrent', false)
265
- }
266
-
267
- for (let element of arrCall) {
268
- element.setAttribute('datacurrentnote', false)
269
- }
270
- this.showWidget = false
271
- this.updateWidgetOpen(false)
272
- },
273
- removehighlight() {
274
- let elAtt = document.querySelectorAll("[datacurrent='true']")
275
-
276
- let arr = Array.from(elAtt)
277
-
278
- for (let element of arr) {
279
- element.setAttribute('datacurrent', false)
280
- }
281
- },
282
-
283
- focusNote(el, page) {
284
- this.remove()
285
- const DOMPages = document.getElementsByClassName(`app-page`)
286
- const targetPage = DOMPages.namedItem(page) //get the desired page
287
-
288
- let e = document.getElementsByClassName('callEndNote')
289
-
290
- if (!targetPage) return
291
- let nts = targetPage.getElementsByClassName('callEndNote')
292
-
293
- let t = el.getAttribute('note-ref')
294
- let s = document.querySelectorAll(`[data-ref="${t}"]`)
295
-
296
- if (s && s[0]) s[0].setAttribute('datacurrent', true)
297
-
298
- for (let element of e) {
299
- element.setAttribute('datacurrentnote', false) //reset the data attribute of all notes
300
- }
301
- let targetNote = nts.namedItem(t)
302
-
303
- if (targetNote) {
304
- targetNote.setAttribute('datacurrentnote', true)
305
-
306
- targetNote.childNodes[0].focus()
307
- } else {
308
- el.setAttribute('aria-disabled', 'true')
309
- el.setAttribute('disabled', 'true')
310
- el.querySelector('.btref-content').setAttribute(
311
- 'data-content',
312
- '\u26A0\uFE0F'
313
- )
314
- el.querySelector('div').style =
315
- 'color:red; text-decoration:line-through;'
316
- el.style.color = 'red'
317
-
318
- el.removeEventListener('click', this.handleNoteDisplay)
319
- }
320
- this.prevNote = el
321
- },
322
-
323
- remove() {
324
- let e = document.getElementsByClassName('widget-note')
325
- for (let element of e) {
326
- element.setAttribute('datacurrent', false)
327
- }
328
- },
329
-
330
- onSidebarVisible(state) {
331
- this.sideBarIsOpen = state
332
- },
333
-
334
- /**
335
- * @description method handle the behavior of note displaying
336
- * @param {Object} evt - the event that trigger the action
337
- * @emits event - 'open-sidebar' to display notes in branch | 'close-sidebar' to display not in parent when branch open
338
- *
339
- */
340
- handleNoteDisplay(ref) {
341
- /*
342
- * Note:
343
- * Element target (button) is wrongly referenced when Screen-reader is active and element selected With TAB
344
- * The Div in the button is selected not the button.
345
- * This ensure that the button is always set as targeted element
346
- */
347
- const widgetContent = document.getElementsByClassName('backToNoteRef') //target all the button of in the notes widget
348
- const elTarget = widgetContent.namedItem(ref) // Target current button
349
- const pageRef = elTarget.getAttribute('pageref')
350
-
351
- if (!this.sideBarIsOpen) this.prevNote = null //reset previous when sidebar is opened
352
-
353
- let prevPageref = this.prevNote
354
- ? this.prevNote.getAttribute('pageref')
355
- : null
356
-
357
- switch (true) {
358
- case pageRef && pageRef.length > 3: {
359
- //this is the branching page Send message to open the side bar
360
- this.$bus.$emit('open-sidebar', {
361
- ctx: 'ctxBranching',
362
- e: pageRef,
363
- persist: this.sideBarIsOpen || pageRef == prevPageref //this will prevent the sidebar to close when the note is in same page
364
- })
365
- //Should set focus after side bar is opened. Need to delay to not interfere with side bar focus
366
- pageRef == prevPageref
367
- ? setTimeout(() => this.focusNote(elTarget, pageRef), 100)
368
- : setTimeout(() => this.focusNote(elTarget, pageRef), 400)
369
-
370
- break
371
- }
372
- default:
373
- //Should have the side bar close if it is opened and note is in parent page
374
- if (this.sideBarIsOpen && pageRef.length == 3)
375
- this.$bus.$emit('close-sidebar', 'ctxBranching')
376
-
377
- return setTimeout(() => this.focusNote(elTarget, pageRef), 100) //Need to delay to not have side bar close animation finished
378
- }
379
-
380
- this.prevNote = elTarget
381
- }
382
- }
383
- }
384
- </script>
385
- <style lang="scss">
386
- #noteCredit {
387
- display: none;
388
- pointer-events: none;
389
- position: absolute;
390
- left: 75px;
391
- top: 0;
392
- width: 419px;
393
- transition: left 0.5s ease-in-out;
394
-
395
- &.openW {
396
- pointer-events: all;
397
- display: block;
398
- opacity: 1;
399
- }
400
-
401
- #close-nc {
402
- padding: 11px;
403
- position: absolute;
404
- right: 24px;
405
- margin-bottom: 0;
406
-
407
- &:focus {
408
- box-shadow: inherit !important;
409
- }
410
- }
411
-
412
- // .note-txt {
413
- // pointer-events: none;
414
- // }
415
-
416
- .box-nc {
417
- margin-top: 54px;
418
- overflow-y: auto;
419
- min-height: 160px;
420
- max-height: 321px;
421
- padding-right: 26px;
422
-
423
- .t-note {
424
- margin-bottom: 8px;
425
- }
426
-
427
- #notes-list {
428
- .note-item {
429
- display: flex;
430
- flex-direction: row;
431
- flex-wrap: wrap;
432
- margin-bottom: 5px;
433
-
434
- color: #deeff8;
435
-
436
- span {
437
- font-size: 0.65rem;
438
- padding: 8px 0 0 0;
439
- }
440
-
441
- .backToNoteRef {
442
- width: 90%;
443
- margin-left: 5px;
444
- padding-left: 5px;
445
-
446
- .btref-content {
447
- text-align: left;
448
- }
449
- }
450
- }
451
- }
452
- }
453
-
454
- #credits-list {
455
- list-style-type: none;
456
- padding: 0;
457
- margin: 0;
458
- }
459
- #notes-list {
460
- padding: 0;
461
- margin: 0;
462
-
463
- .widget-note {
464
- padding: 5px 0;
465
-
466
- &.note-error {
467
- background-color: firebrick;
468
- }
469
-
470
- .btref-content {
471
- pointer-events: none;
472
- }
473
- }
474
- }
475
- .overlay-w {
476
- width: 100%;
477
- height: 100%;
478
- }
479
- }
480
-
481
- .app-nav {
482
- &.show,
483
- &:hover {
484
- .ctn-w {
485
- #noteCredit {
486
- left: 120px;
487
- }
488
- }
489
- }
490
- }
491
- </style>
1
+ <!--
2
+ ----------------- MUST ADD ERROR GESTION ------------------
3
+ ----------------- MUST ADD css ------------------
4
+
5
+ @ Description:
6
+ @ What it does:
7
+ -->
8
+ <template>
9
+ <div id="noteCredit" :class="{ openW: showWidget }">
10
+ <app-base-button
11
+ id="close-nc"
12
+ :title="$t('button.closePopUp')"
13
+ :aria-label="$t('button.closePopUp')"
14
+ @click="close()"
15
+ >
16
+ <svg aria-hidden="true" focusable="false">
17
+ <use href="#close-square-icon" />
18
+ </svg>
19
+ </app-base-button>
20
+ <div class="box-nc">
21
+ <app-base-error-display
22
+ v-if="errorData.length"
23
+ :error-group="'component'"
24
+ :error-title="'ERREUR: NOTES ET CREDITS'"
25
+ :errors-list="errorData"
26
+ :error-text="`Vous avez une/des erreur(s) dans la création des notes/crédits.`"
27
+ ></app-base-error-display>
28
+ <template v-else>
29
+ <div v-if="notes" class="ctn-note">
30
+ <p class="t-note">{{ $t('text.title_note') }}</p>
31
+ <div id="notes-list">
32
+ <template
33
+ v-for="(note, index) of notes"
34
+ :key="`nt_${note.page_ref}__${note.id.substring(3)}`"
35
+ >
36
+ <div class="note-item">
37
+ <span>{{ index + 1 }}.</span>
38
+ <app-base-button
39
+ :id="`nt_${note.page_ref}__${note.id.substring(3)}`"
40
+ type="button"
41
+ :title="$t('text.title_link_bas')"
42
+ class="backToNoteRef"
43
+ :data-ref="`rnt_${note.page_ref}__${note.id.substring(3)}`"
44
+ :note-ref="`rnt_${note.page_ref}__${note.id.substring(3)}`"
45
+ :pageref="note.page_ref"
46
+ :class="
47
+ note.hasError ? 'widget-note note-error' : 'widget-note'
48
+ "
49
+ @click="
50
+ handleNoteDisplay(
51
+ `nt_${note.page_ref}__${note.id.substring(3)}`
52
+ )
53
+ "
54
+ >
55
+ <div
56
+ class="btref-content"
57
+ data-content=""
58
+ v-html="`${note.text}`"
59
+ ></div>
60
+ </app-base-button>
61
+ </div>
62
+ </template>
63
+ </div>
64
+ </div>
65
+ <div v-if="credits" class="ctn-credit">
66
+ <p class="t-crdt">{{ $t('text.title_credit') }}</p>
67
+
68
+ <ul id="credits-list">
69
+ <li
70
+ v-for="(credit, index) of credits"
71
+ :key="`credit_${index + 1}`"
72
+ :ref="`#nt_${index + 1}`"
73
+ v-html="credit"
74
+ ></li>
75
+ </ul>
76
+ </div>
77
+ </template>
78
+ </div>
79
+ </div>
80
+ </template>
81
+ <script>
82
+ import { mapState, mapActions } from 'pinia'
83
+ import { useAppStore } from '../module/stores/appStore'
84
+ import { validateObjType } from '../shared/validators'
85
+ import { useI18n } from 'vue-i18n'
86
+
87
+ export default {
88
+ props: {},
89
+ setup() {
90
+ const { t } = useI18n()
91
+ return { t }
92
+ },
93
+ data() {
94
+ return {
95
+ current: false,
96
+ showWidget: false,
97
+ sideBarIsOpen: null,
98
+ prevNote: null,
99
+ shouldDeactivate: false,
100
+ errorData: [],
101
+ notes: null,
102
+ credits: null
103
+ }
104
+ },
105
+ computed: {
106
+ ...mapState(useAppStore, [
107
+ 'getDataNoteCredit',
108
+ 'getAllActivities',
109
+ 'getCurrentPage',
110
+ 'getNotes',
111
+ 'getCredits'
112
+ ])
113
+ },
114
+ created() {},
115
+ beforeUnmount() {
116
+ this.$bus.$off('credit-note', this.onCreditNote)
117
+ this.$bus.$off('toggle-widget', this.onToggleWidget)
118
+ this.$bus.$off('close-widget', this.close)
119
+ this.$bus.$off('note-to-show', this.onNoteToShow)
120
+ this.$bus.$off('side-bar-open', this.onSidebarVisible)
121
+ },
122
+ mounted() {
123
+ // close or open widget depending which btn was click
124
+ this.$bus.$on('toggle-widget', this.onToggleWidget)
125
+ //close widget
126
+ this.$bus.$on('close-widget', this.close)
127
+ // show widget when note click and higligth note
128
+ this.$bus.$on('note-to-show', this.onNoteToShow)
129
+ this.$bus.$on('side-bar-open', this.onSidebarVisible)
130
+ },
131
+ methods: {
132
+ ...mapActions(useAppStore, ['updateWidgetOpen']),
133
+ getPageNotes(data) {
134
+ if (!data) return
135
+ const _notes = data
136
+ let formatedNotes = []
137
+ _notes.forEach((g) => {
138
+ let count = 0
139
+ Object.values(g)[0].map((n) => {
140
+ count++
141
+ if (
142
+ this.validateData('note', {
143
+ n,
144
+ details: `${count} ${Object.keys(g)[0]} `
145
+ })
146
+ )
147
+ return
148
+ n['page_ref'] = `${Object.keys(g)[0]}`
149
+ })
150
+ //Add all the note to the temp array
151
+ formatedNotes.push(...Object.values(g)[0])
152
+ })
153
+
154
+ return formatedNotes
155
+ },
156
+
157
+ getPageCredits(data) {
158
+ if (!data) return
159
+
160
+ let credits = data
161
+ let count = 0
162
+ for (const c of credits) {
163
+ count++
164
+ if (this.validateData('credit', { c, details: count })) return
165
+ }
166
+
167
+ return credits
168
+ },
169
+ setPageNotesAndCredits(data) {
170
+ const { notes, credits } = data
171
+ this.notes = this.getPageNotes(notes)
172
+ this.credits = this.getPageCredits(credits)
173
+ },
174
+ /**
175
+ * @description method to validate that the data for the note and credit.
176
+ * @param {String} ctx context of the validation : credit | note.
177
+ * @param {String| Object} data the data to be validate- Object type for note and String type for credit
178
+ * @return {boolean}
179
+ */
180
+ validateData(ctx, data) {
181
+ let errConsole = []
182
+ if (this.errorData.length) this.errorData = [] //reset the error tracker
183
+
184
+ switch (ctx) {
185
+ case 'note': {
186
+ if (data.constructor !== Object) {
187
+ this.errorData.push(
188
+ "Mauvais d'éclaration de la note. Doit être de type <i>Objet</i>"
189
+ )
190
+ throw Error(
191
+ `Unexpected definition the note. Expecting Object but received ${typeof data}`
192
+ )
193
+ }
194
+
195
+ let stringType = ['id', 'text'] //expected attribute in the note declaration
196
+
197
+ let { errorInConsole, errorList } = validateObjType(
198
+ data.n,
199
+ {
200
+ stringType
201
+ },
202
+ null,
203
+ `Note ${data.details}`
204
+ )
205
+
206
+ if (errorInConsole.length) errConsole.push(...errorInConsole)
207
+ if (errorList.length) this.errorData.push(...errorList)
208
+ break
209
+ }
210
+ case 'credit':
211
+ if (data.c.constructor !== String) {
212
+ this.errorData.push(
213
+ `Mauvaise déclaration du <b>crédit ${data.details}</b>. Doit être de type <i>String</i>`
214
+ )
215
+ console.warn(
216
+ `\n 💥 Invalid type declaration for 👉\x1B[1mcredit ${data.details} 👈\x1B[0m. Expecting type String \uD83D\uDEABtype ${typeof data.c}`
217
+ )
218
+ }
219
+ }
220
+ if (errConsole.length) {
221
+ errConsole.forEach((err) => console.warn(err))
222
+ }
223
+ return this.errorData.length
224
+ },
225
+ onNoteToShow(el) {
226
+ this.showWidget = true
227
+ this.updateWidgetOpen(true)
228
+
229
+ if (el) {
230
+ let ref = el.getAttribute('data-Ref')
231
+
232
+ const reg1 = /.*(P\d{2}(_E\d{2})?).*/gm //reg for the content of data-ref
233
+ const id = `$1`
234
+ const pageRef = ref.replace(reg1, id) //replace all to page id
235
+
236
+ if (this.sideBarIsOpen && pageRef.length == 3)
237
+ this.$bus.$emit('close-sidebar', 'ctxBranching') //send close signal if sibebar is open and note to focus is in parent page
238
+
239
+ this.removehighlight()
240
+
241
+ let elAtt = document.getElementsByClassName('widget-note')
242
+ let arr = Array.from(elAtt)
243
+
244
+ for (let element of arr) {
245
+ if (ref == element.getAttribute('id')) {
246
+ element.setAttribute('datacurrent', true)
247
+ element.focus()
248
+ }
249
+ }
250
+ }
251
+ },
252
+
253
+ onToggleWidget(data) {
254
+ if (data.type !== 'noteCredit') return (this.showWidget = false)
255
+ const { notes, credits } = data.content
256
+
257
+ this.setPageNotesAndCredits({ notes, credits })
258
+ this.showWidget = !this.showWidget
259
+ this.updateWidgetOpen(this.showWidget) //update data in store
260
+ },
261
+ close() {
262
+ let elNote = document.getElementsByClassName('widget-note')
263
+ let elCall = document.getElementsByClassName('callEndNote')
264
+
265
+ let arrNote = Array.from(elNote)
266
+ let arrCall = Array.from(elCall)
267
+
268
+ for (let element of arrNote) {
269
+ element.setAttribute('datacurrent', false)
270
+ }
271
+
272
+ for (let element of arrCall) {
273
+ element.setAttribute('datacurrentnote', false)
274
+ }
275
+ this.showWidget = false
276
+ this.updateWidgetOpen(false)
277
+ },
278
+ removehighlight() {
279
+ let elAtt = document.querySelectorAll("[datacurrent='true']")
280
+
281
+ let arr = Array.from(elAtt)
282
+
283
+ for (let element of arr) {
284
+ element.setAttribute('datacurrent', false)
285
+ }
286
+ },
287
+
288
+ focusNote(el, page) {
289
+ this.remove()
290
+ const DOMPages = document.getElementsByClassName(`app-page`)
291
+ const targetPage = DOMPages.namedItem(page) //get the desired page
292
+
293
+ let e = document.getElementsByClassName('callEndNote')
294
+
295
+ if (!targetPage) return
296
+ let nts = targetPage.getElementsByClassName('callEndNote')
297
+
298
+ let t = el.getAttribute('note-ref')
299
+ let s = document.querySelectorAll(`[data-ref="${t}"]`)
300
+
301
+ if (s && s[0]) s[0].setAttribute('datacurrent', true)
302
+
303
+ for (let element of e) {
304
+ element.setAttribute('datacurrentnote', false) //reset the data attribute of all notes
305
+ }
306
+ let targetNote = nts.namedItem(t)
307
+
308
+ if (targetNote) {
309
+ targetNote.setAttribute('datacurrentnote', true)
310
+
311
+ targetNote.childNodes[0].focus()
312
+ } else {
313
+ el.setAttribute('aria-disabled', 'true')
314
+ el.setAttribute('disabled', 'true')
315
+ el.querySelector('.btref-content').setAttribute(
316
+ 'data-content',
317
+ '\u26A0\uFE0F'
318
+ )
319
+ el.querySelector('div').style =
320
+ 'color:red; text-decoration:line-through;'
321
+ el.style.color = 'red'
322
+
323
+ el.removeEventListener('click', this.handleNoteDisplay)
324
+ }
325
+ this.prevNote = el
326
+ },
327
+
328
+ remove() {
329
+ let e = document.getElementsByClassName('widget-note')
330
+ for (let element of e) {
331
+ element.setAttribute('datacurrent', false)
332
+ }
333
+ },
334
+
335
+ onSidebarVisible(state) {
336
+ this.sideBarIsOpen = state
337
+ },
338
+
339
+ /**
340
+ * @description method handle the behavior of note displaying
341
+ * @param {Object} evt - the event that trigger the action
342
+ * @emits event - 'open-sidebar' to display notes in branch | 'close-sidebar' to display not in parent when branch open
343
+ *
344
+ */
345
+ handleNoteDisplay(ref) {
346
+ /*
347
+ * Note:
348
+ * Element target (button) is wrongly referenced when Screen-reader is active and element selected With TAB
349
+ * The Div in the button is selected not the button.
350
+ * This ensure that the button is always set as targeted element
351
+ */
352
+ const widgetContent = document.getElementsByClassName('backToNoteRef') //target all the button of in the notes widget
353
+ const elTarget = widgetContent.namedItem(ref) // Target current button
354
+ const pageRef = elTarget.getAttribute('pageref')
355
+
356
+ if (!this.sideBarIsOpen) this.prevNote = null //reset previous when sidebar is opened
357
+
358
+ let prevPageref = this.prevNote
359
+ ? this.prevNote.getAttribute('pageref')
360
+ : null
361
+
362
+ switch (true) {
363
+ case pageRef && pageRef.length > 3: {
364
+ //this is the branching page Send message to open the side bar
365
+ this.$bus.$emit('open-sidebar', {
366
+ ctx: 'ctxBranching',
367
+ e: pageRef,
368
+ persist: this.sideBarIsOpen || pageRef == prevPageref //this will prevent the sidebar to close when the note is in same page
369
+ })
370
+ //Should set focus after side bar is opened. Need to delay to not interfere with side bar focus
371
+ pageRef == prevPageref
372
+ ? setTimeout(() => this.focusNote(elTarget, pageRef), 100)
373
+ : setTimeout(() => this.focusNote(elTarget, pageRef), 400)
374
+
375
+ break
376
+ }
377
+ default:
378
+ //Should have the side bar close if it is opened and note is in parent page
379
+ if (this.sideBarIsOpen && pageRef.length == 3)
380
+ this.$bus.$emit('close-sidebar', 'ctxBranching')
381
+
382
+ return setTimeout(() => this.focusNote(elTarget, pageRef), 100) //Need to delay to not have side bar close animation finished
383
+ }
384
+
385
+ this.prevNote = elTarget
386
+ }
387
+ }
388
+ }
389
+ </script>
390
+ <style lang="scss">
391
+ #noteCredit {
392
+ display: none;
393
+ pointer-events: none;
394
+ position: absolute;
395
+ left: 75px;
396
+ top: 0;
397
+ width: 419px;
398
+ transition: left 0.5s ease-in-out;
399
+
400
+ &.openW {
401
+ pointer-events: all;
402
+ display: block;
403
+ opacity: 1;
404
+ }
405
+
406
+ #close-nc {
407
+ padding: 11px;
408
+ position: absolute;
409
+ right: 24px;
410
+ margin-bottom: 0;
411
+
412
+ &:focus {
413
+ box-shadow: inherit !important;
414
+ }
415
+ }
416
+
417
+ // .note-txt {
418
+ // pointer-events: none;
419
+ // }
420
+
421
+ .box-nc {
422
+ margin-top: 54px;
423
+ overflow-y: auto;
424
+ min-height: 160px;
425
+ max-height: 321px;
426
+ padding-right: 26px;
427
+
428
+ .t-note {
429
+ margin-bottom: 8px;
430
+ }
431
+
432
+ #notes-list {
433
+ .note-item {
434
+ display: flex;
435
+ flex-direction: row;
436
+ flex-wrap: wrap;
437
+ margin-bottom: 5px;
438
+
439
+ color: #deeff8;
440
+
441
+ span {
442
+ font-size: 0.65rem;
443
+ padding: 8px 0 0 0;
444
+ }
445
+
446
+ .backToNoteRef {
447
+ width: 90%;
448
+ margin-left: 5px;
449
+ padding-left: 5px;
450
+
451
+ .btref-content {
452
+ text-align: left;
453
+ }
454
+ }
455
+ }
456
+ }
457
+ }
458
+
459
+ #credits-list {
460
+ list-style-type: none;
461
+ padding: 0;
462
+ margin: 0;
463
+ }
464
+ #notes-list {
465
+ padding: 0;
466
+ margin: 0;
467
+
468
+ .widget-note {
469
+ padding: 5px 0;
470
+
471
+ &.note-error {
472
+ background-color: firebrick;
473
+ }
474
+
475
+ .btref-content {
476
+ pointer-events: none;
477
+ }
478
+ }
479
+ }
480
+ .overlay-w {
481
+ width: 100%;
482
+ height: 100%;
483
+ }
484
+ }
485
+
486
+ .app-nav {
487
+ &.show,
488
+ &:hover {
489
+ .ctn-w {
490
+ #noteCredit {
491
+ left: 120px;
492
+ }
493
+ }
494
+ }
495
+ }
496
+ </style>