@cnamts/synapse 0.0.8-alpha → 0.0.9-alpha

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 (111) hide show
  1. package/dist/design-system-v3.d.ts +584 -128
  2. package/dist/design-system-v3.js +4176 -2694
  3. package/dist/design-system-v3.umd.cjs +1 -1
  4. package/dist/style.css +1 -1
  5. package/package.json +1 -1
  6. package/src/assets/settings.scss +1 -1
  7. package/src/components/ContextualMenu/Accessibilite.mdx +14 -0
  8. package/src/components/ContextualMenu/Accessibilite.stories.ts +191 -0
  9. package/src/components/ContextualMenu/AccessibiliteItems.ts +89 -0
  10. package/src/components/ContextualMenu/constants/ExpertiseLevelEnum.ts +4 -0
  11. package/src/components/Customs/SySelect/SySelect.stories.ts +7 -7
  12. package/src/components/Customs/SySelect/SySelect.vue +9 -4
  13. package/src/components/Customs/SySelect/tests/SySelect.spec.ts +2 -2
  14. package/src/components/Customs/SyTextField/SyTextField.stories.ts +187 -2
  15. package/src/components/Customs/SyTextField/SyTextField.vue +185 -16
  16. package/src/components/Customs/SyTextField/tests/SyTextField.spec.ts +2 -4
  17. package/src/components/Customs/SyTextField/tests/__snapshots__/SyTextField.spec.ts.snap +18 -16
  18. package/src/components/Customs/SyTextField/types.d.ts +2 -2
  19. package/src/components/DatePicker/DatePicker.mdx +191 -0
  20. package/src/components/DatePicker/DatePicker.stories.ts +787 -0
  21. package/src/components/DatePicker/DatePicker.vue +560 -0
  22. package/src/components/DatePicker/DateTextInput.vue +409 -0
  23. package/src/components/DatePicker/tests/DatePicker.spec.ts +266 -0
  24. package/src/components/DialogBox/DialogBox.stories.ts +1 -1
  25. package/src/components/ExternalLinks/Accessibilite.mdx +14 -0
  26. package/src/components/ExternalLinks/Accessibilite.stories.ts +191 -0
  27. package/src/components/ExternalLinks/AccessibiliteItems.ts +197 -0
  28. package/src/components/ExternalLinks/constants/ExpertiseLevelEnum.ts +4 -0
  29. package/src/components/ExternalLinks/tests/__snapshots__/ExternalLinks.spec.ts.snap +9 -9
  30. package/src/components/FileUpload/FileUpload.mdx +165 -0
  31. package/src/components/FileUpload/FileUpload.stories.ts +429 -0
  32. package/src/components/FileUpload/FileUpload.vue +195 -0
  33. package/src/components/FileUpload/FileUploadContent.vue +109 -0
  34. package/src/components/FileUpload/locales.ts +10 -0
  35. package/src/components/FileUpload/tests/FileUpload.spec.ts +332 -0
  36. package/src/components/FileUpload/tests/__snapshots__/FileUpload.spec.ts.snap +7 -0
  37. package/src/components/FileUpload/useFileDrop.ts +23 -0
  38. package/src/components/FileUpload/validateFiles.ts +39 -0
  39. package/src/components/NirField/NirField.stories.ts +1 -1
  40. package/src/components/NirField/NirField.vue +2 -1
  41. package/src/components/PasswordField/Accessibilite.mdx +14 -0
  42. package/src/components/PasswordField/Accessibilite.stories.ts +191 -0
  43. package/src/components/PasswordField/AccessibiliteItems.ts +184 -0
  44. package/src/components/PasswordField/PasswordField.vue +3 -3
  45. package/src/components/PasswordField/constants/ExpertiseLevelEnum.ts +4 -0
  46. package/src/components/PhoneField/PhoneField.vue +44 -60
  47. package/src/components/PhoneField/tests/PhoneField.spec.ts +0 -15
  48. package/src/components/RangeField/RangeField.mdx +54 -0
  49. package/src/components/RangeField/RangeField.stories.ts +189 -0
  50. package/src/components/RangeField/RangeField.vue +157 -0
  51. package/src/components/RangeField/RangeSlider/RangeSlider.vue +387 -0
  52. package/src/components/RangeField/RangeSlider/Tooltip/Tooltip.vue +64 -0
  53. package/src/components/RangeField/RangeSlider/tests/__snapshots__/rangeSlider.spec.ts.snap +27 -0
  54. package/src/components/RangeField/RangeSlider/tests/rangeSlider.spec.ts +100 -0
  55. package/src/components/RangeField/RangeSlider/tests/useDoubleSlider.spec.ts +246 -0
  56. package/src/components/RangeField/RangeSlider/tests/useMouseSlide.spec.ts +204 -0
  57. package/src/components/RangeField/RangeSlider/tests/useThumb.spec.ts +22 -0
  58. package/src/components/RangeField/RangeSlider/tests/useThumbKeyboard.spec.ts +233 -0
  59. package/src/components/RangeField/RangeSlider/tests/useTooltipsNudge.spec.ts +150 -0
  60. package/src/components/RangeField/RangeSlider/tests/useTrack.spec.ts +314 -0
  61. package/src/components/RangeField/RangeSlider/tests/vAnimateClick.spec.ts +32 -0
  62. package/src/components/RangeField/RangeSlider/types.ts +15 -0
  63. package/src/components/RangeField/RangeSlider/useMouseSlide.ts +109 -0
  64. package/src/components/RangeField/RangeSlider/useRangeSlider.ts +126 -0
  65. package/src/components/RangeField/RangeSlider/useThumb.ts +18 -0
  66. package/src/components/RangeField/RangeSlider/useThumbKeyboard.ts +84 -0
  67. package/src/components/RangeField/RangeSlider/useTooltipsNudge.ts +92 -0
  68. package/src/components/RangeField/RangeSlider/useTrack.ts +116 -0
  69. package/src/components/RangeField/RangeSlider/vAnimateClick.ts +19 -0
  70. package/src/components/RangeField/config.ts +7 -0
  71. package/src/components/RangeField/locales.ts +4 -0
  72. package/src/components/RangeField/tests/RangeField.spec.ts +224 -0
  73. package/src/components/RangeField/tests/__snapshots__/RangeField.spec.ts.snap +379 -0
  74. package/src/components/RatingPicker/EmotionPicker/EmotionPicker.vue +205 -0
  75. package/src/components/RatingPicker/EmotionPicker/locales.ts +3 -0
  76. package/src/components/RatingPicker/EmotionPicker/tests/EmotionPicker.spec.ts +104 -0
  77. package/src/components/RatingPicker/EmotionPicker/tests/__snapshots__/EmotionPicker.spec.ts.snap +66 -0
  78. package/src/components/RatingPicker/NumberPicker/NumberPicker.vue +159 -0
  79. package/src/components/RatingPicker/NumberPicker/locales.ts +4 -0
  80. package/src/components/RatingPicker/NumberPicker/tests/NumberPicker.spec.ts +73 -0
  81. package/src/components/RatingPicker/NumberPicker/tests/__snapshots__/NumberPicker.spec.ts.snap +105 -0
  82. package/src/components/RatingPicker/Rating.ts +45 -0
  83. package/src/components/RatingPicker/RatingPicker.mdx +56 -0
  84. package/src/components/RatingPicker/RatingPicker.stories.ts +515 -0
  85. package/src/components/RatingPicker/RatingPicker.vue +122 -0
  86. package/src/components/RatingPicker/StarsPicker/StarsPicker.vue +116 -0
  87. package/src/components/RatingPicker/StarsPicker/tests/StarsPicker.spec.ts +95 -0
  88. package/src/components/RatingPicker/StarsPicker/tests/__snapshots__/StarsPicker.spec.ts.snap +36 -0
  89. package/src/components/RatingPicker/locales.ts +3 -0
  90. package/src/components/RatingPicker/tests/Rating.spec.ts +104 -0
  91. package/src/components/RatingPicker/tests/RatingPicker.spec.ts +187 -0
  92. package/src/components/RatingPicker/tests/__snapshots__/RatingPicker.spec.ts.snap +108 -0
  93. package/src/components/SearchListField/SearchListField.mdx +74 -0
  94. package/src/components/SearchListField/SearchListField.stories.ts +126 -0
  95. package/src/components/SearchListField/SearchListField.vue +194 -0
  96. package/src/components/SearchListField/locales.ts +5 -0
  97. package/src/components/SearchListField/tests/SearchListField.spec.ts +323 -0
  98. package/src/components/SearchListField/types.d.ts +4 -0
  99. package/src/components/SelectBtnField/SelectBtnField.mdx +50 -0
  100. package/src/components/SelectBtnField/SelectBtnField.stories.ts +763 -0
  101. package/src/components/SelectBtnField/SelectBtnField.vue +283 -0
  102. package/src/components/SelectBtnField/config.ts +11 -0
  103. package/src/components/SelectBtnField/tests/SelectBtnField.spec.ts +327 -0
  104. package/src/components/SelectBtnField/tests/__snapshots__/SelectBtnField.spec.ts.snap +125 -0
  105. package/src/components/SelectBtnField/types.d.ts +11 -0
  106. package/src/components/index.ts +8 -1
  107. package/src/composables/rules/useFieldValidation.ts +172 -44
  108. package/src/designTokens/index.ts +3 -3
  109. package/src/stories/Fondamentaux/CustomisationEtThemes.mdx +52 -2
  110. package/src/utils/calcHumanFileSize/index.ts +12 -0
  111. package/src/utils/calcHumanFileSize/tests/calcHumanFileSize.spec.ts +21 -0
