Sous-composants internes (CalendarHeader, MonthGrid, MonthPicker), composant public Date.vue, tests d'intégration, story Histoire et page playground. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
94 lines
2.9 KiB
Vue
94 lines
2.9 KiB
Vue
<template>
|
|
<div data-test="month-grid">
|
|
<div class="grid grid-cols-8">
|
|
<div class="flex h-8 items-center justify-center text-xs font-medium uppercase text-m-muted">
|
|
Sem
|
|
</div>
|
|
<div
|
|
v-for="d in dayLabels"
|
|
:key="d"
|
|
class="flex h-8 items-center justify-center text-xs font-medium uppercase text-m-muted"
|
|
>
|
|
{{ d }}
|
|
</div>
|
|
|
|
<template
|
|
v-for="week in weeks"
|
|
:key="week.days[0].isoDate"
|
|
>
|
|
<div
|
|
data-test="week-number"
|
|
class="flex h-10 items-center justify-center bg-m-primary/10 text-sm text-m-primary/70"
|
|
>
|
|
{{ week.weekNumber }}
|
|
</div>
|
|
<button
|
|
v-for="cell in week.days"
|
|
:key="cell.isoDate"
|
|
type="button"
|
|
data-test="day"
|
|
:data-iso="cell.isoDate"
|
|
:disabled="!inRange(cell.isoDate)"
|
|
:aria-label="ariaLabel(cell)"
|
|
:aria-disabled="!inRange(cell.isoDate)"
|
|
class="flex h-10 w-10 items-center justify-center text-sm transition-colors duration-100"
|
|
:class="cellClass(cell)"
|
|
@click="onSelect(cell.isoDate)"
|
|
>
|
|
{{ cell.day }}
|
|
</button>
|
|
</template>
|
|
</div>
|
|
</div>
|
|
</template>
|
|
|
|
<script setup lang="ts">
|
|
import {toRef} from 'vue'
|
|
import {useMonthMatrix, type DayCell} from '../composables/useMonthMatrix'
|
|
import {isDateInRange} from '../composables/dateFormat'
|
|
|
|
defineOptions({name: 'MalioDateMonthGrid'})
|
|
|
|
const props = withDefaults(
|
|
defineProps<{
|
|
month: number
|
|
year: number
|
|
selectedDate?: string | null
|
|
min?: string
|
|
max?: string
|
|
}>(),
|
|
{selectedDate: null, min: undefined, max: undefined},
|
|
)
|
|
|
|
const emit = defineEmits<{(e: 'select', iso: string): void}>()
|
|
|
|
const dayLabels = ['Lun', 'Mar', 'Mer', 'Jeu', 'Ven', 'Sam', 'Dim']
|
|
const monthsLong = ['janvier', 'février', 'mars', 'avril', 'mai', 'juin',
|
|
'juillet', 'août', 'septembre', 'octobre', 'novembre', 'décembre']
|
|
|
|
const {weeks} = useMonthMatrix(toRef(props, 'month'), toRef(props, 'year'))
|
|
|
|
const inRange = (iso: string) => isDateInRange(iso, props.min, props.max)
|
|
|
|
const ariaLabel = (cell: DayCell) => {
|
|
const [, m, d] = cell.isoDate.split('-')
|
|
return `${Number(d)} ${monthsLong[Number(m) - 1]} ${cell.isoDate.slice(0, 4)}`
|
|
}
|
|
|
|
const cellClass = (cell: DayCell) => {
|
|
const selected = props.selectedDate === cell.isoDate
|
|
if (!inRange(cell.isoDate)) return 'text-m-muted/30 cursor-not-allowed'
|
|
if (selected) return 'bg-m-primary text-white font-medium rounded-full'
|
|
const parts = ['cursor-pointer hover:bg-m-primary/10 rounded-full']
|
|
if (cell.isToday) parts.push('border border-m-primary text-m-primary font-semibold')
|
|
else if (cell.isCurrentMonth) parts.push('text-black')
|
|
else parts.push('text-m-muted/50')
|
|
return parts.join(' ')
|
|
}
|
|
|
|
const onSelect = (iso: string) => {
|
|
if (!inRange(iso)) return
|
|
emit('select', iso)
|
|
}
|
|
</script>
|