@cnamts/synapse 0.0.0-alpha.0

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 (130) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +2 -0
  3. package/dist/design-system-v3.d.ts +246 -0
  4. package/dist/design-system-v3.js +5425 -0
  5. package/dist/design-system-v3.umd.cjs +2 -0
  6. package/dist/style.css +1 -0
  7. package/package.json +104 -0
  8. package/src/assets/tokens.scss +500 -0
  9. package/src/components/Alert/Alert.mdx +36 -0
  10. package/src/components/Alert/Alert.stories.ts +115 -0
  11. package/src/components/Alert/Alert.vue +248 -0
  12. package/src/components/Alert/locales.ts +3 -0
  13. package/src/components/Alert/tests/Alert.spec.ts +105 -0
  14. package/src/components/Alert/tests/__snapshots__/Alert.spec.ts.snap +15 -0
  15. package/src/components/BackBtn/BackBtn.mdx +26 -0
  16. package/src/components/BackBtn/BackBtn.stories.ts +138 -0
  17. package/src/components/BackBtn/BackBtn.vue +60 -0
  18. package/src/components/BackBtn/locales.ts +3 -0
  19. package/src/components/BackBtn/tests/BackBtn.spec.ts +103 -0
  20. package/src/components/BackBtn/tests/__snapshots__/BackBtn.spec.ts.snap +9 -0
  21. package/src/components/BackToTopBtn/BackToTopBtn.mdx +52 -0
  22. package/src/components/BackToTopBtn/BackToTopBtn.stories.ts +188 -0
  23. package/src/components/BackToTopBtn/BackToTopBtn.vue +137 -0
  24. package/src/components/BackToTopBtn/config.ts +12 -0
  25. package/src/components/BackToTopBtn/locales.ts +3 -0
  26. package/src/components/BackToTopBtn/tests/BackToTopBtn.spec.ts +173 -0
  27. package/src/components/BackToTopBtn/tests/__snapshots__/BackToTopBtn.spec.ts.snap +17 -0
  28. package/src/components/Beta/beta.mdx +5 -0
  29. package/src/components/CopyBtn/CopyBtn.mdx +38 -0
  30. package/src/components/CopyBtn/CopyBtn.stories.ts +209 -0
  31. package/src/components/CopyBtn/CopyBtn.vue +103 -0
  32. package/src/components/CopyBtn/config.ts +17 -0
  33. package/src/components/CopyBtn/locales.ts +3 -0
  34. package/src/components/CopyBtn/tests/CopyBtn.spec.ts +99 -0
  35. package/src/components/CopyBtn/tests/__snapshots__/CopyBtn.spec.ts.snap +7 -0
  36. package/src/components/Deprecated/deprecated.mdx +5 -0
  37. package/src/components/DownloadBtn/DownloadBtn.mdx +94 -0
  38. package/src/components/DownloadBtn/DownloadBtn.stories.ts +211 -0
  39. package/src/components/DownloadBtn/DownloadBtn.vue +113 -0
  40. package/src/components/DownloadBtn/config.ts +13 -0
  41. package/src/components/DownloadBtn/tests/DownloadBtn.spec.ts +82 -0
  42. package/src/components/DownloadBtn/tests/__snapshots__/DownloadBtn.spec.ts.snap +17 -0
  43. package/src/components/DownloadBtn/tests/data/filePromise.ts +53 -0
  44. package/src/components/DownloadBtn/tests/data/test.json +0 -0
  45. package/src/components/FranceConnectBtn/FranceConnectBtn.mdx +34 -0
  46. package/src/components/FranceConnectBtn/FranceConnectBtn.stories.ts +92 -0
  47. package/src/components/FranceConnectBtn/FranceConnectBtn.vue +154 -0
  48. package/src/components/FranceConnectBtn/locales.ts +6 -0
  49. package/src/components/FranceConnectBtn/tests/FranceConnectBtn.spec.ts +62 -0
  50. package/src/components/FranceConnectBtn/tests/__snapshots__/FranceConnectBtn.spec.ts.snap +36 -0
  51. package/src/components/LangBtn/LangBtn.mdx +37 -0
  52. package/src/components/LangBtn/LangBtn.stories.ts +147 -0
  53. package/src/components/LangBtn/LangBtn.vue +167 -0
  54. package/src/components/LangBtn/config.ts +17 -0
  55. package/src/components/LangBtn/locales.ts +3 -0
  56. package/src/components/LangBtn/tests/Config.spec.ts +24 -0
  57. package/src/components/LangBtn/tests/LangBtn.spec.ts +283 -0
  58. package/src/components/LangBtn/tests/__snapshots__/LangBtn.spec.ts.snap +11 -0
  59. package/src/components/LangBtn/types.d.ts +7 -0
  60. package/src/components/NotificationBar/NotificationBar.mdx +94 -0
  61. package/src/components/NotificationBar/NotificationBar.stories.ts +366 -0
  62. package/src/components/NotificationBar/NotificationBar.vue +296 -0
  63. package/src/components/NotificationBar/options.ts +15 -0
  64. package/src/components/NotificationBar/tests/NotificationBar.spec.ts +332 -0
  65. package/src/components/NotificationBar/tests/__snapshots__/NotificationBar.spec.ts.snap +7 -0
  66. package/src/components/NotificationBar/types.ts +7 -0
  67. package/src/components/PageContainer/PageContainer.mdx +29 -0
  68. package/src/components/PageContainer/PageContainer.stories.ts +115 -0
  69. package/src/components/PageContainer/PageContainer.vue +68 -0
  70. package/src/components/PageContainer/tests/PageContainer.spec.ts +56 -0
  71. package/src/components/PageContainer/tests/__snapshots__/PageContainer.spec.ts.snap +7 -0
  72. package/src/components/SkipLink/SkipLink.mdx +55 -0
  73. package/src/components/SkipLink/SkipLink.stories.ts +70 -0
  74. package/src/components/SkipLink/SkipLink.vue +79 -0
  75. package/src/components/SkipLink/locales.ts +3 -0
  76. package/src/components/SkipLink/tests/__snapshots__/skipLink.spec.ts.snap +3 -0
  77. package/src/components/SkipLink/tests/skipLink.spec.ts +46 -0
  78. package/src/components/index.ts +8 -0
  79. package/src/composables/useCustomizableOptions.ts +23 -0
  80. package/src/designTokens/bootstrapColors.md +66 -0
  81. package/src/designTokens/cnamColors.md +193 -0
  82. package/src/designTokens/index.ts +15 -0
  83. package/src/designTokens/tokens/bootstrap/bootstrapColors.ts +158 -0
  84. package/src/designTokens/tokens/bootstrap/bootstrapLightTheme.ts +22 -0
  85. package/src/designTokens/tokens/cnam/cnamColors.ts +171 -0
  86. package/src/designTokens/tokens/cnam/cnamContextual.ts +58 -0
  87. package/src/designTokens/tokens/cnam/cnamLightTheme.ts +90 -0
  88. package/src/designTokens/tokens/cnam/cnamSemantic.ts +87 -0
  89. package/src/designTokens/tokens/json/contextual-tokens.json +156 -0
  90. package/src/designTokens/tokens/json/primitives.json +209 -0
  91. package/src/designTokens/tokens/json/semantic.json +120 -0
  92. package/src/designTokens/utils/convertGaps.ts +11 -0
  93. package/src/designTokens/utils/convertSemanticsToken.ts +32 -0
  94. package/src/designTokens/utils/createFlattenTheme.ts +19 -0
  95. package/src/designTokens/utils/index.ts +4 -0
  96. package/src/main.ts +2 -0
  97. package/src/services/NotificationService.ts +27 -0
  98. package/src/stories/Fondamentaux/Accessibilite/Accessibilite.mdx +52 -0
  99. package/src/stories/Fondamentaux/Accessibilite/Accessibilite.stories.ts +36 -0
  100. package/src/stories/Fondamentaux/Accessibilite/AccessibiliteItems.ts +706 -0
  101. package/src/stories/Fondamentaux/Accessibilite/constants/ExpertiseLevelEnum.ts +5 -0
  102. package/src/stories/Fondamentaux/Accessibilite/constants/RGAALevelEnum.ts +4 -0
  103. package/src/stories/Fondamentaux/EcoConception/EcoConception.mdx +24 -0
  104. package/src/stories/Fondamentaux/EcoConception/Econception.stories.ts +30 -0
  105. package/src/stories/Fondamentaux/EcoConception/ecoDesignItems.ts +55 -0
  106. package/src/stories/GuideDuDev/CommentContribuer.mdx +22 -0
  107. package/src/stories/GuideDuDev/components.stories.ts +23 -0
  108. package/src/stories/GuideDuDev/moduleDeNotification.mdx +182 -0
  109. package/src/stories/GuideDuDev/vuetifyOptions.mdx +72 -0
  110. package/src/stories/Guidelines/Colors.mdx +220 -0
  111. package/src/stories/Guidelines/CustomisationEtThemes.mdx +3 -0
  112. package/src/stories/Guidelines/Introduction.mdx +35 -0
  113. package/src/stories/Guidelines/Typo.mdx +53 -0
  114. package/src/stories/Home/Accueil.mdx +7 -0
  115. package/src/stories/Home/PolitiqueDeConfidentialite.mdx +4 -0
  116. package/src/stories/Home/synapse.webp +0 -0
  117. package/src/temp/TestA11y.vue +14 -0
  118. package/src/temp/TestComponent.vue +37 -0
  119. package/src/temp/TestDTComponent.vue +93 -0
  120. package/src/temp/customizableOptions.vue +18 -0
  121. package/src/temp/gridsTests.vue +54 -0
  122. package/src/temp/options.json +5 -0
  123. package/src/types/vuetifyTypes.ts +3 -0
  124. package/src/utils/convertToUnit/index.ts +16 -0
  125. package/src/utils/convertToUnit/test/convertToUnit.spec.ts +32 -0
  126. package/src/utils/functions/copyToClipboard/index.ts +38 -0
  127. package/src/utils/functions/copyToClipboard/tests/copyToClipboard.spec.ts +104 -0
  128. package/src/utils/functions/downloadFile/index.ts +37 -0
  129. package/src/utils/functions/downloadFile/tests/downloadFile.spec.ts +69 -0
  130. package/src/utils/functions/downloadFile/types.ts +1 -0
