@cnamts/synapse 0.0.15-alpha → 0.0.16-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 (41) hide show
  1. package/dist/components/CookiesSelection/CookiesSelection.d.ts +26 -26
  2. package/dist/components/Customs/SyTextField/SyTextField.d.ts +1391 -1
  3. package/dist/components/DatePicker/DatePicker.d.ts +2810 -16
  4. package/dist/components/DatePicker/DateTextInput.d.ts +1401 -4
  5. package/dist/components/LangBtn/LangBtn.d.ts +4 -4
  6. package/dist/components/NirField/NirField.d.ts +2794 -4
  7. package/dist/components/PeriodField/PeriodField.d.ts +5636 -48
  8. package/dist/components/SyAlert/SyAlert.d.ts +72 -1
  9. package/dist/components/UploadWorkflow/UploadWorkflow.d.ts +26 -26
  10. package/dist/components/index.d.ts +1 -0
  11. package/dist/composables/date/useDateFormat.d.ts +2 -2
  12. package/dist/composables/date/useDateFormatDayjs.d.ts +23 -0
  13. package/dist/composables/date/useDateInitializationDayjs.d.ts +18 -0
  14. package/dist/design-system-v3.js +3953 -3728
  15. package/dist/design-system-v3.umd.cjs +1 -1
  16. package/dist/style.css +1 -1
  17. package/package.json +1 -1
  18. package/src/components/Customs/SyTextField/Accessibilite.stories.ts +7 -0
  19. package/src/components/Customs/SyTextField/SyTextField.stories.ts +13 -0
  20. package/src/components/Customs/SyTextField/SyTextField.vue +82 -17
  21. package/src/components/DatePicker/ComplexDatePicker/ComplexDatePicker.vue +795 -0
  22. package/src/components/DatePicker/DatePicker.stories.ts +432 -1
  23. package/src/components/DatePicker/DatePicker.vue +66 -24
  24. package/src/components/DatePicker/DatePickerValidation.stories.ts +9 -1
  25. package/src/components/DatePicker/DateTextInput.vue +85 -133
  26. package/src/components/DatePicker/docExamples/DatePickerBidirectionalValidation.vue +282 -0
  27. package/src/components/DatePicker/tests/DatePicker.spec.ts +33 -32
  28. package/src/components/DatePicker/tests/DateTextInput.spec.ts +81 -33
  29. package/src/components/SyAlert/Accessibilite.stories.ts +4 -0
  30. package/src/components/SyAlert/SyAlert.mdx +3 -7
  31. package/src/components/SyAlert/SyAlert.stories.ts +19 -12
  32. package/src/components/SyAlert/SyAlert.vue +88 -51
  33. package/src/components/SyAlert/tests/SyAlert.spec.ts +20 -2
  34. package/src/components/SyAlert/tests/__snapshots__/SyAlert.spec.ts.snap +83 -75
  35. package/src/components/index.ts +1 -0
  36. package/src/composables/date/useDateFormat.ts +17 -1
  37. package/src/composables/date/useDateFormatDayjs.ts +84 -0
  38. package/src/composables/date/useDateInitializationDayjs.ts +133 -0
  39. package/src/stories/Accessibilite/Avancement/Avancement.mdx +12 -0
  40. package/src/stories/Accessibilite/Avancement/Avancement.stories.ts +134 -0
  41. /package/src/components/DatePicker/{DatePickerValidationExamples.vue → docExamples/DatePickerValidationExamples.vue} +0 -0