@@ -0,0 +1,165 @@
1
+ import {Controls, Canvas, Meta, Source} from '@storybook/blocks';
2
+
3
+ import * as FileUploadStories from './FileUpload.stories.ts';
4
+
5
+ <Meta of={FileUploadStories} />
6
+
7
+ # FileUpload
8
+
9
+ Le composant `FileUpload` est un champ d'upload de fichier par glisser-déposer ou par sélection de fichier.
10
+
11
+ <Canvas of={FileUploadStories.Default} />
12
+
13
+ # API
14
+
15
+ <Controls of={FileUploadStories.Default} />
16
+
17
+ ## Gérer l'envoi des fichiers
18
+
19
+ Vous pouvez envoyer les fichiers sur un serveur en un objet FormData.
20
+
21
+ <Source dark language="typescript" code={`
22
+ <script setup lang="ts">
23
+ import { FileUpload } from '@cnamts/synapse'
24
+ import { ref } from 'vue'
25
+
26
+ const files = ref<File[]>([])
27
+
28
+ function upload() {
29
+ const formData = new FormData()
30
+ files.value.forEach((file: File) => {
31
+ formData.append(file.name, file)
32
+ })
33
+
34
+ fetch('http://localhost:3000/upload', {
35
+ method: 'POST',
36
+ body: formData,
37
+ })
38
+ }
39
+
40
+ </script>
41
+
42
+ <template>
43
+ <main class="pa-12">
44
+ {{ files?.map(file => file.name).join(', ') }}
45
+
46
+ <br>
47
+ <FileUpload
48
+ v-model="files"
49
+ :multiple="true"
50
+ @error="console.log"
51
+ />
52
+
53
+ <VBtn
54
+ @click="upload"
55
+ >
56
+ submit
57
+ </VBtn>
58
+ </main>
59
+ </template>
60
+ `} />
61
+
62
+ Même exemple avec axios :
63
+
64
+ <Source dark language="typescript" code={`
65
+ <script setup lang="ts">
66
+ import { FileUpload } from '@cnamts/synapse'
67
+ import { ref } from 'vue'
68
+ import axios from 'axios'
69
+
70
+ const files = ref<File[]>([])
71
+
72
+ function upload() {
73
+ const formData = new FormData()
74
+ files.value.forEach((file: File) => {
75
+ formData.append(file.name, file)
76
+ })
77
+
78
+ axios.post('http://localhost:3000/upload', formData, {
79
+ headers: {
80
+ 'Content-Type': 'multipart/form-data',
81
+ },
82
+ })
83
+ }
84
+
85
+ </script>
86
+
87
+ <template>
88
+ <main class="pa-12">
89
+ {{ files?.map(file => file.name).join(', ') }}
90
+
91
+ <br>
92
+ <FileUpload
93
+ v-model="files"
94
+ :multiple="true"
95
+ @error="console.log"
96
+ />
97
+
98
+ <VBtn
99
+ @click="upload"
100
+ >
101
+ submit
102
+ </VBtn>
103
+
104
+ </main>
105
+ </template>
106
+ `} />
107
+
108
+ ## Coté backend
109
+
110
+ Voici un exemple de code pour gérer l'upload de fichier en Node.js avec Express,
111
+ ce code est destiné à des fins de tests uniquement.
112
+
113
+ <Source dark language="javascript" code={`
114
+ import express from "express";
115
+ import fileUpload from "express-fileupload";
116
+ import cors from "cors";
117
+ import { join } from "path";
118
+ import { existsSync, mkdirSync } from "fs";
119
+
120
+ const app = express();
121
+
122
+ app.use(fileUpload());
123
+ app.use(cors({ origin: true, credentials: true }));
124
+
125
+ const uploadDir = join(import.meta.dirname, "uploads");
126
+
127
+ if (!existsSync(uploadDir)) {
128
+ mkdirSync(uploadDir);
129
+ }
130
+
131
+ app.post("/upload", async function (req, res) {
132
+ if (!req.files || Object.keys(req.files).length === 0) {
133
+ return res.status(400).send("No files were uploaded.");
134
+ }
135
+
136
+ let errors = [];
137
+
138
+ const uploadPromises = Object.keys(req.files).map(async (fileKey) => {
139
+ const file = req.files[fileKey];
140
+ const filePath = join(uploadDir, file.name);
141
+
142
+ try {
143
+ await file.mv(filePath);
144
+ } catch (err) {
145
+ errors.push(err);
146
+ }
147
+ });
148
+
149
+ try {
150
+ await Promise.all(uploadPromises);
151
+
152
+ if (errors.length > 0) {
153
+ return res.status(500).send(errors.join(", "));
154
+ }
155
+ res.send("Files uploaded!");
156
+ } catch (err) {
157
+ res.status(500).send("An error occurred during file upload.");
158
+ }
159
+ });
160
+
161
+ app.listen(3000, function () {
162
+ console.log("App is listening on port 3000");
163
+ });
164
+
165
+ `} />
@@ -0,0 +1,429 @@
1
+ import { fn } from '@storybook/test'
2
+ import FileUpload from './FileUpload.vue'
3
+ import type { Meta, StoryObj } from '@storybook/vue3'
4
+
5
+ import NotificationBar from '@/components/NotificationBar/NotificationBar.vue'
6
+ import { useNotificationService } from '@/services/NotificationService'
7
+ import { mdiCloudUpload } from '@mdi/js'
8
+ import { VIcon } from 'vuetify/components'
9
+
10
+ const meta = {
11
+ title: 'Composants/Formulaires/FileUpload',
12
+ component: FileUpload,
13
+ argTypes: {
14
+ 'modelValue': {
15
+ description: 'La/Les fichiers de l\'utilisateur',
16
+ control: 'file',
17
+ table: {
18
+ type: {
19
+ summary: 'File[]',
20
+ },
21
+ disable: false,
22
+ category: 'props',
23
+ },
24
+ },
25
+ 'disabled': {
26
+ description: 'Désactive le champ',
27
+ control: 'boolean',
28
+ table: {
29
+ type: {
30
+ summary: 'boolean',
31
+ },
32
+ },
33
+ defaultValue: false,
34
+ },
35
+ 'multiple': {
36
+ description: 'Autorise l\'envoi de plusieurs fichiers',
37
+ control: 'boolean',
38
+ table: {
39
+ type: {
40
+ summary: 'boolean',
41
+ },
42
+ },
43
+ defaultValue: false,
44
+ },
45
+ 'fileSizeMax': {
46
+ description: 'Taille maximale des fichiers en octets',
47
+ control: 'number',
48
+ table: {
49
+ type: {
50
+ summary: 'number',
51
+ },
52
+ },
53
+ defaultValue: 10485760,
54
+ },
55
+ 'fileSizeUnits': {
56
+ description: 'Unité de taille des fichiers',
57
+ control: 'object',
58
+ table: {
59
+ type: {
60
+ summary: 'string[]',
61
+ },
62
+ defaultValue: {
63
+ summary: '[\'o\', \'Ko\', \'Mo\', \'Go\', \'To\']',
64
+ },
65
+ },
66
+ },
67
+ 'allowedExtensions': {
68
+ description: 'Extensions de fichiers autorisées, un tableau vide autorise toutes les extensions',
69
+ control: 'object',
70
+ table: {
71
+ type: {
72
+ summary: 'string[]',
73
+ },
74
+
75
+ defaultValue: {
76
+ summary: '[\'pdf\', \'jpg\', \'jpeg\', \'png\']',
77
+ },
78
+ },
79
+ },
80
+ 'maxWidth': {
81
+ description: 'Largeur maximale du composant',
82
+ control: 'text',
83
+ table: {
84
+ type: {
85
+ summary: 'number | string',
86
+ },
87
+ category: 'props',
88
+ },
89
+ },
90
+ 'minWidth': {
91
+ description: 'Largeur minimale du composant',
92
+ control: 'text',
93
+ table: {
94
+ type: {
95
+ summary: 'number | string',
96
+ },
97
+ category: 'props',
98
+ },
99
+ },
100
+ 'width': {
101
+ description: 'Largeur du composant',
102
+ control: 'text',
103
+ table: {
104
+ type: {
105
+ summary: 'number | string',
106
+ },
107
+ category: 'props',
108
+ },
109
+ },
110
+ 'onUpdate:modelValue': {
111
+ description: 'Événement émis lorsqu\'un fichier est ajouté ou supprimé',
112
+ table: {
113
+ type: {
114
+ summary: 'File[]',
115
+ },
116
+ category: 'events',
117
+ },
118
+ },
119
+ 'onError': {
120
+ description: 'Événement émis lorsqu\'une erreur de validation survient',
121
+ table: {
122
+ type: {
123
+ summary: 'string[]',
124
+ },
125
+ category: 'events',
126
+ },
127
+ },
128
+ 'default': {
129
+ description: 'Intérieur de champ',
130
+ control: 'text',
131
+ table: {
132
+ category: 'slots',
133
+ type: {
134
+ summary: undefined,
135
+ },
136
+ },
137
+ },
138
+ 'icon': {
139
+ description: 'Icône supérieur',
140
+ control: 'text',
141
+ table: {
142
+ category: 'slots',
143
+ type: {
144
+ summary: undefined,
145
+ },
146
+ },
147
+ },
148
+ 'action-text': {
149
+ description: 'Texte de l\'appel à l\'action',
150
+ control: 'text',
151
+ table: {
152
+ category: 'slots',
153
+ type: {
154
+ summary: undefined,
155
+ },
156
+ },
157
+ },
158
+ 'or': {
159
+ description: 'Texte de séparation entre le cta et le bouton',
160
+ control: 'text',
161
+ table: {
162
+ category: 'slots',
163
+ type: {
164
+ summary: undefined,
165
+ },
166
+ },
167
+ },
168
+ 'button-text': {
169
+ description: 'Texte du bouton',
170
+ control: 'text',
171
+ table: {
172
+ category: 'slots',
173
+ type: {
174
+ summary: undefined,
175
+ },
176
+ },
177
+ },
178
+ 'locales': {
179
+ description: 'Traductions',
180
+ control: false,
181
+ table: {
182
+ category: 'props',
183
+ type: {
184
+ summary: undefined,
185
+ },
186
+ defaultValue: {
187
+ summary: `Locales`,
188
+ detail: `{
189
+ or: 'Ou',
190
+ chooseFile: (multiple: boolean) => multiple ? 'Choisir des fichiers' : 'Choisir un fichier',
191
+ infoText: (max: string, ext: string[]): string =>
192
+ \`Taille max. : \${max}. \${ext.length === 1 ? 'Format accepté' : 'Formats acceptés'} : \${ext.join(', ')}\`,
193
+ fileSizeUnits: ['o', 'Ko', 'Mo', 'Go', 'To'],
194
+ dropFilesHere: (multiple: boolean): string => (!multiple ? 'Déposer votre fichier ici' : 'Déposer vos fichiers ici'),
195
+ errorSize: (fileName: string, max: string): string => \`Le fichier \${fileName} est trop volumineux. Taille max. : \${max}\`,
196
+ errorExtension: (fileName: string, ext: string[]): string => \`Le fichier \${fileName} a une extension invalide. Extensions acceptées : \${ext.join(', ')}\`,
197
+ }`,
198
+ },
199
+ },
200
+ },
201
+ },
202
+ parameters: {
203
+ controls: {
204
+ exclude: ['error', 'update:modelValue', 'slotName'],
205
+ },
206
+ docs: {
207
+ controls: {
208
+ sort: 'requiredFirst',
209
+ },
210
+ },
211
+ },
212
+ } satisfies Meta<typeof FileUpload>
213
+
214
+ export default meta
215
+
216
+ type Story = StoryObj<typeof meta>
217
+
218
+ export const Default: Story = {
219
+ args: {
220
+ 'modelValue': [],
221
+ 'multiple': false,
222
+ 'onUpdate:modelValue': fn(),
223
+ 'onError': fn(),
224
+ },
225
+ render: args => ({
226
+ components: { FileUpload },
227
+ setup() {
228
+ return { args }
229
+ },
230
+ template: `<div>
231
+ <FileUpload
232
+ v-model="args.modelValue"
233
+ v-bind="args"
234
+ />
235
+ <ul class="ma-2">
236
+ <li v-for="file in args.modelValue" :key="file.name">{{ file.name }}</li>
237
+ </ul>
238
+ </div>`,
239
+ }),
240
+ parameters: {
241
+ sourceCode: [
242
+ {
243
+ name: 'Template',
244
+ code: `
245
+ <template>
246
+ <FileUpload
247
+ v-model="modelValue"
248
+ />
249
+ <ul class="ma-2">
250
+ <li v-for="file in modelValue" :key="file.name">{{ file.name }}</li>
251
+ </ul>
252
+ </template>
253
+ `,
254
+ },
255
+ {
256
+ name: 'Script',
257
+ code: `
258
+ <script setup lang="ts">
259
+ import { FileUpload } from '@cnamts/synapse'
260
+ import { ref } from 'vue'
261
+
262
+ const modelValue = ref([])
263
+
264
+ </script>
265
+ `,
266
+ },
267
+ ],
268
+ },
269
+ }
270
+
271
+ export const MultipleFiles: Story = {
272
+ args: {
273
+ 'modelValue': [],
274
+ 'multiple': true,
275
+ 'onUpdate:modelValue': fn(),
276
+ 'onError': fn(),
277
+ },
278
+ render: args => ({
279
+ components: { FileUpload, NotificationBar },
280
+ setup() {
281
+ const { addNotification } = useNotificationService()
282
+ const sendError = (e: string[]) => {
283
+ addNotification({
284
+ id: Date.now().toString(),
285
+ message: e.join(', '),
286
+ type: 'error',
287
+ timeout: -1,
288
+ })
289
+ }
290
+ return { args, sendError }
291
+ },
292
+ template: `<div>
293
+ <NotificationBar />
294
+ <FileUpload
295
+ v-model="args.modelValue"
296
+ v-bind="args"
297
+ />
298
+ <ul class="ma-2">
299
+ <li v-for="file in args.modelValue" :key="file.name">{{ file.name }}</li>
300
+ </ul>
301
+ </div>`,
302
+ }),
303
+ parameters: {
304
+ sourceCode: [
305
+ {
306
+ name: 'Template',
307
+ code: `
308
+ <template>
309
+ <div>
310
+ <FileUpload
311
+ v-model="files"
312
+ :multiple="true"
313
+ />
314
+ <ul class="ma-2">
315
+ <li v-for="file in files" :key="file.name">{{ file.name }}</li>
316
+ </ul>
317
+ <NotificationBar />
318
+ </div>
319
+ </template>
320
+ `,
321
+ },
322
+ {
323
+ name: 'Script',
324
+ code: `
325
+ <script setup lang="ts">
326
+ import { FileUpload, NotificationBar, useNotificationService } from '@cnamts/synapse'
327
+ import { ref } from 'vue'
328
+
329
+ const files = ref([])
330
+
331
+ const { addNotification } = useNotificationService()
332
+ const sendError = (e: string[]) => {
333
+ addNotification({
334
+ id: Date.now().toString(),
335
+ message: e.join(', '),
336
+ type: 'error',
337
+ timeout: -1,
338
+ })
339
+ }
340
+
341
+ </script>
342
+ `,
343
+ },
344
+ ],
345
+ },
346
+ }
347
+
348
+ export const Customization: Story = {
349
+ args: {
350
+ 'modelValue': [],
351
+ 'onUpdate:modelValue': fn(),
352
+ 'onError': fn(),
353
+ 'width': '50%',
354
+ 'minWidth': '300px',
355
+ 'maxWidth': '600px',
356
+ },
357
+ render: args => ({
358
+ components: { FileUpload, VIcon },
359
+ setup() {
360
+ return { args, uploadIcon: mdiCloudUpload }
361
+ },
362
+ template: `<div>
363
+ <FileUpload
364
+ v-model="args.modelValue"
365
+ v-bind="args"
366
+ class="bg-accent elevation-3 px-4 py-3 border-0 rounded-0"
367
+ >
368
+ <span class="d-flex align-center white--text">
369
+ <VIcon
370
+ size="25"
371
+ color="white"
372
+ class="mr-4"
373
+ >
374
+ {{ uploadIcon }}
375
+ </VIcon>
376
+
377
+ Sélectionner un fichier
378
+ </span>
379
+ </FileUpload>
380
+ <ul class="ma-2">
381
+ <li v-for="file in args.modelValue" :key="file.name">{{ file.name }}</li>
382
+ </ul>
383
+ </div>`,
384
+ }),
385
+ parameters: {
386
+ sourceCode: [
387
+ {
388
+ name: 'Template',
389
+ code: `
390
+ <template>
391
+ <div>
392
+ <FileUpload
393
+ v-model="files"
394
+ width="50%"
395
+ min-width="300px"
396
+ max-width="600px"
397
+ class="bg-accent elevation-3 px-4 py-3 border-0 rounded-0"
398
+ >
399
+ <span>
400
+ <v-icon size="25" color="white" class="mr-4">
401
+ {{ uploadIcon }}
402
+ </v-icon>
403
+ Sélectionner un fichier
404
+ </span>
405
+ </FileUpload>
406
+ <ul class="ma-2">
407
+ <li v-for="file in files" :key="file.name">{{ file.name }}</li>
408
+ </ul>
409
+ </div>
410
+ </template>
411
+ `,
412
+ },
413
+ {
414
+ name: 'Script',
415
+ code: `
416
+ <script setup lang="ts">
417
+ import { FileUpload, VIcon } from '@cnamts/synapse'
418
+ import { mdiCloudUpload } from '@mdi/js'
419
+ import { ref } from 'vue'
420
+
421
+ const files = ref([])
422
+
423
+ const uploadIcon = mdiCloudUpload
424
+ </script>
425
+ `,
426
+ },
427
+ ],
428
+ },
429
+ }