DatePicker.vue 2.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134
  1. <!--
  2. Sélecteur de dates avec Vuetify
  3. @see https://vuetifyjs.com/en/components/date-pickers/
  4. -->
  5. <template>
  6. <v-layout row wrap>
  7. <v-menu
  8. ref="menu"
  9. v-model="menu"
  10. :close-on-content-click="false"
  11. :nudge-right="40"
  12. :return-value.sync="modelValue"
  13. lazy
  14. transition="scale-transition"
  15. offset-y
  16. :max-width="290"
  17. :min-width="290"
  18. :position-x="positionX"
  19. :position-y="positionY"
  20. >
  21. <template v-slot:activator="{ on, attrs }">
  22. <v-text-field
  23. v-model="displayDate"
  24. :label="label"
  25. :readonly="readOnly"
  26. v-bind="attrs"
  27. v-on="on"
  28. @blur="menu = false"
  29. ></v-text-field>
  30. </template>
  31. <v-date-picker
  32. v-if="!withTime"
  33. :model-value="modelValue"
  34. :locale="i18n.locale.value"
  35. @update:model-value="updateDate"
  36. no-title
  37. scrollable
  38. >
  39. </v-date-picker>
  40. <v-date-picker
  41. v-else
  42. :model-value="modelValue"
  43. :locale="i18n.locale.value"
  44. @update:model-value="updateDate"
  45. no-title
  46. scrollable
  47. type="datetime"
  48. >
  49. </v-date-picker>
  50. </v-menu>
  51. </v-layout>
  52. </template>
  53. <script setup lang="ts">
  54. import { ref, computed, nextTick, watch } from 'vue'
  55. import { useI18n } from 'vue-i18n'
  56. const props = defineProps({
  57. modelValue: Date,
  58. label: String,
  59. readOnly: {
  60. type: Boolean,
  61. default: false,
  62. },
  63. format: {
  64. type: String,
  65. default: null,
  66. },
  67. withTime: {
  68. type: Boolean,
  69. default: false,
  70. },
  71. /**
  72. * Position du date-picker
  73. * @see https://vuetifyjs.com/en/api/v-menu/#props-position
  74. */
  75. position: {
  76. type: String as PropType<'left' | 'center' | 'right'>,
  77. default: 'center',
  78. },
  79. })
  80. const emit = defineEmits(['update:modelValue'])
  81. const i18n = useI18n()
  82. const menu = ref(false)
  83. const positionX = ref(0)
  84. const positionY = ref(0)
  85. const displayDate = computed({
  86. get: () => {
  87. if (!props.modelValue) return ''
  88. if (props.format) {
  89. return new Intl.DateTimeFormat(i18n.locale.value, {
  90. year: 'numeric',
  91. month: '2-digit',
  92. day: '2-digit',
  93. hour: props.withTime ? '2-digit' : undefined,
  94. minute: props.withTime ? '2-digit' : undefined,
  95. }).format(props.modelValue)
  96. }
  97. return props.modelValue.toLocaleDateString(i18n.locale.value)
  98. },
  99. set: () => {},
  100. })
  101. function updateDate(value) {
  102. emit('update:modelValue', value)
  103. menu.value = false
  104. }
  105. function updatePosition() {
  106. nextTick(() => {
  107. const activator = document.querySelector('.v-menu__activator')
  108. if (activator) {
  109. const rect = activator.getBoundingClientRect()
  110. positionX.value = rect.left
  111. positionY.value = rect.bottom
  112. }
  113. })
  114. }
  115. watch(menu, (val) => {
  116. if (val) updatePosition()
  117. })
  118. </script>
  119. <style scoped>
  120. .v-menu__content {
  121. position: absolute !important;
  122. }
  123. </style>