@@ -0,0 +1,795 @@
1
+ <script lang="ts" setup>
2
+ import { ref, computed, watch, onMounted, onBeforeUnmount, nextTick, type ComponentPublicInstance } from 'vue'
3
+ import SyTextField from '@/components/Customs/SyTextField/SyTextField.vue'
4
+ import DateTextInput from '../DateTextInput.vue'
5
+ import { VDatePicker } from 'vuetify/components'
6
+ import { useValidation } from '@/composables/validation/useValidation'
7
+ import { useDateFormat } from '@/composables/date/useDateFormatDayjs'
8
+ import { useDateInitialization, type DateValue, type DateInput } from '@/composables/date/useDateInitializationDayjs'
9
+ import { useDatePickerAccessibility } from '@/composables/date/useDatePickerAccessibility'
10
+ import dayjs from 'dayjs'
11
+ import customParseFormat from 'dayjs/plugin/customParseFormat'
12
+
13
+ // Initialiser les plugins dayjs
14
+ dayjs.extend(customParseFormat)
15
+
16
+ const { parseDate, formatDate } = useDateFormat()
17
+ const { initializeSelectedDates } = useDateInitialization()
18
+ const { updateAccessibility } = useDatePickerAccessibility()
19
+
20
+ const props = withDefaults(defineProps<{
21
+ modelValue?: DateInput
22
+ placeholder?: string
23
+ format?: string
24
+ dateFormatReturn?: string
25
+ isBirthDate?: boolean
26
+ showWeekNumber?: boolean
27
+ required?: boolean
28
+ displayRange?: boolean
29
+ displayIcon?: boolean
30
+ displayAppendIcon?: boolean
31
+ displayPrependIcon?: boolean
32
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any -- mock Axios headers
33
+ customRules?: { type: string, options: any }[]
34
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any -- mock Axios headers
35
+ customWarningRules?: { type: string, options: any }[]
36
+ disabled?: boolean
37
+ noIcon?: boolean
38
+ noCalendar?: boolean
39
+ isOutlined?: boolean
40
+ readonly?: boolean
41
+ width?: string
42
+ disableErrorHandling?: boolean
43
+ showSuccessMessages?: boolean
44
+ bgColor?: string
45
+ textFieldActivator?: boolean
46
+ }>(), {
47
+ modelValue: undefined,
48
+ placeholder: 'Sélectionner une date',
49
+ format: 'DD/MM/YYYY',
50
+ dateFormatReturn: '',
51
+ isBirthDate: false,
52
+ showWeekNumber: false,
53
+ required: false,
54
+ displayRange: false,
55
+ displayIcon: true,
56
+ displayAppendIcon: false,
57
+ displayPrependIcon: true,
58
+ customRules: () => [],
59
+ customWarningRules: () => [],
60
+ disabled: false,
61
+ noIcon: false,
62
+ noCalendar: false,
63
+ isOutlined: true,
64
+ readonly: false,
65
+ width: '100%',
66
+ disableErrorHandling: false,
67
+ showSuccessMessages: true,
68
+ bgColor: undefined,
69
+ textFieldActivator: false,
70
+ })
71
+
72
+ const emit = defineEmits<{
73
+ (e: 'update:modelValue', value: DateValue): void
74
+ (e: 'closed'): void
75
+ (e: 'focus'): void
76
+ (e: 'blur'): void
77
+ }>()
78
+
79
+ const selectedDates = ref<Date | Date[] | null>(
80
+ initializeSelectedDates(props.modelValue as DateInput | null, props.format, props.dateFormatReturn),
81
+ )
82
+
83
+ const isDatePickerVisible = ref(false)
84
+ const validation = useValidation({
85
+ showSuccessMessages: props.showSuccessMessages,
86
+ fieldIdentifier: 'Date',
87
+ customRules: props.customRules,
88
+ warningRules: props.customWarningRules,
89
+ disableErrorHandling: props.disableErrorHandling,
90
+ })
91
+ const { errors, warnings, successes, validateField, clearValidation } = validation
92
+
93
+ const errorMessages = errors
94
+ const warningMessages = warnings
95
+ const successMessages = successes
96
+ const displayFormattedDate = ref('')
97
+
98
+ const textInputValue = ref<string>('')
99
+
100
+ // Variable pour éviter les mises à jour récursives
101
+ const isUpdatingFromInternal = ref(false)
102
+
103
+ // Fonction pour valider les dates
104
+ const validateDates = (forceValidation = false) => {
105
+ if (props.noCalendar) {
106
+ // En mode no-calendar, on délègue la validation au DateTextInput
107
+ return
108
+ }
109
+
110
+ // Réinitialiser la validation
111
+ clearValidation()
112
+
113
+ // Si la gestion des erreurs est désactivée, on effectue la validation interne
114
+ // mais on n'ajoute pas les messages d'erreur
115
+ const shouldDisplayErrors = !props.disableErrorHandling
116
+
117
+ // Vérifier si le champ est requis et vide
118
+ if ((forceValidation || !isUpdatingFromInternal.value) && props.required && (!selectedDates.value || (Array.isArray(selectedDates.value) && selectedDates.value.length === 0))) {
119
+ if (shouldDisplayErrors) {
120
+ errors.value.push('La date est requise.')
121
+ }
122
+ return
123
+ }
124
+
125
+ if (!selectedDates.value) return
126
+
127
+ // Préparer les dates à valider
128
+ const datesToValidate = Array.isArray(selectedDates.value)
129
+ ? selectedDates.value
130
+ : [selectedDates.value]
131
+
132
+ // Valider chaque date
133
+ if (shouldDisplayErrors) {
134
+ datesToValidate.forEach((date) => {
135
+ validateField(
136
+ date,
137
+ props.customRules,
138
+ props.customWarningRules,
139
+ )
140
+ })
141
+
142
+ // Dédoublonner les messages (au cas où plusieurs dates auraient les mêmes messages)
143
+ errors.value = [...new Set(errors.value)]
144
+ warnings.value = [...new Set(warnings.value)]
145
+ successes.value = [...new Set(successes.value)]
146
+ }
147
+ }
148
+
149
+ // Fonction centralisée pour mettre à jour le modèle
150
+ const updateModel = (value: DateValue) => {
151
+ // Éviter les mises à jour inutiles
152
+ if (JSON.stringify(value) === JSON.stringify(props.modelValue)) return
153
+
154
+ try {
155
+ isUpdatingFromInternal.value = true
156
+ emit('update:modelValue', value)
157
+ }
158
+ finally {
159
+ // S'assurer que le flag est toujours réinitialisé
160
+ setTimeout(() => {
161
+ isUpdatingFromInternal.value = false
162
+ }, 0)
163
+ }
164
+ }
165
+
166
+ // Watcher pour mettre à jour le modèle lorsque les dates sélectionnées changent
167
+ watch(selectedDates, (newValue) => {
168
+ // Valider les dates
169
+ validateDates()
170
+
171
+ // Mettre à jour le modèle si nécessaire
172
+ if (newValue !== null) {
173
+ updateModel(formattedDate.value)
174
+
175
+ // Mettre à jour textInputValue pour le DateTextInput
176
+ try {
177
+ isUpdatingFromInternal.value = true
178
+ if (Array.isArray(newValue)) {
179
+ // Pour les plages de dates, utiliser la première date
180
+ if (newValue.length > 0) {
181
+ textInputValue.value = formatDate(newValue[0], props.format)
182
+ }
183
+ }
184
+ else {
185
+ // Pour une date unique
186
+ textInputValue.value = formatDate(newValue, props.format)
187
+ }
188
+ }
189
+ finally {
190
+ setTimeout(() => {
191
+ isUpdatingFromInternal.value = false
192
+ }, 0)
193
+ }
194
+ }
195
+ else {
196
+ updateModel(null)
197
+ // Réinitialiser textInputValue
198
+ textInputValue.value = ''
199
+ }
200
+ })
201
+
202
+ const getMessageClasses = () => ({
203
+ 'dp-width': true,
204
+ 'v-messages__message--success': successMessages.value.length > 0,
205
+ 'v-messages__message--error': errorMessages.value.length > 0,
206
+ 'v-messages__message--warning': warningMessages.value.length > 0 && errorMessages.value.length < 1,
207
+ })
208
+
209
+ const inputStyle = computed(() => ({
210
+ 'min-width': '100%',
211
+ }))
212
+
213
+ // Date(s) formatée(s) en chaîne de caractères pour la valeur de retour
214
+ const formattedDate = computed<DateValue>(() => {
215
+ if (!selectedDates.value) return ''
216
+
217
+ const returnFormat = props.dateFormatReturn || props.format
218
+
219
+ if (Array.isArray(selectedDates.value)) {
220
+ if (selectedDates.value.length >= 2) {
221
+ return [
222
+ formatDate(selectedDates.value[0], returnFormat),
223
+ formatDate(selectedDates.value[1], returnFormat),
224
+ ] as [string, string]
225
+ }
226
+ return ''
227
+ }
228
+
229
+ return formatDate(selectedDates.value, returnFormat)
230
+ })
231
+
232
+ watch(formattedDate, (newValue) => {
233
+ if (!newValue || newValue === '') {
234
+ textInputValue.value = ''
235
+ }
236
+ else if (typeof newValue === 'string') {
237
+ // Si on a un format de retour différent, on doit convertir la date
238
+ if (props.dateFormatReturn) {
239
+ const date = parseDate(newValue, props.dateFormatReturn)
240
+ if (date) {
241
+ textInputValue.value = formatDate(date, props.format)
242
+ }
243
+ }
244
+ else {
245
+ textInputValue.value = newValue
246
+ }
247
+ }
248
+ }, { immediate: true })
249
+
250
+ watch(textInputValue, (newValue) => {
251
+ // Éviter les mises à jour récursives
252
+ if (isUpdatingFromInternal.value) return
253
+
254
+ // Parse la date avec le format d'affichage
255
+ const date = parseDate(newValue, props.format)
256
+ if (date) {
257
+ // Si on a un format de retour, formater la date dans ce format
258
+ const formattedValue = props.dateFormatReturn
259
+ ? formatDate(date, props.dateFormatReturn)
260
+ : formatDate(date, props.format)
261
+ updateModel(formattedValue)
262
+
263
+ // Mettre à jour selectedDates sans déclencher de watchers supplémentaires
264
+ try {
265
+ isUpdatingFromInternal.value = true
266
+ selectedDates.value = date
267
+ // Mettre à jour l'affichage formaté
268
+ displayFormattedDate.value = formatDate(date, props.format)
269
+ }
270
+ finally {
271
+ setTimeout(() => {
272
+ isUpdatingFromInternal.value = false
273
+ }, 0)
274
+ }
275
+ }
276
+ else if (newValue) {
277
+ // Même si la date n'est pas valide, conserver la valeur saisie
278
+ // pour éviter que la date ne disparaisse
279
+ updateModel(newValue)
280
+ // Mettre à jour l'affichage formaté pour qu'il corresponde à ce qui est saisi
281
+ try {
282
+ isUpdatingFromInternal.value = true
283
+ displayFormattedDate.value = newValue
284
+ }
285
+ finally {
286
+ setTimeout(() => {
287
+ isUpdatingFromInternal.value = false
288
+ }, 0)
289
+ }
290
+ }
291
+ else {
292
+ updateModel(null)
293
+ // Réinitialiser l'affichage formaté
294
+ try {
295
+ isUpdatingFromInternal.value = true
296
+ displayFormattedDate.value = ''
297
+ selectedDates.value = null
298
+ }
299
+ finally {
300
+ setTimeout(() => {
301
+ isUpdatingFromInternal.value = false
302
+ }, 0)
303
+ }
304
+ }
305
+ })
306
+
307
+ // Date(s) formatée(s) en chaîne de caractères pour l'affichage
308
+ const displayFormattedDateComputed = computed(() => {
309
+ if (!selectedDates.value) return null
310
+
311
+ if (Array.isArray(selectedDates.value)) {
312
+ if (selectedDates.value.length >= 2) {
313
+ return `${formatDate(selectedDates.value[0], props.format)} - ${formatDate(
314
+ selectedDates.value[selectedDates.value.length - 1],
315
+ props.format,
316
+ )}`
317
+ }
318
+ return formatDate(selectedDates.value[0], props.format)
319
+ }
320
+
321
+ return formatDate(selectedDates.value, props.format)
322
+ })
323
+
324
+ watch(displayFormattedDateComputed, (newValue) => {
325
+ if (!props.noCalendar && newValue) {
326
+ displayFormattedDate.value = newValue
327
+ }
328
+ })
329
+
330
+ // Fonction pour mettre à jour displayFormattedDate quand le VDatePicker change
331
+ const updateDisplayFormattedDate = () => {
332
+ if (displayFormattedDateComputed.value) {
333
+ displayFormattedDate.value = displayFormattedDateComputed.value
334
+ }
335
+ }
336
+
337
+ const updateSelectedDates = (input: DateValue) => {
338
+ if (Array.isArray(input)) {
339
+ const dates = input
340
+ .map(date => (date ? parseDate(date, props.format) : null))
341
+ .filter((date): date is Date => date !== null)
342
+
343
+ if (dates.length === 0) {
344
+ selectedDates.value = null
345
+ return
346
+ }
347
+
348
+ selectedDates.value = dates
349
+ return
350
+ }
351
+
352
+ const date = input ? parseDate(input, props.format) : null
353
+ selectedDates.value = date === null ? null : date
354
+ }
355
+
356
+ // Gestionnaire de clic en dehors
357
+ const handleClickOutside = (event: MouseEvent) => {
358
+ if (!isDatePickerVisible.value) return
359
+
360
+ const target = event.target as HTMLElement
361
+ const container = target.closest('.date-picker-container')
362
+
363
+ // Si on clique dans le conteneur du DatePicker, on ne fait rien
364
+ if (container) return
365
+ emit('closed')
366
+ // Déclencher la validation à la fermeture
367
+ validateDates()
368
+ }
369
+
370
+ const todayInString = computed(() => {
371
+ return dayjs().locale('fr').format('dddd D MMMM').replace(/\b\w/g, l => l.toUpperCase())
372
+ })
373
+
374
+ onMounted(() => {
375
+ document.addEventListener('click', handleClickOutside)
376
+
377
+ // Initialiser l'affichage formaté
378
+ if (displayFormattedDateComputed.value) {
379
+ displayFormattedDate.value = displayFormattedDateComputed.value
380
+ }
381
+
382
+ // Valider les dates au montage
383
+ validateDates()
384
+ })
385
+
386
+ onBeforeUnmount(() => {
387
+ document.removeEventListener('click', handleClickOutside)
388
+ })
389
+
390
+ const dateTextInputRef = ref<null | ComponentPublicInstance<typeof DateTextInput>>()
391
+ const dateCalendarTextInputRef = ref<null | ComponentPublicInstance<typeof SyTextField>>()
392
+ const datePickerRef = ref<null | ComponentPublicInstance<typeof VDatePicker>>()
393
+
394
+ const validateOnSubmit = () => {
395
+ if (props.noCalendar) {
396
+ return dateTextInputRef.value?.validateOnSubmit()
397
+ }
398
+ // Forcer la validation pour ignorer les conditions de validation interactive
399
+ validateDates(true)
400
+ // Retourner directement un booléen pour maintenir la compatibilité avec les tests existants
401
+ return errors.value.length === 0
402
+ }
403
+
404
+ const openDatePicker = () => {
405
+ if (!isDatePickerVisible.value) {
406
+ toggleDatePicker()
407
+ }
408
+ }
409
+
410
+ type ViewMode = 'month' | 'year' | 'months' | undefined
411
+
412
+ // Variable pour suivre le mode d'affichage actuel du DatePicker
413
+ const currentViewMode = ref<ViewMode>(props.isBirthDate ? 'year' : 'month')
414
+
415
+ watch(() => props.isBirthDate, (newValue) => {
416
+ currentViewMode.value = newValue ? 'year' : 'month'
417
+ })
418
+
419
+ // Fonction pour gérer le changement de mode d'affichage
420
+ const handleViewModeUpdate = (newMode: ViewMode) => {
421
+ currentViewMode.value = newMode
422
+ }
423
+
424
+ // Fonction pour gérer la sélection de l'année quand isBirthDate est true
425
+ const handleYearUpdate = () => {
426
+ if (props.isBirthDate) {
427
+ // Après la sélection de l'année, passer automatiquement à la sélection du mois
428
+ currentViewMode.value = 'months'
429
+ }
430
+ }
431
+
432
+ // Fonction pour gérer la sélection du mois quand isBirthDate est true
433
+ const handleMonthUpdate = () => {
434
+ if (props.isBirthDate) {
435
+ // Après la sélection du mois, passer automatiquement à la sélection du jour
436
+ currentViewMode.value = undefined
437
+ }
438
+ }
439
+
440
+ const handleInputBlur = () => {
441
+ emit('blur')
442
+ validateDates(true)
443
+ }
444
+
445
+ watch(isDatePickerVisible, async (isVisible) => {
446
+ if (!isVisible && props.isBirthDate) {
447
+ // Réinitialiser le mode d'affichage au type birthdate
448
+ currentViewMode.value = 'year'
449
+ }
450
+
451
+ if (isVisible) {
452
+ // set the focus on the date picker
453
+ await nextTick()
454
+ const firstButton = datePickerRef.value?.$el.querySelector('button')
455
+ if (firstButton) {
456
+ firstButton.focus()
457
+ }
458
+ }
459
+ else {
460
+ // set the focus on the text input
461
+ // wait for VMenu to finish DOM updates & transition
462
+ setTimeout(() => {
463
+ requestAnimationFrame(() => {
464
+ const inputElement = dateCalendarTextInputRef.value?.$el?.querySelector('input')
465
+ if (inputElement) {
466
+ inputElement.focus()
467
+ isDatePickerVisible.value = false
468
+ }
469
+ })
470
+ }, 0)
471
+ }
472
+ })
473
+
474
+ const getIcon = () => {
475
+ if (props.noCalendar || props.disableErrorHandling) {
476
+ return
477
+ }
478
+ switch (true) {
479
+ case errorMessages.value.length > 0:
480
+ return 'error'
481
+ case warningMessages.value.length > 0:
482
+ return 'warning'
483
+ case successMessages.value.length > 0:
484
+ return 'success'
485
+ default:
486
+ return
487
+ }
488
+ }
489
+
490
+ const syncFromModelValue = (newValue: DateInput | undefined) => {
491
+ if (!newValue || newValue === '') {
492
+ selectedDates.value = null
493
+ textInputValue.value = ''
494
+ displayFormattedDate.value = ''
495
+ validateDates()
496
+ return
497
+ }
498
+
499
+ selectedDates.value = initializeSelectedDates(newValue, props.format, props.dateFormatReturn)
500
+
501
+ if (selectedDates.value) {
502
+ const firstDate = Array.isArray(selectedDates.value)
503
+ ? selectedDates.value[0]
504
+ : selectedDates.value
505
+
506
+ textInputValue.value = formatDate(firstDate, props.format)
507
+ displayFormattedDate.value = displayFormattedDateComputed.value || ''
508
+ }
509
+
510
+ validateDates()
511
+ }
512
+
513
+ watch(() => props.modelValue, (newValue) => {
514
+ if (isUpdatingFromInternal.value) {
515
+ if (props.displayRange) {
516
+ if (Array.isArray(newValue) && newValue.length >= 2) {
517
+ isDatePickerVisible.value = false
518
+ emit('closed')
519
+ }
520
+ }
521
+ else {
522
+ isDatePickerVisible.value = false
523
+ emit('closed')
524
+ }
525
+ return
526
+ }
527
+
528
+ try {
529
+ isUpdatingFromInternal.value = true
530
+ syncFromModelValue(newValue)
531
+ }
532
+ finally {
533
+ setTimeout(() => {
534
+ isUpdatingFromInternal.value = false
535
+ }, 0)
536
+ }
537
+ }, { immediate: true })
538
+
539
+ const toggleDatePicker = () => {
540
+ if (props.disabled || props.readonly) return
541
+
542
+ isDatePickerVisible.value = !isDatePickerVisible.value
543
+
544
+ if (isDatePickerVisible.value) {
545
+ // Mettre à jour l'accessibilité après l'ouverture du DatePicker
546
+ nextTick(() => {
547
+ updateAccessibility()
548
+ })
549
+ }
550
+ else {
551
+ emit('closed')
552
+ validateDates()
553
+ }
554
+ }
555
+
556
+ const openDatePickerOnClick = () => {
557
+ if (props.textFieldActivator) {
558
+ openDatePicker()
559
+ }
560
+ }
561
+
562
+ const openDatePickerOnFocus = () => {
563
+ // Only open the DatePicker if textFieldActivator is true
564
+ if (props.textFieldActivator) {
565
+ openDatePicker()
566
+ }
567
+ // Always emit the focus event
568
+ emit('focus')
569
+ }
570
+
571
+ const openDatePickerOnIconClick = () => {
572
+ toggleDatePicker()
573
+ }
574
+
575
+ defineExpose({
576
+ validateOnSubmit,
577
+ isDatePickerVisible,
578
+ selectedDates,
579
+ errorMessages,
580
+ handleClickOutside,
581
+ initializeSelectedDates,
582
+ updateAccessibility,
583
+ openDatePicker,
584
+ })
585
+ </script>
586
+
587
+ <template>
588
+ <div
589
+ class="date-picker-container"
590
+ :style="inputStyle"
591
+ >
592
+ <template v-if="props.noCalendar">
593
+ <DateTextInput
594
+ ref="dateTextInputRef"
595
+ v-model="textInputValue"
596
+ :class="[getMessageClasses(), 'label-hidden-on-focus']"
597
+ :date-format-return="props.dateFormatReturn"
598
+ :format="props.format"
599
+ :label="props.placeholder"
600
+ :placeholder="props.placeholder"
601
+ :required="props.required"
602
+ :custom-rules="props.customRules"
603
+ :custom-warning-rules="props.customWarningRules"
604
+ :disabled="props.disabled"
605
+ :readonly="props.readonly"
606
+ :is-outlined="props.isOutlined"
607
+ :display-icon="props.displayIcon"
608
+ :display-append-icon="props.displayAppendIcon"
609
+ :display-prepend-icon="props.displayPrependIcon"
610
+ :no-icon="props.noIcon"
611
+ :disable-error-handling="props.disableErrorHandling"
612
+ :show-success-messages="props.showSuccessMessages"
613
+ :bg-color="props.bgColor"
614
+ title="Date text input"
615
+ @focus="emit('focus')"
616
+ @blur="emit('blur')"
617
+ />
618
+ </template>
619
+ <template v-else>
620
+ <VMenu
621
+ v-if="!props.noCalendar"
622
+ v-model="isDatePickerVisible"
623
+ activator="parent"
624
+ :min-width="0"
625
+ location="bottom"
626
+ :close-on-content-click="false"
627
+ :open-on-click="false"
628
+ scroll-strategy="none"
629
+ transition="fade-transition"
630
+ attach="body"
631
+ :offset="[-20, 5]"
632
+ >
633
+ <template #activator="{ props: menuProps }">
634
+ <SyTextField
635
+ v-bind="menuProps"
636
+ ref="dateCalendarTextInputRef"
637
+ v-model="displayFormattedDate"
638
+ :append-icon="displayIcon && displayAppendIcon ? 'calendar' : undefined"
639
+ :append-inner-icon="getIcon()"
640
+ :class="[getMessageClasses(), 'label-hidden-on-focus']"
641
+ :error-messages="errorMessages"
642
+ :warning-messages="warningMessages"
643
+ :success-messages="props.showSuccessMessages ? successMessages : []"
644
+ :disabled="props.disabled"
645
+ :disable-click-button="false"
646
+ :readonly="true"
647
+ :label="props.placeholder"
648
+ :no-icon="props.noIcon"
649
+ :prepend-icon="displayIcon && !displayAppendIcon ? 'calendar' : undefined"
650
+ :variant-style="props.isOutlined ? 'outlined' : 'underlined'"
651
+ color="primary"
652
+ :show-success-messages="props.showSuccessMessages"
653
+ :bg-color="props.bgColor"
654
+ is-clearable
655
+ title="Date Picker"
656
+ @click="openDatePickerOnClick"
657
+ @focus="openDatePickerOnFocus"
658
+ @blur="handleInputBlur"
659
+ @update:model-value="updateSelectedDates"
660
+ @prepend-icon-click="openDatePickerOnIconClick"
661
+ @append-icon-click="openDatePickerOnIconClick"
662
+ />
663
+ </template>
664
+ <VDatePicker
665
+ v-if="isDatePickerVisible && !props.noCalendar"
666
+ ref="datePickerRef"
667
+ v-model="selectedDates"
668
+ color="primary"
669
+ :first-day-of-week="1"
670
+ :multiple="props.displayRange ? 'range' : false"
671
+ :show-adjacent-months="true"
672
+ :show-week="props.showWeekNumber"
673
+ :view-mode="currentViewMode"
674
+ @update:view-mode="handleViewModeUpdate"
675
+ @update:year="handleYearUpdate"
676
+ @update:month="handleMonthUpdate"
677
+ @update:model-value="updateDisplayFormattedDate"
678
+ >
679
+ <template #title>
680
+ Sélectionnez une date
681
+ </template>
682
+ <template #header>
683
+ <h3 class="mx-auto my-auto ml-5 mb-4">
684
+ {{ todayInString }}
685
+ </h3>
686
+ </template>
687
+ </VDatePicker>
688
+ </VMenu>
689
+ </template>
690
+ </div>
691
+ </template>
692
+
693
+ <style lang="scss" scoped>
694
+ @use '@/assets/tokens';
695
+
696
+ .label-hidden-on-focus:focus + label {
697
+ display: none;
698
+ }
699
+
700
+ .dp-width {
701
+ width: v-bind('props.width');
702
+ }
703
+
704
+ .v-messages__message--success {
705
+ :deep(.v-input__control),
706
+ :deep(.v-messages__message) {
707
+ color: tokens.$colors-text-success !important;
708
+
709
+ --v-medium-emphasis-opacity: 1;
710
+ }
711
+
712
+ .v-field--active & {
713
+ color: tokens.$colors-border-success !important;
714
+ }
715
+ }
716
+
717
+ .v-messages__message--error {
718
+ :deep(.v-input__control),
719
+ :deep(.v-messages__message) {
720
+ color: tokens.$colors-text-error !important;
721
+ }
722
+
723
+ .v-field--active & {
724
+ color: tokens.$colors-border-error !important;
725
+ }
726
+ }
727
+
728
+ .v-messages__message--warning {
729
+ :deep(.v-input__control) {
730
+ color: tokens.$colors-text-warning !important;
731
+
732
+ --v-medium-emphasis-opacity: 1;
733
+ }
734
+
735
+ :deep(.v-messages__message) {
736
+ color: tokens.$colors-text-warning !important;
737
+ }
738
+
739
+ .v-field--active & {
740
+ color: tokens.$colors-text-warning !important;
741
+ }
742
+ }
743
+
744
+ :deep(.v-btn__content) {
745
+ font-size: tokens.$font-size-body-text + 3;
746
+ font-weight: bold;
747
+ }
748
+
749
+ :deep(.v-messages) {
750
+ opacity: 1;
751
+ }
752
+
753
+ :deep(.v-field--dirty) {
754
+ opacity: 1 !important;
755
+
756
+ --v-medium-emphasis-opacity: 1;
757
+ }
758
+
759
+ :deep(.v-field--focused) {
760
+ opacity: 1 !important;
761
+
762
+ --v-medium-emphasis-opacity: 1;
763
+ }
764
+
765
+ .date-picker-container {
766
+ max-width: 100%;
767
+ position: relative;
768
+
769
+ :deep(.v-date-picker) {
770
+ max-width: 445px;
771
+ position: absolute;
772
+ top: 56px;
773
+ left: 0;
774
+ z-index: 2;
775
+ box-shadow:
776
+ 0 5px 5px -3px rgb(0 0 0 / 20%),
777
+ 0 8px 10px 1px rgb(0 0 0 / 14%),
778
+ 0 3px 14px 2px rgb(0 0 0 / 12%) !important;
779
+ }
780
+ }
781
+
782
+ :deep(.v-date-picker-month__day--selected, .v-date-picker-month__day--adjacent) {
783
+ opacity: 1;
784
+ }
785
+
786
+ .fade-enter-active,
787
+ .fade-leave-active {
788
+ transition: opacity 0.5s ease;
789
+ }
790
+
791
+ .fade-enter-from,
792
+ .fade-leave-to {
793
+ opacity: 0;
794
+ }
795
+ </style>