import type { LeimDate, LeimPeriod, LeimPeriodShort } from '@/models/Date' import { isDigit, isInt, isSignedInt } from '@/utils/Regex' import { useUrlSearchParams } from '@vueuse/core' import { defineStore } from 'pinia' import { computed, ref, type ComputedRef, type Ref } from 'vue' type CalendarViewType = 'month' | 'year' | 'decade' | 'century' type CalendarStaticConfig = { months: string[] monthsPerYear: number daysPerYear: number daysPerMonth: number daysPerWeek: number } type CalendarCurrentConfig = { viewType: CalendarViewType } type CalendarCurrentDate = { currentDay: ComputedRef currentMonth: ComputedRef currentMonthName: ComputedRef currentYear: ComputedRef currentPeriod: Ref currentPeriodAbbr: Ref currentDateTitle: ComputedRef } export const useCalendar = defineStore('calendar', () => { /** * Static calendar config * This shouldn't change */ /** * Month list */ const months = [ 'Jalen', 'Malsen', 'Verlys', 'Nalys', 'Verdore', 'Sidore', 'Lyllion', 'Rion', 'Farene', 'Dalvene' ] const monthsPerYear = computed(() => months.length) const daysPerYear = 320 const daysPerMonth = computed(() => daysPerYear / monthsPerYear.value) const daysPerWeek = 6 // Assign the static config const staticConfig: CalendarStaticConfig = { months, monthsPerYear: monthsPerYear.value, daysPerYear, daysPerMonth: daysPerMonth.value, daysPerWeek } const currentConfig: Ref = ref({ viewType: 'month' }) const viewTypeOptions: Set = new Set([ 'month', 'year', 'decade', 'century' ]) // Get date from URL params const params = useUrlSearchParams('history', { write: true }) // Default date settings (current day) const defaultDay = 12 const defaultMonth = 7 const defaultYear = 3209 const defaultDate: ComputedRef = computed(() => { return { day: defaultDay, month: defaultMonth, year: defaultYear, period: 'nante' } }) // Assign default values if no params exist in URL if (!params.day || typeof params.day === 'object' || !isInt(params.day)) { params.day = defaultDay.toString() } if (!params.month || typeof params.month === 'object' || !isDigit(params.month)) { params.month = defaultMonth.toString() } if (!params.year || typeof params.year === 'object' || !isSignedInt(params.year)) { params.year = defaultYear.toString() } const currentDay = computed(() => { return Number(params.day) }) const currentMonth = computed(() => { return Number(params.month) }) // Gets the label from currentMonth index const currentMonthName = computed(() => getMonthName(currentMonth.value)) const currentYear = computed(() => { return Number(params.year) }) // Get period from currentYear const currentPeriod: ComputedRef = computed( () => getPeriodOfYear(currentYear.value).long ) const currentPeriodAbbr: ComputedRef = computed( () => getPeriodOfYear(currentYear.value).short ) const currentDateTitle = computed(() => { switch (currentConfig.value.viewType) { case 'month': return getFormattedDateTitle({ day: currentDate.currentDay.value, month: currentDate.currentMonth.value, year: currentDate.currentYear.value, period: currentDate.currentPeriod.value }) case 'year': return `Année ${currentYear.value} ${currentPeriodAbbr.value}` case 'decade': return `Années ${currentYear.value} ${getPeriodOfYear(currentYear.value).short} - ${currentYear.value + 10} ${getPeriodOfYear(currentYear.value + 10).short}` case 'century': return `Années ${currentYear.value} ${getPeriodOfYear(currentYear.value).short} - ${currentYear.value + 100} ${getPeriodOfYear(currentYear.value + 100).short}` default: return 'Date inconnue' } }) // Create base config const currentDate: CalendarCurrentDate = { currentDay, currentMonth, currentMonthName, currentYear, currentPeriod, currentPeriodAbbr, currentDateTitle } /** * Check whether the current viewType is active */ const isCurrentScreenActive = computed(() => { switch (currentConfig.value.viewType) { case 'month': return ( defaultDate.value.month === currentDate.currentMonth.value && defaultDate.value.year === currentDate.currentYear.value ) case 'year': case 'decade': case 'century': return defaultDate.value.year === currentDate.currentYear.value default: return false } }) /** * Moves the current date forward one month */ function incrementMonth() { let newValue = Number(params.month) + 1 // If the new value would exceed the max number of month per year if (newValue >= staticConfig.monthsPerYear) { newValue = 0 // Increment the year incrementYear() } params.month = newValue.toString() } /** * Moves the current date backward one month */ function decrementMonth() { let newValue = Number(params.month) - 1 // If the new value would go below 0 if (newValue < 0) { newValue = staticConfig.monthsPerYear - 1 // Decrement the year decrementYear() } params.month = newValue.toString() } /** * Moves the current date to a particular month */ function setMonth(target: number) { // If the target is outside the month bounds if (target < 0 || target >= staticConfig.monthsPerYear) { return } params.month = target.toString() } /** * Moves the current date forward one year */ function incrementYear(inc: number = 1) { const newValue = Number(params.year) + inc params.year = newValue.toString() } /** * Moves the current date backward one year */ function decrementYear(inc: number = 1) { const newValue = Number(params.year) - inc params.year = newValue.toString() } /** * From a given year, returns a set of LeimPeriod identifier * * This is used in range use-cases * * @param year The year to display * @returns An object containing both short and long LeimPeriod */ function getPeriodOfYear(year: string | number): { long: LeimPeriod; short: LeimPeriodShort } { const numYear = year as number return { long: numYear >= 0 ? 'nante' : 'ante', short: numYear >= 0 ? 'N.R' : 'A.R' } } /** * Get the formatted month name given its index * * @param monthNumber Index of the month * @returns Formatted month name */ function getMonthName(monthNumber: number): string { const index = Number(monthNumber) return staticConfig.months[index] } /** * Switches the active viewType * * @param viewType Target viewType */ function setViewType(viewType: CalendarViewType): void { currentConfig.value.viewType = viewType } /** * Gets the formatted viewType title * * @param viewType Target viewType * @returns Formatted mode title */ function getViewTypeTitle(viewType: CalendarViewType): string { switch (viewType) { case 'year': return 'Année' case 'decade': return 'Décennie' case 'century': return 'Siècle' case 'month': default: return 'Mois' } } /** * From a LeimDate, returns the legible date title * * @param date Date target * @param showNumber Should the date show the day number * @returns Formatted date name */ function getFormattedDateTitle(date: LeimDate, showNumber?: boolean): string { if (showNumber) { return `${date.day} ${getMonthName(date.month)} ${date.year} ${getPeriodOfYear(date.year).short}` } return `${getMonthName(date.month)} ${date.year} ${getPeriodOfYear(date.year).short}` } /** * Check whether two dates are identical * * @param date1 First date * @param date2 Second date * @returns True if the dates are identical */ function compareTwoDates(date1: LeimDate, date2: LeimDate) { // To refacto to be more precise return JSON.stringify({ ...date1 }) === JSON.stringify({ ...date2 }) } /** * Jumps the calendar to the given date * * @param date Target date */ function jumpToDate(date: LeimDate) { params.day = date.day.toString() params.month = date.month.toString() params.year = date.year.toString() } /** * Jump the calendar to the default date */ function jumpToDefaultDate() { jumpToDate(defaultDate.value) currentConfig.value.viewType = 'month' } return { staticConfig, viewTypeOptions, currentConfig, currentDate, defaultDate, params, getPeriodOfYear, incrementMonth, decrementMonth, setMonth, incrementYear, decrementYear, jumpToDate, jumpToDefaultDate, compareTwoDates, getFormattedDateTitle, getMonthName, setViewType, getViewTypeTitle, isCurrentScreenActive } })