@@ -0,0 +1,366 @@
1
+ import type { Meta, StoryFn } from '@storybook/vue3'
2
+ import NotificationBar from './NotificationBar.vue'
3
+ import { VBtn } from 'vuetify/components'
4
+ import { ref, toRefs, watch } from 'vue'
5
+ import { useNotificationService } from '@/services/NotificationService'
6
+ import type { Notification } from '@/components/NotificationBar/types'
7
+
8
+ interface StoryArgs {
9
+ closeBtnText: string
10
+ type: Notification['type']
11
+ bottom: boolean
12
+ rounded: string
13
+ }
14
+
15
+ const meta: Meta<typeof NotificationBar> = {
16
+ title: 'Components/NotificationBar',
17
+ parameters: {
18
+ layout: 'fullscreen',
19
+ },
20
+ argTypes: {
21
+ closeBtnText: { control: 'text', type: 'string' },
22
+ bottom: { control: 'boolean', type: 'boolean' },
23
+ rounded: { control: 'text', type: 'string' },
24
+ },
25
+ }
26
+
27
+ export default meta
28
+
29
+ type Story = StoryFn<StoryArgs>
30
+
31
+ const Template: Story = args => ({
32
+ components: { NotificationBar, VBtn },
33
+ setup() {
34
+ const { addNotification } = useNotificationService()
35
+ const showNotification = ref(false)
36
+ const { closeBtnText, bottom, rounded } = toRefs(args)
37
+ const typeRef = ref(args.type)
38
+
39
+ watch(
40
+ () => args,
41
+ (newArgs) => {
42
+ typeRef.value = newArgs.type
43
+ closeBtnText.value = newArgs.closeBtnText
44
+ bottom.value = newArgs.bottom
45
+ rounded.value = newArgs.rounded
46
+ },
47
+ { deep: true },
48
+ )
49
+
50
+ const envoyerNotification = (message: string) => {
51
+ const notification: Notification = {
52
+ id: Date.now().toString(),
53
+ message,
54
+ type: typeRef.value,
55
+ timeout: -1,
56
+ }
57
+ addNotification(notification)
58
+ showNotification.value = true
59
+ }
60
+
61
+ return {
62
+ showNotification,
63
+ typeRef,
64
+ closeBtnText,
65
+ bottom,
66
+ rounded,
67
+ envoyerNotification,
68
+ }
69
+ },
70
+ template: `
71
+ <div class="d-flex flex-wrap align-center justify-center">
72
+ <NotificationBar
73
+ v-model="showNotification"
74
+ :close-btn-text="closeBtnText"
75
+ :bottom="bottom"
76
+ :rounded="rounded"
77
+ >
78
+ <template #default>This is a {{ typeRef }} notification</template>
79
+ </NotificationBar>
80
+ <VBtn
81
+ color="primary"
82
+ @click="envoyerNotification('This is a ' + typeRef + ' notification')"
83
+ class="ma-6"
84
+ >
85
+ Afficher la notification
86
+ </VBtn>
87
+ </div>
88
+ `,
89
+ })
90
+
91
+ export const Success = Template.bind({})
92
+ Success.args = { type: 'success', closeBtnText: 'Fermer', bottom: false, rounded: 'lg' }
93
+ export const Info = Template.bind({})
94
+ Info.args = { type: 'info', closeBtnText: 'Fermer', bottom: false, rounded: 'lg' }
95
+ export const Warning = Template.bind({})
96
+ Warning.args = { type: 'warning', closeBtnText: 'Fermer', bottom: false, rounded: 'lg' }
97
+ export const Error = Template.bind({})
98
+ Error.args = { type: 'error', closeBtnText: 'Fermer', bottom: false, rounded: 'lg' }
99
+
100
+ export const WithAction: Story = args => ({
101
+ components: { NotificationBar, VBtn },
102
+ setup() {
103
+ const { addNotification } = useNotificationService()
104
+ const showNotification = ref(false)
105
+ const { type: typeRef, closeBtnText } = toRefs(args)
106
+
107
+ const envoyerNotification = (message: string) => {
108
+ const notification: Notification = {
109
+ id: Date.now().toString(),
110
+ message,
111
+ type: typeRef.value,
112
+ timeout: -1,
113
+ }
114
+ addNotification(notification)
115
+ showNotification.value = true
116
+ }
117
+
118
+ return {
119
+ showNotification,
120
+ typeRef,
121
+ closeBtnText,
122
+ envoyerNotification,
123
+ }
124
+ },
125
+ template: `
126
+ <div class="d-flex flex-wrap align-center justify-center">
127
+ <NotificationBar
128
+ v-model="showNotification"
129
+ :close-btn-text="closeBtnText"
130
+ >
131
+ <template #default>This is a {{ typeRef }} notification</template>
132
+ <template #action>
133
+ <VBtn variant="outlined">Action</VBtn>
134
+ </template>
135
+ </NotificationBar>
136
+ <VBtn
137
+ color="primary"
138
+ @click="envoyerNotification('This is a ' + typeRef + ' notification')"
139
+ class="ma-6"
140
+ >
141
+ Afficher la notification
142
+ </VBtn>
143
+ </div>
144
+ `,
145
+ })
146
+ WithAction.args = {
147
+ type: 'info',
148
+ closeBtnText: 'Fermer',
149
+ }
150
+
151
+ export const WithTimeout: Story = args => ({
152
+ components: { NotificationBar, VBtn },
153
+ setup() {
154
+ const { addNotification } = useNotificationService()
155
+ const showNotification = ref(false)
156
+
157
+ // Ajouter une valeur par défaut si `args.type` n'est pas défini
158
+ const typeRef = ref(args.type || 'info')
159
+ const { closeBtnText } = toRefs(args)
160
+
161
+ const envoyerNotification = (message: string) => {
162
+ const notification: Notification = {
163
+ id: Date.now().toString(),
164
+ message,
165
+ type: typeRef.value,
166
+ timeout: 5000, // Timeout de 5 secondes
167
+ }
168
+ addNotification(notification)
169
+ showNotification.value = true
170
+ }
171
+
172
+ return {
173
+ showNotification,
174
+ typeRef, // Utilisation de `typeRef`
175
+ closeBtnText,
176
+ envoyerNotification,
177
+ }
178
+ },
179
+ template: `
180
+ <div class="d-flex flex-wrap align-center justify-center">
181
+ <NotificationBar
182
+ v-model="showNotification"
183
+ :close-btn-text="closeBtnText"
184
+ >
185
+ <template #default>This is a {{ typeRef }} notification</template>
186
+ </NotificationBar>
187
+ <VBtn
188
+ color="primary"
189
+ @click="envoyerNotification('This is a ' + typeRef + ' notification')"
190
+ class="ma-6"
191
+ >
192
+ Afficher la notification
193
+ </VBtn>
194
+ </div>
195
+ `,
196
+ })
197
+ WithTimeout.args = {
198
+ type: 'info', // Valeur par défaut pour `type`
199
+ closeBtnText: 'Fermer',
200
+ }
201
+
202
+ export const WithLongText: Story = args => ({
203
+ components: { NotificationBar, VBtn },
204
+ setup() {
205
+ const { addNotification } = useNotificationService()
206
+ const showNotification = ref(false)
207
+
208
+ // Ajouter une valeur par défaut pour `type`
209
+ const typeRef = ref(args.type || 'info')
210
+ const { closeBtnText } = toRefs(args)
211
+
212
+ const envoyerNotification = (message: string) => {
213
+ const notification: Notification = {
214
+ id: Date.now().toString(),
215
+ message,
216
+ type: typeRef.value,
217
+ timeout: -1, // Notification persistante
218
+ }
219
+ addNotification(notification)
220
+ showNotification.value = true
221
+ }
222
+
223
+ return {
224
+ showNotification,
225
+ typeRef, // Utilisation de `typeRef`
226
+ closeBtnText,
227
+ envoyerNotification,
228
+ }
229
+ },
230
+ template: `
231
+ <div class="d-flex flex-wrap align-center justify-center">
232
+ <NotificationBar
233
+ v-model="showNotification"
234
+ :close-btn-text="closeBtnText"
235
+ >
236
+ <template #default>
237
+ <div>
238
+ <p>This is a {{ typeRef }} notification</p>
239
+ <p>Lorem ipsum dolor sit amet, consectetur adipiscing elit...</p>
240
+ </div>
241
+ </template>
242
+ </NotificationBar>
243
+ <VBtn
244
+ color="primary"
245
+ @click="envoyerNotification('This is a ' + typeRef + ' lorem ipsum dolor sit amet, consectetur adipiscing elit...')"
246
+ class="ma-6"
247
+ >
248
+ Afficher la notification
249
+ </VBtn>
250
+ </div>
251
+ `,
252
+ })
253
+ WithLongText.args = {
254
+ type: 'info',
255
+ closeBtnText: 'Fermer',
256
+ }
257
+
258
+ export const forMobile: Story = args => ({
259
+ components: { NotificationBar, VBtn },
260
+ setup() {
261
+ const { addNotification } = useNotificationService()
262
+ const showNotification = ref(false)
263
+
264
+ const typeRef = ref(args.type || 'info')
265
+ const { closeBtnText } = toRefs(args)
266
+
267
+ const envoyerNotification = (message: string) => {
268
+ const notification: Notification = {
269
+ id: Date.now().toString(),
270
+ message,
271
+ type: typeRef.value,
272
+ timeout: -1,
273
+ }
274
+ addNotification(notification)
275
+ showNotification.value = true
276
+ }
277
+
278
+ return {
279
+ showNotification,
280
+ typeRef,
281
+ closeBtnText,
282
+ envoyerNotification,
283
+ }
284
+ },
285
+ template: `
286
+ <div class="d-flex flex-wrap align-center justify-center">
287
+ <NotificationBar
288
+ v-model="showNotification"
289
+ :close-btn-text="closeBtnText"
290
+ >
291
+ <template #default>This is a {{ typeRef }} notification</template>
292
+ </NotificationBar>
293
+ <VBtn
294
+ color="primary"
295
+ @click="envoyerNotification('This is a ' + typeRef + ' notification ')"
296
+ class="ma-6"
297
+ >
298
+ Afficher la notification
299
+ </VBtn>
300
+ </div>
301
+ `,
302
+ })
303
+ forMobile.args = {
304
+ type: 'info',
305
+ closeBtnText: 'Fermer',
306
+ }
307
+ forMobile.parameters = {
308
+ viewport: {
309
+ defaultViewport: 'mobile2',
310
+ },
311
+ }
312
+
313
+ export const forMobileWithLongText: Story = args => ({
314
+ components: { NotificationBar, VBtn },
315
+ setup() {
316
+ const { addNotification } = useNotificationService()
317
+ const showNotification = ref(false)
318
+
319
+ const typeRef = ref(args.type || 'info')
320
+ const { closeBtnText } = toRefs(args)
321
+
322
+ const envoyerNotification = (message: string) => {
323
+ const notification: Notification = {
324
+ id: Date.now().toString(),
325
+ message,
326
+ type: typeRef.value,
327
+ timeout: -1,
328
+ }
329
+ addNotification(notification)
330
+ showNotification.value = true
331
+ }
332
+
333
+ return {
334
+ showNotification,
335
+ typeRef,
336
+ closeBtnText,
337
+ envoyerNotification,
338
+ }
339
+ },
340
+ template: `
341
+ <div class="d-flex flex-wrap align-center justify-center">
342
+ <NotificationBar
343
+ v-model="showNotification"
344
+ :close-btn-text="closeBtnText"
345
+ >
346
+ <template #default>This is a {{ typeRef }} notification</template>
347
+ </NotificationBar>
348
+ <VBtn
349
+ color="primary"
350
+ @click="envoyerNotification('This is a ' + typeRef + ' lorem ipsum dolor sit amet, consectetur adipiscing elit...')"
351
+ class="ma-6"
352
+ >
353
+ Afficher la notification
354
+ </VBtn>
355
+ </div>
356
+ `,
357
+ })
358
+ forMobileWithLongText.args = {
359
+ type: 'info',
360
+ closeBtnText: 'Fermer',
361
+ }
362
+ forMobileWithLongText.parameters = {
363
+ viewport: {
364
+ defaultViewport: 'mobile2',
365
+ },
366
+ }
@@ -0,0 +1,296 @@
1
+ <script setup lang="ts">
2
+ import { ref, computed, getCurrentInstance, watch } from 'vue'
3
+ import { mdiClose, mdiInformationOutline, mdiCheckCircleOutline, mdiAlertCircleOutline, mdiAlertOctagonOutline } from '@mdi/js'
4
+ import { useDisplay } from 'vuetify'
5
+ import { useNotificationService } from '@/services/NotificationService'
6
+ import { type Notification } from './types'
7
+ import { VSnackbar, VIcon, VBtn } from 'vuetify/components'
8
+
9
+ import useCustomizableOptions, { type CustomizableOptions } from '@/composables/useCustomizableOptions'
10
+ import defaultOptions from './options'
11
+
12
+ const props = withDefaults(defineProps<CustomizableOptions & {
13
+ closeBtnText?: string
14
+ rounded?: 0 | 1 | 2 | 3 | 4 | 'xs' | 'sm' | true | 'lg' | 'xl' | 'pill' | 'circle' | 'shaped'
15
+ bottom?: true | false
16
+ }>(), {
17
+ closeBtnText: 'Fermer',
18
+ rounded: 4,
19
+ bottom: false,
20
+ })
21
+
22
+ const options = useCustomizableOptions(defaultOptions, props)
23
+
24
+ const display = useDisplay()
25
+
26
+ const { notificationQueue, removeNotification } = useNotificationService()
27
+
28
+ const instance = getCurrentInstance()
29
+
30
+ const currentNotification = ref<Notification>()
31
+ const isNotificationVisible = ref(false)
32
+
33
+ const isProcessingNotifications = ref(false) // Nouvelle variable pour suivre le traitement
34
+
35
+ const hasActionSlot = computed(() => !!instance?.slots.action)
36
+ const isMobileVersion = computed(() => display.name.value === 'xs')
37
+ const isTabletVersion = computed(() => display.name.value === 'sm')
38
+ const hasLongContent = computed(() => (currentNotification.value?.message?.length ?? 0) > 50)
39
+
40
+ const iconMapping: Record<string, string> = {
41
+ info: mdiInformationOutline,
42
+ success: mdiCheckCircleOutline,
43
+ warning: mdiAlertCircleOutline,
44
+ error: mdiAlertOctagonOutline,
45
+ }
46
+
47
+ const icon = computed(() => {
48
+ return currentNotification.value ? iconMapping[currentNotification.value.type] : null
49
+ })
50
+
51
+ const color = computed(() => {
52
+ if (currentNotification.value) {
53
+ const typeColor: Record<string, string> = {
54
+ info: 'info',
55
+ success: 'success',
56
+ warning: 'warning',
57
+ error: 'error',
58
+ }
59
+ return typeColor[currentNotification.value.type] || 'info'
60
+ }
61
+ return 'info'
62
+ })
63
+
64
+ const contentStyle = computed(() => {
65
+ if (currentNotification.value) {
66
+ const isDarkText = currentNotification.value.type === 'success' || currentNotification.value.type === 'warning'
67
+ return {
68
+ contentColor: isDarkText ? 'grey-darken-80' : 'white',
69
+ }
70
+ }
71
+ return {
72
+ contentColor: 'white',
73
+ }
74
+ })
75
+
76
+ const smallCloseBtn = computed(() => isMobileVersion.value && !hasLongContent.value && !hasActionSlot.value)
77
+
78
+ const setNotification = (notification: Notification) => {
79
+ currentNotification.value = { ...notification }
80
+ }
81
+
82
+ const processNotificationQueue = async () => {
83
+ isProcessingNotifications.value = true
84
+
85
+ while (notificationQueue.value.length > 0) {
86
+ const nextNotification = notificationQueue.value[0]
87
+ setNotification(nextNotification)
88
+ isNotificationVisible.value = true
89
+
90
+ let timeout = nextNotification.timeout ?? -1
91
+
92
+ if (timeout <= 0) {
93
+ // Attend que la notification soit fermée manuellement car pas de timeout
94
+ await new Promise<void>((resolve) => {
95
+ const stopWatch = watch(isNotificationVisible, (visible) => {
96
+ if (!visible) {
97
+ stopWatch()
98
+ resolve()
99
+ }
100
+ })
101
+ })
102
+ }
103
+ else {
104
+ // Attend la fin du délai du timeout avant de fermer la notification automatiquement
105
+ await new Promise<void>((resolve) => {
106
+ const timeoutId = setTimeout(() => {
107
+ handleClearNotification()
108
+ resolve()
109
+ }, timeout)
110
+
111
+ const stopWatch = watch(isNotificationVisible, (visible) => {
112
+ if (!visible) {
113
+ clearTimeout(timeoutId)
114
+ stopWatch()
115
+ resolve()
116
+ }
117
+ })
118
+ })
119
+ }
120
+
121
+ // Retire la notification de la file
122
+ removeNotification(nextNotification.id)
123
+ }
124
+
125
+ isProcessingNotifications.value = false
126
+ }
127
+
128
+ const handleClearNotification = () => {
129
+ isNotificationVisible.value = false
130
+ if (currentNotification.value) {
131
+ removeNotification(currentNotification.value.id)
132
+ currentNotification.value = undefined
133
+ }
134
+ }
135
+
136
+ const openNotification = (notification: Notification) => {
137
+ setNotification(notification)
138
+ isNotificationVisible.value = true
139
+ }
140
+
141
+ const showNextNotification = () => {
142
+ if (notificationQueue.value.length > 0) {
143
+ const nextNotification = notificationQueue.value[0]
144
+ setNotification(nextNotification)
145
+ isNotificationVisible.value = true
146
+ }
147
+ }
148
+
149
+ watch(
150
+ () => notificationQueue.value.length,
151
+ (newLength) => {
152
+ if (newLength > 0 && !isProcessingNotifications.value) {
153
+ processNotificationQueue()
154
+ }
155
+ },
156
+ )
157
+
158
+ defineExpose({
159
+ openNotification,
160
+ handleClearNotification,
161
+ showNextNotification,
162
+ processNotificationQueue,
163
+ currentNotification,
164
+ isNotificationVisible,
165
+ hasActionSlot,
166
+ isMobileVersion,
167
+ hasLongContent,
168
+ color,
169
+ icon,
170
+ contentStyle,
171
+ smallCloseBtn,
172
+ isVertical: computed(() => hasLongContent.value && isMobileVersion.value),
173
+ })
174
+
175
+ </script>
176
+
177
+ <template>
178
+ <div v-if="currentNotification">
179
+ <VSnackbar
180
+ v-bind="options.snackbar"
181
+ v-model="isNotificationVisible"
182
+ role="status"
183
+ :color="color"
184
+ :location="props.bottom ? 'bottom' : 'top'"
185
+ :vertical="hasLongContent"
186
+ :multi-line="hasLongContent"
187
+ :timeout="currentNotification?.timeout ?? -1"
188
+ :width="isMobileVersion || isTabletVersion ? 'auto' : '960px'"
189
+ :rounded="props.rounded"
190
+ :class="[{ 'long-text': hasLongContent }]"
191
+ >
192
+ <div class="d-flex align-center ga-2">
193
+ <VIcon
194
+ v-if="!isMobileVersion && icon"
195
+ v-bind="options.icon"
196
+ :icon="icon"
197
+ size="24"
198
+ aria-hidden="true"
199
+ />
200
+ <p :class="'text-' + contentStyle.contentColor">
201
+ {{ currentNotification?.message }}
202
+ </p>
203
+ </div>
204
+
205
+ <template #actions>
206
+ <div
207
+ class="d-flex ga-2"
208
+ style="width:100%"
209
+ :class="hasLongContent ? 'action-section-longText' : 'action-section-shortText'"
210
+ >
211
+ <slot name="action" />
212
+ <VBtn
213
+ class="notification-bar__close"
214
+ :class="{ 'ma-0': smallCloseBtn }"
215
+ aria-label="Fermer la notification"
216
+ v-bind="options.btn"
217
+ @click="handleClearNotification"
218
+ >
219
+ <template v-if="!smallCloseBtn">
220
+ {{ closeBtnText }}
221
+ </template>
222
+ <template v-else>
223
+ <VIcon :icon="mdiClose" />
224
+ </template>
225
+ </VBtn>
226
+ </div>
227
+ </template>
228
+ </VSnackbar>
229
+ </div>
230
+ </template>
231
+
232
+ <style lang="scss" scoped>
233
+ @use '@/assets/tokens.scss';
234
+
235
+ .vd-notification-content {
236
+ display: flex;
237
+ align-items: center;
238
+ }
239
+
240
+ .vd-notification-bar :deep(.v-snack__wrapper) {
241
+ padding: 16px;
242
+ min-width: 0;
243
+ max-width: none;
244
+ }
245
+
246
+ :deep(.v-snackbar__content) {
247
+ padding: tokens.$padding-4 !important;
248
+ }
249
+ :deep(.v-snackbar__actions) {
250
+ margin-inline-end: 10px;
251
+ }
252
+
253
+ .vd-notification-bar.v-snackbar--vertical :deep() {
254
+ .v-snackbar--vertical .v-snackbar__wrapper .v-snackbar__actions {
255
+ width: 100% !important;
256
+ align-self: auto;
257
+ }
258
+ .v-snack__wrapper {
259
+ align-items: stretch;
260
+ flex-direction: row;
261
+ }
262
+
263
+ .v-snack__action {
264
+ align-self: stretch;
265
+ align-items: stretch;
266
+ flex-direction: column;
267
+ }
268
+
269
+ .v-snackbar__content {
270
+ margin: 0;
271
+ width: 100%;
272
+ display: flex;
273
+ }
274
+
275
+ .vd-notification-content {
276
+ flex-direction: column;
277
+ display: flex;
278
+ }
279
+ }
280
+
281
+ .long-text :deep(.v-snackbar__actions) {
282
+ width: 98% !important;
283
+ }
284
+
285
+ .short-text :deep(.v-snackbar__actions) {
286
+ width: 48% !important;
287
+ }
288
+
289
+ .action-section-longText {
290
+ justify-content: space-around;
291
+ }
292
+
293
+ .action-section-shortText {
294
+ justify-content: end !important;
295
+ }
296
+ </style>
@@ -0,0 +1,15 @@
1
+ type VariantType = 'text' | 'flat' | 'elevated' | 'tonal' | 'outlined' | 'plain'
2
+
3
+ const defaultOptions = {
4
+ snackBar: {
5
+ timeout: -1,
6
+ },
7
+ icon: {
8
+ class: 'mr-2',
9
+ },
10
+ btn: {
11
+ variant: 'text' as VariantType,
12
+ },
13
+ }
14
+
15
+ export default defaultOptions