DatePicker.vue 3.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150
  1. <!--
  2. Sélecteur de dates, à placer dans un composant `UiForm`
  3. -->
  4. <template>
  5. <main>
  6. <div class="d-flex flex-column container">
  7. <span class="label">
  8. {{ $t(fieldLabel) }}
  9. </span>
  10. <UiDatePicker
  11. :model-value="date"
  12. :readonly="readonly"
  13. class="date-picker"
  14. @update:model-value="onUpdate($event)"
  15. />
  16. <span v-if="error || !!fieldViolations" class="theme-danger">
  17. {{ errorMessage || fieldViolations ? $t(fieldViolations) : '' }}
  18. </span>
  19. </div>
  20. </main>
  21. </template>
  22. <script setup lang="ts">
  23. import { formatISO } from 'date-fns'
  24. import type { PropType, Ref } from 'vue'
  25. import { ref } from 'vue'
  26. import { useFieldViolation } from '~/composables/form/useFieldViolation'
  27. const props = defineProps({
  28. /**
  29. * v-model
  30. */
  31. modelValue: {
  32. type: String as PropType<Date | string | null>,
  33. required: false,
  34. default: null,
  35. },
  36. /**
  37. * Nom de la propriété d'une entité lorsque l'input concerne cette propriété
  38. * - Utilisé par la validation
  39. * - Laisser null si le champ ne s'applique pas à une entité
  40. */
  41. field: {
  42. type: String,
  43. required: false,
  44. default: null,
  45. },
  46. /**
  47. * Label du champ
  48. * Si non défini, c'est le nom de propriété qui est utilisé
  49. */
  50. label: {
  51. type: String,
  52. required: false,
  53. default: null,
  54. },
  55. /**
  56. * Définit si le champ est en lecture seule
  57. */
  58. readonly: {
  59. type: Boolean,
  60. required: false,
  61. },
  62. /**
  63. * Règles de validation
  64. * @see https://vuetify.cn/en/components/forms/#validation-with-submit-clear
  65. */
  66. rules: {
  67. type: Array,
  68. required: false,
  69. default: () => [],
  70. },
  71. /**
  72. * Le champ est-il actuellement en état d'erreur
  73. */
  74. error: {
  75. type: Boolean,
  76. required: false,
  77. },
  78. /**
  79. * Si le champ est en état d'erreur, quel est le message d'erreur?
  80. */
  81. errorMessage: {
  82. type: String,
  83. required: false,
  84. default: null,
  85. },
  86. })
  87. const { fieldViolations, updateViolationState } = useFieldViolation(props.field)
  88. const fieldLabel = props.label ?? props.field
  89. const emit = defineEmits(['update:model-value', 'change'])
  90. const date: Ref<Date | undefined> = ref(
  91. props.modelValue ? new Date(props.modelValue) : undefined,
  92. )
  93. const onUpdate = (event: string) => {
  94. console.log(event)
  95. updateViolationState(event)
  96. date.value = event ? new Date(event) : undefined
  97. emit('update:model-value', date.value ? formatISO(date.value) : undefined)
  98. }
  99. </script>
  100. <style scoped lang="scss">
  101. .container {
  102. position: relative;
  103. }
  104. .label {
  105. position: absolute;
  106. color: #8e8e8e;
  107. top: -0.7rem;
  108. left: 0.75rem;
  109. background-color: rgb(var(--v-theme-surface));
  110. padding: 0 0.3rem;
  111. font-size: 0.8rem;
  112. z-index: 1;
  113. transition:
  114. color 0.2s,
  115. font-size 0.2s,
  116. top 0.2s;
  117. }
  118. .date-picker:hover {
  119. :deep(.dp__input) {
  120. border-color: #333333;
  121. }
  122. }
  123. .container:focus-within {
  124. .label {
  125. color: #333333;
  126. }
  127. :deep(.dp__input_focus) {
  128. border: solid 2px #333333;
  129. }
  130. :deep(.dp__input_icon) {
  131. color: #333333;
  132. }
  133. }
  134. </style>