@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.
- package/LICENSE +21 -0
- package/README.md +2 -0
- package/dist/design-system-v3.d.ts +246 -0
- package/dist/design-system-v3.js +5425 -0
- package/dist/design-system-v3.umd.cjs +2 -0
- package/dist/style.css +1 -0
- package/package.json +104 -0
- package/src/assets/tokens.scss +500 -0
- package/src/components/Alert/Alert.mdx +36 -0
- package/src/components/Alert/Alert.stories.ts +115 -0
- package/src/components/Alert/Alert.vue +248 -0
- package/src/components/Alert/locales.ts +3 -0
- package/src/components/Alert/tests/Alert.spec.ts +105 -0
- package/src/components/Alert/tests/__snapshots__/Alert.spec.ts.snap +15 -0
- package/src/components/BackBtn/BackBtn.mdx +26 -0
- package/src/components/BackBtn/BackBtn.stories.ts +138 -0
- package/src/components/BackBtn/BackBtn.vue +60 -0
- package/src/components/BackBtn/locales.ts +3 -0
- package/src/components/BackBtn/tests/BackBtn.spec.ts +103 -0
- package/src/components/BackBtn/tests/__snapshots__/BackBtn.spec.ts.snap +9 -0
- package/src/components/BackToTopBtn/BackToTopBtn.mdx +52 -0
- package/src/components/BackToTopBtn/BackToTopBtn.stories.ts +188 -0
- package/src/components/BackToTopBtn/BackToTopBtn.vue +137 -0
- package/src/components/BackToTopBtn/config.ts +12 -0
- package/src/components/BackToTopBtn/locales.ts +3 -0
- package/src/components/BackToTopBtn/tests/BackToTopBtn.spec.ts +173 -0
- package/src/components/BackToTopBtn/tests/__snapshots__/BackToTopBtn.spec.ts.snap +17 -0
- package/src/components/Beta/beta.mdx +5 -0
- package/src/components/CopyBtn/CopyBtn.mdx +38 -0
- package/src/components/CopyBtn/CopyBtn.stories.ts +209 -0
- package/src/components/CopyBtn/CopyBtn.vue +103 -0
- package/src/components/CopyBtn/config.ts +17 -0
- package/src/components/CopyBtn/locales.ts +3 -0
- package/src/components/CopyBtn/tests/CopyBtn.spec.ts +99 -0
- package/src/components/CopyBtn/tests/__snapshots__/CopyBtn.spec.ts.snap +7 -0
- package/src/components/Deprecated/deprecated.mdx +5 -0
- package/src/components/DownloadBtn/DownloadBtn.mdx +94 -0
- package/src/components/DownloadBtn/DownloadBtn.stories.ts +211 -0
- package/src/components/DownloadBtn/DownloadBtn.vue +113 -0
- package/src/components/DownloadBtn/config.ts +13 -0
- package/src/components/DownloadBtn/tests/DownloadBtn.spec.ts +82 -0
- package/src/components/DownloadBtn/tests/__snapshots__/DownloadBtn.spec.ts.snap +17 -0
- package/src/components/DownloadBtn/tests/data/filePromise.ts +53 -0
- package/src/components/DownloadBtn/tests/data/test.json +0 -0
- package/src/components/FranceConnectBtn/FranceConnectBtn.mdx +34 -0
- package/src/components/FranceConnectBtn/FranceConnectBtn.stories.ts +92 -0
- package/src/components/FranceConnectBtn/FranceConnectBtn.vue +154 -0
- package/src/components/FranceConnectBtn/locales.ts +6 -0
- package/src/components/FranceConnectBtn/tests/FranceConnectBtn.spec.ts +62 -0
- package/src/components/FranceConnectBtn/tests/__snapshots__/FranceConnectBtn.spec.ts.snap +36 -0
- package/src/components/LangBtn/LangBtn.mdx +37 -0
- package/src/components/LangBtn/LangBtn.stories.ts +147 -0
- package/src/components/LangBtn/LangBtn.vue +167 -0
- package/src/components/LangBtn/config.ts +17 -0
- package/src/components/LangBtn/locales.ts +3 -0
- package/src/components/LangBtn/tests/Config.spec.ts +24 -0
- package/src/components/LangBtn/tests/LangBtn.spec.ts +283 -0
- package/src/components/LangBtn/tests/__snapshots__/LangBtn.spec.ts.snap +11 -0
- package/src/components/LangBtn/types.d.ts +7 -0
- package/src/components/NotificationBar/NotificationBar.mdx +94 -0
- package/src/components/NotificationBar/NotificationBar.stories.ts +366 -0
- package/src/components/NotificationBar/NotificationBar.vue +296 -0
- package/src/components/NotificationBar/options.ts +15 -0
- package/src/components/NotificationBar/tests/NotificationBar.spec.ts +332 -0
- package/src/components/NotificationBar/tests/__snapshots__/NotificationBar.spec.ts.snap +7 -0
- package/src/components/NotificationBar/types.ts +7 -0
- package/src/components/PageContainer/PageContainer.mdx +29 -0
- package/src/components/PageContainer/PageContainer.stories.ts +115 -0
- package/src/components/PageContainer/PageContainer.vue +68 -0
- package/src/components/PageContainer/tests/PageContainer.spec.ts +56 -0
- package/src/components/PageContainer/tests/__snapshots__/PageContainer.spec.ts.snap +7 -0
- package/src/components/SkipLink/SkipLink.mdx +55 -0
- package/src/components/SkipLink/SkipLink.stories.ts +70 -0
- package/src/components/SkipLink/SkipLink.vue +79 -0
- package/src/components/SkipLink/locales.ts +3 -0
- package/src/components/SkipLink/tests/__snapshots__/skipLink.spec.ts.snap +3 -0
- package/src/components/SkipLink/tests/skipLink.spec.ts +46 -0
- package/src/components/index.ts +8 -0
- package/src/composables/useCustomizableOptions.ts +23 -0
- package/src/designTokens/bootstrapColors.md +66 -0
- package/src/designTokens/cnamColors.md +193 -0
- package/src/designTokens/index.ts +15 -0
- package/src/designTokens/tokens/bootstrap/bootstrapColors.ts +158 -0
- package/src/designTokens/tokens/bootstrap/bootstrapLightTheme.ts +22 -0
- package/src/designTokens/tokens/cnam/cnamColors.ts +171 -0
- package/src/designTokens/tokens/cnam/cnamContextual.ts +58 -0
- package/src/designTokens/tokens/cnam/cnamLightTheme.ts +90 -0
- package/src/designTokens/tokens/cnam/cnamSemantic.ts +87 -0
- package/src/designTokens/tokens/json/contextual-tokens.json +156 -0
- package/src/designTokens/tokens/json/primitives.json +209 -0
- package/src/designTokens/tokens/json/semantic.json +120 -0
- package/src/designTokens/utils/convertGaps.ts +11 -0
- package/src/designTokens/utils/convertSemanticsToken.ts +32 -0
- package/src/designTokens/utils/createFlattenTheme.ts +19 -0
- package/src/designTokens/utils/index.ts +4 -0
- package/src/main.ts +2 -0
- package/src/services/NotificationService.ts +27 -0
- package/src/stories/Fondamentaux/Accessibilite/Accessibilite.mdx +52 -0
- package/src/stories/Fondamentaux/Accessibilite/Accessibilite.stories.ts +36 -0
- package/src/stories/Fondamentaux/Accessibilite/AccessibiliteItems.ts +706 -0
- package/src/stories/Fondamentaux/Accessibilite/constants/ExpertiseLevelEnum.ts +5 -0
- package/src/stories/Fondamentaux/Accessibilite/constants/RGAALevelEnum.ts +4 -0
- package/src/stories/Fondamentaux/EcoConception/EcoConception.mdx +24 -0
- package/src/stories/Fondamentaux/EcoConception/Econception.stories.ts +30 -0
- package/src/stories/Fondamentaux/EcoConception/ecoDesignItems.ts +55 -0
- package/src/stories/GuideDuDev/CommentContribuer.mdx +22 -0
- package/src/stories/GuideDuDev/components.stories.ts +23 -0
- package/src/stories/GuideDuDev/moduleDeNotification.mdx +182 -0
- package/src/stories/GuideDuDev/vuetifyOptions.mdx +72 -0
- package/src/stories/Guidelines/Colors.mdx +220 -0
- package/src/stories/Guidelines/CustomisationEtThemes.mdx +3 -0
- package/src/stories/Guidelines/Introduction.mdx +35 -0
- package/src/stories/Guidelines/Typo.mdx +53 -0
- package/src/stories/Home/Accueil.mdx +7 -0
- package/src/stories/Home/PolitiqueDeConfidentialite.mdx +4 -0
- package/src/stories/Home/synapse.webp +0 -0
- package/src/temp/TestA11y.vue +14 -0
- package/src/temp/TestComponent.vue +37 -0
- package/src/temp/TestDTComponent.vue +93 -0
- package/src/temp/customizableOptions.vue +18 -0
- package/src/temp/gridsTests.vue +54 -0
- package/src/temp/options.json +5 -0
- package/src/types/vuetifyTypes.ts +3 -0
- package/src/utils/convertToUnit/index.ts +16 -0
- package/src/utils/convertToUnit/test/convertToUnit.spec.ts +32 -0
- package/src/utils/functions/copyToClipboard/index.ts +38 -0
- package/src/utils/functions/copyToClipboard/tests/copyToClipboard.spec.ts +104 -0
- package/src/utils/functions/downloadFile/index.ts +37 -0
- package/src/utils/functions/downloadFile/tests/downloadFile.spec.ts +69 -0
- 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
|