|
|
@@ -1,3 +1,9 @@
|
|
|
+<!--
|
|
|
+Sélecteur de dates avec Vuetify
|
|
|
+
|
|
|
+@see https://vuetifyjs.com/en/components/date-pickers/
|
|
|
+-->
|
|
|
+
|
|
|
<template>
|
|
|
<v-layout row wrap>
|
|
|
<v-menu
|
|
|
@@ -9,27 +15,38 @@
|
|
|
lazy
|
|
|
transition="scale-transition"
|
|
|
offset-y
|
|
|
- full-width
|
|
|
- min-width="290px"
|
|
|
+ :max-width="290"
|
|
|
+ :min-width="290"
|
|
|
+ :position-x="positionX"
|
|
|
+ :position-y="positionY"
|
|
|
>
|
|
|
<template v-slot:activator="{ on, attrs }">
|
|
|
<v-text-field
|
|
|
v-model="displayDate"
|
|
|
:label="label"
|
|
|
- readonly
|
|
|
+ :readonly="readOnly"
|
|
|
+ v-bind="attrs"
|
|
|
v-on="on"
|
|
|
@blur="menu = false"
|
|
|
></v-text-field>
|
|
|
</template>
|
|
|
<v-date-picker
|
|
|
- color="primary"
|
|
|
+ v-if="!withTime"
|
|
|
+ :model-value="modelValue"
|
|
|
+ :locale="i18n.locale.value"
|
|
|
+ @update:model-value="updateDate"
|
|
|
+ no-title
|
|
|
+ scrollable
|
|
|
+ >
|
|
|
+ </v-date-picker>
|
|
|
+ <v-date-picker
|
|
|
+ v-else
|
|
|
:model-value="modelValue"
|
|
|
:locale="i18n.locale.value"
|
|
|
@update:model-value="updateDate"
|
|
|
no-title
|
|
|
scrollable
|
|
|
- @change="menu = false"
|
|
|
-
|
|
|
+ type="datetime"
|
|
|
>
|
|
|
</v-date-picker>
|
|
|
</v-menu>
|
|
|
@@ -37,29 +54,81 @@
|
|
|
</template>
|
|
|
|
|
|
<script setup lang="ts">
|
|
|
-import { ref, computed } from 'vue'
|
|
|
+import { ref, computed, nextTick, watch } from 'vue'
|
|
|
import { useI18n } from 'vue-i18n'
|
|
|
|
|
|
const props = defineProps({
|
|
|
modelValue: Date,
|
|
|
label: String,
|
|
|
+ readOnly: {
|
|
|
+ type: Boolean,
|
|
|
+ default: false,
|
|
|
+ },
|
|
|
+ format: {
|
|
|
+ type: String,
|
|
|
+ default: null,
|
|
|
+ },
|
|
|
+ withTime: {
|
|
|
+ type: Boolean,
|
|
|
+ default: false,
|
|
|
+ },
|
|
|
+ /**
|
|
|
+ * Position du date-picker
|
|
|
+ * @see https://vuetifyjs.com/en/api/v-menu/#props-position
|
|
|
+ */
|
|
|
+ position: {
|
|
|
+ type: String as PropType<'left' | 'center' | 'right'>,
|
|
|
+ default: 'center',
|
|
|
+ },
|
|
|
})
|
|
|
|
|
|
const emit = defineEmits(['update:modelValue'])
|
|
|
|
|
|
const i18n = useI18n()
|
|
|
const menu = ref(false)
|
|
|
+const positionX = ref(0)
|
|
|
+const positionY = ref(0)
|
|
|
|
|
|
const displayDate = computed({
|
|
|
- get: () =>
|
|
|
- props.modelValue
|
|
|
- ? props.modelValue.toLocaleDateString(i18n.locale.value)
|
|
|
- : '',
|
|
|
- set: (val) => {},
|
|
|
+ get: () => {
|
|
|
+ if (!props.modelValue) return ''
|
|
|
+ if (props.format) {
|
|
|
+ return new Intl.DateTimeFormat(i18n.locale.value, {
|
|
|
+ year: 'numeric',
|
|
|
+ month: '2-digit',
|
|
|
+ day: '2-digit',
|
|
|
+ hour: props.withTime ? '2-digit' : undefined,
|
|
|
+ minute: props.withTime ? '2-digit' : undefined,
|
|
|
+ }).format(props.modelValue)
|
|
|
+ }
|
|
|
+ return props.modelValue.toLocaleDateString(i18n.locale.value)
|
|
|
+ },
|
|
|
+ set: () => {},
|
|
|
})
|
|
|
|
|
|
function updateDate(value) {
|
|
|
emit('update:modelValue', value)
|
|
|
menu.value = false
|
|
|
}
|
|
|
+
|
|
|
+function updatePosition() {
|
|
|
+ nextTick(() => {
|
|
|
+ const activator = document.querySelector('.v-menu__activator')
|
|
|
+ if (activator) {
|
|
|
+ const rect = activator.getBoundingClientRect()
|
|
|
+ positionX.value = rect.left
|
|
|
+ positionY.value = rect.bottom
|
|
|
+ }
|
|
|
+ })
|
|
|
+}
|
|
|
+
|
|
|
+watch(menu, (val) => {
|
|
|
+ if (val) updatePosition()
|
|
|
+})
|
|
|
</script>
|
|
|
+
|
|
|
+<style scoped>
|
|
|
+.v-menu__content {
|
|
|
+ position: absolute !important;
|
|
|
+}
|
|
|
+</style>
|