Support Monday/Sunday day of week

This commit is contained in:
Zachary
2024-11-13 14:38:22 +01:00
parent a6402654a1
commit 99f76a9a99
2 changed files with 85 additions and 21 deletions

View File

@@ -6,10 +6,33 @@
export let holidays = []; export let holidays = [];
export let optimizedDaysOff = []; export let optimizedDaysOff = [];
export let consecutiveDaysOff = []; export let consecutiveDaysOff = [];
export let selectedCountryCode;
// Function to determine the first day of the week based on locale
function getFirstDayOfWeek(locale) {
// Convert 'us' to proper locale format
const normalizedLocale = locale.toLowerCase() === 'us' ? 'en-US' : `en-${locale.toUpperCase()}`;
try {
// Try to get firstDay from Intl.Locale weekInfo
// @ts-ignore .weekInfo exists on all browsers except Firefox
const weekFirstDay = new Intl.Locale(normalizedLocale)?.weekInfo?.firstDay;
if (weekFirstDay !== undefined) {
return weekFirstDay;
}
} catch (e) {
// Fallback if weekInfo is not supported
}
// Fallback: US starts on Sunday (0), most others on Monday (1)
return normalizedLocale === 'en-US' ? 0 : 1;
}
// Reactive declarations // Reactive declarations
$: daysInMonth = getDaysInMonth(year, month); $: daysInMonth = getDaysInMonth(year, month);
$: firstDay = getFirstDayOfMonth(year, month); $: locale = selectedCountryCode ? new Intl.Locale(selectedCountryCode).toString() : 'us';
$: firstDayOfWeek = getFirstDayOfWeek(locale);
$: adjustedFirstDay = (getFirstDayOfMonth(year, month) - firstDayOfWeek + 7) % 7;
function getDaysInMonth(year, month) { function getDaysInMonth(year, month) {
return new Date(year, month + 1, 0).getDate(); return new Date(year, month + 1, 0).getDate();
@@ -58,15 +81,45 @@
return date >= start && date <= end; return date >= start && date <= end;
}); });
} }
// Function to determine if a day is a weekend
function isWeekend(day) {
const dayOfWeek = (adjustedFirstDay + day - 1) % 7;
// Calculate the indices for Saturday and Sunday
const saturdayIndex = (6 - firstDayOfWeek + 7) % 7;
const sundayIndex = (7 - firstDayOfWeek + 7) % 7;
return dayOfWeek === saturdayIndex || dayOfWeek === sundayIndex;
}
// Function to get the localized day initials based on the first day of the week
function getLocalizedDayInitials(locale) {
const formatter = new Intl.DateTimeFormat(locale, { weekday: 'short' });
const dayInitials = [];
for (let i = 0; i < 7; i++) {
const date = new Date(Date.UTC(2021, 0, i + 3)); // Start from a known Thursday to ensure full week coverage
const dayName = formatter.format(date);
dayInitials.push(dayName.charAt(0).toUpperCase());
}
return dayInitials;
}
// Reactive declaration to get the ordered day initials
$: orderedDayInitials = getLocalizedDayInitials(locale).slice(firstDayOfWeek).concat(getLocalizedDayInitials(locale).slice(0, firstDayOfWeek));
</script> </script>
<div class="calendar"> <div class="calendar">
<div class="month-name">{new Date(year, month).toLocaleString('default', { month: 'long' })}</div> <div class="month-name">{new Date(year, month).toLocaleString('default', { month: 'long' })}</div>
{#each Array.from({ length: firstDay }) as _}
<!-- Day initials header -->
{#each orderedDayInitials as dayInitial}
<div class="day-initial">{dayInitial}</div>
{/each}
{#each Array.from({ length: adjustedFirstDay }) as _}
<div class="day"></div> <div class="day"></div>
{/each} {/each}
{#each Array.from({ length: daysInMonth }, (_, i) => i + 1) as day} {#each Array.from({ length: daysInMonth }, (_, i) => i + 1) as day}
<div class="day {(firstDay + day - 1) % 7 === 0 || (firstDay + day - 1) % 7 === 6 ? 'weekend' : ''} {getHoliday(day) ? 'holiday' : ''} {isOptimizedDayOff(day) ? 'optimized' : ''} {isConsecutiveDayOff(day) ? 'consecutive-day' : ''}"> <div class="day {isWeekend(day) ? 'weekend' : ''} {getHoliday(day) ? 'holiday' : ''} {isOptimizedDayOff(day) ? 'optimized' : ''} {isConsecutiveDayOff(day) ? 'consecutive-day' : ''}">
{day} {day}
{#if getHoliday(day)} {#if getHoliday(day)}
<Tooltip text={getHoliday(day).name} /> <Tooltip text={getHoliday(day).name} />
@@ -96,6 +149,12 @@
width: 100%; width: 100%;
height: auto; height: auto;
} }
.day-initial {
text-align: center;
font-weight: bold;
color: #c5c6c7;
font-size: 0.6em;
}
.day { .day {
aspect-ratio: 1; aspect-ratio: 1;
text-align: center; text-align: center;

View File

@@ -35,6 +35,12 @@
let showHolidaysList = false; // State to toggle the visibility of the holidays list let showHolidaysList = false; // State to toggle the visibility of the holidays list
$: selectedCountryCode = Object.keys(countriesList).find(code => countriesList[code] === selectedCountry);
$: if (selectedCountryCode || selectedStateCode || daysOff) {
updateHolidays();
}
function updateStatesList(countryCode) { function updateStatesList(countryCode) {
const hd = new Holidays(countryCode); const hd = new Holidays(countryCode);
statesList = hd.getStates(countryCode) || []; statesList = hd.getStates(countryCode) || [];
@@ -44,7 +50,6 @@
const stateName = event.target.value; const stateName = event.target.value;
selectedStateCode = Object.keys(statesList).find(code => statesList[code] === stateName); selectedStateCode = Object.keys(statesList).find(code => statesList[code] === stateName);
selectedState = stateName; selectedState = stateName;
updateHolidays();
localStorage.setItem('selectedState', selectedState); localStorage.setItem('selectedState', selectedState);
localStorage.setItem('selectedStateCode', selectedStateCode); localStorage.setItem('selectedStateCode', selectedStateCode);
} }
@@ -70,13 +75,12 @@
daysOff = storedDaysOff ? parseInt(storedDaysOff, 10) : defaultDaysOff; daysOff = storedDaysOff ? parseInt(storedDaysOff, 10) : defaultDaysOff;
selectedState = storedState || ''; selectedState = storedState || '';
selectedStateCode = storedStateCode || ''; selectedStateCode = storedStateCode || '';
updateHolidays();
}); });
const countryCode = Object.keys(countriesList).find(code => countriesList[code] === selectedCountry); if (selectedCountryCode) {
if (countryCode) { updateStatesList(selectedCountryCode);
updateStatesList(countryCode);
} }
window.addEventListener('keydown', handleKeyDown);
}); });
async function fetchCountryCode() { async function fetchCountryCode() {
@@ -94,14 +98,11 @@
function handleCountryChange(event) { function handleCountryChange(event) {
const fullValue = event.target.value; const fullValue = event.target.value;
const countryCode = Object.keys(countriesList).find(code => countriesList[code] === fullValue); if (selectedCountryCode) {
if (countryCode) { daysOff = ptoData[selectedCountryCode] || 0;
selectedCountry = fullValue;
daysOff = ptoData[countryCode] || 0;
selectedState = ''; // Reset state selectedState = ''; // Reset state
selectedStateCode = ''; // Reset state code selectedStateCode = ''; // Reset state code
updateStatesList(countryCode); // Update states list for the new country updateStatesList(selectedCountryCode); // Update states list for the new country
updateHolidays();
localStorage.setItem('selectedCountry', selectedCountry); localStorage.setItem('selectedCountry', selectedCountry);
localStorage.setItem('selectedState', selectedState); localStorage.setItem('selectedState', selectedState);
localStorage.setItem('selectedStateCode', selectedStateCode); localStorage.setItem('selectedStateCode', selectedStateCode);
@@ -110,15 +111,13 @@
} }
function updateHolidays() { function updateHolidays() {
const countryCode = Object.keys(countriesList).find(code => countriesList[code] === selectedCountry); if (selectedCountryCode) {
if (countryCode) { updateStatesList(selectedCountryCode);
updateStatesList(countryCode); let allHolidays = getHolidaysForYear(selectedCountryCode, year, selectedStateCode);
let allHolidays = getHolidaysForYear(countryCode, year, selectedStateCode);
holidays = allHolidays.map(holiday => ({ holidays = allHolidays.map(holiday => ({
...holiday, ...holiday,
hidden: isHolidayHidden(holiday) hidden: isHolidayHidden(holiday)
})); }));
// Filter out hidden holidays for calculations
const visibleHolidays = holidays.filter(h => !h.hidden); const visibleHolidays = holidays.filter(h => !h.hidden);
optimizedDaysOff = optimizeDaysOff(visibleHolidays, year, daysOff); optimizedDaysOff = optimizeDaysOff(visibleHolidays, year, daysOff);
consecutiveDaysOff = calculateConsecutiveDaysOff(visibleHolidays, optimizedDaysOff, year); consecutiveDaysOff = calculateConsecutiveDaysOff(visibleHolidays, optimizedDaysOff, year);
@@ -142,7 +141,6 @@
localStorage.setItem('selectedState', selectedState); localStorage.setItem('selectedState', selectedState);
localStorage.setItem('selectedStateCode', selectedStateCode); localStorage.setItem('selectedStateCode', selectedStateCode);
localStorage.setItem('daysOff', daysOff); localStorage.setItem('daysOff', daysOff);
updateHolidays();
} }
function handleKeyDown(event) { function handleKeyDown(event) {
@@ -665,7 +663,14 @@
<div class="calendar-grid"> <div class="calendar-grid">
{#each months as month} {#each months as month}
<div class="calendar-container"> <div class="calendar-container">
<CalendarMonth {year} {month} {holidays} {optimizedDaysOff} {consecutiveDaysOff} /> <CalendarMonth
year={year}
month={month}
holidays={holidays}
optimizedDaysOff={optimizedDaysOff}
consecutiveDaysOff={consecutiveDaysOff}
selectedCountryCode={selectedCountryCode}
/>
</div> </div>
{/each} {/each}
</div> </div>