Add new utils file, deploy
This commit is contained in:
14
src/app.html
14
src/app.html
@@ -5,8 +5,20 @@
|
|||||||
<link rel="icon" href="%sveltekit.assets%/favicon.png" />
|
<link rel="icon" href="%sveltekit.assets%/favicon.png" />
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||||
%sveltekit.head%
|
%sveltekit.head%
|
||||||
|
<style>
|
||||||
|
@import url('https://fonts.googleapis.com/css2?family=Poppins:wght@400;600&display=swap');
|
||||||
|
|
||||||
|
body {
|
||||||
|
font-family: 'Poppins', sans-serif; /* Modern, cooler font */
|
||||||
|
background-color: #121212; /* Dark background */
|
||||||
|
color: #e0e0e0; /* Light gray text for readability */
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
line-height: 1.6;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
</head>
|
</head>
|
||||||
<body style="margin: 0; font-family: Arial, sans-serif; background-color: black; color: white;">
|
<body>
|
||||||
<div style="display: contents">%sveltekit.body%</div>
|
<div style="display: contents">%sveltekit.body%</div>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|||||||
@@ -6,6 +6,10 @@
|
|||||||
export let holidays = [];
|
export let holidays = [];
|
||||||
export let optimizedDaysOff = [];
|
export let optimizedDaysOff = [];
|
||||||
|
|
||||||
|
// Reactive declarations
|
||||||
|
$: daysInMonth = getDaysInMonth(year, month);
|
||||||
|
$: firstDay = getFirstDayOfMonth(year, month);
|
||||||
|
|
||||||
function getDaysInMonth(year, month) {
|
function getDaysInMonth(year, month) {
|
||||||
return new Date(year, month + 1, 0).getDate();
|
return new Date(year, month + 1, 0).getDate();
|
||||||
}
|
}
|
||||||
@@ -14,9 +18,6 @@
|
|||||||
return new Date(year, month, 1).getDay();
|
return new Date(year, month, 1).getDay();
|
||||||
}
|
}
|
||||||
|
|
||||||
let daysInMonth = getDaysInMonth(year, month);
|
|
||||||
let firstDay = getFirstDayOfMonth(year, month);
|
|
||||||
|
|
||||||
function getHoliday(day) {
|
function getHoliday(day) {
|
||||||
return holidays.find(holiday =>
|
return holidays.find(holiday =>
|
||||||
holiday.date.getFullYear() === year &&
|
holiday.date.getFullYear() === year &&
|
||||||
|
|||||||
@@ -1,19 +1,20 @@
|
|||||||
<script>
|
<script>
|
||||||
|
import { onMount } from 'svelte';
|
||||||
import countries from 'i18n-iso-countries';
|
import countries from 'i18n-iso-countries';
|
||||||
import enLocale from 'i18n-iso-countries/langs/en.json';
|
import enLocale from 'i18n-iso-countries/langs/en.json';
|
||||||
import CalendarMonth from '../lib/CalendarMonth.svelte';
|
import CalendarMonth from '../lib/CalendarMonth.svelte';
|
||||||
import Holidays from 'date-holidays';
|
import { getHolidaysForYear, optimizeDaysOff, calculateConsecutiveDaysOff } from '../lib/holidayUtils.js';
|
||||||
|
|
||||||
countries.registerLocale(enLocale);
|
countries.registerLocale(enLocale);
|
||||||
let countriesList = countries.getNames('en');
|
let countriesList = countries.getNames('en');
|
||||||
|
|
||||||
let year = new Date().getFullYear();
|
let year = new Date().getFullYear();
|
||||||
let months = Array.from({ length: 12 }, (_, i) => i);
|
let months = Array.from({ length: 12 }, (_, i) => i);
|
||||||
let selectedCountry = 'Belgium'; // Default country name
|
let selectedCountry = 'Belgium';
|
||||||
let holidays = [];
|
let holidays = [];
|
||||||
let daysOff = 24; // Default days off per year
|
let daysOff = 24;
|
||||||
let optimizedDaysOff = [];
|
let optimizedDaysOff = [];
|
||||||
let extendedHolidays = [];
|
let consecutiveDaysOff = [];
|
||||||
|
|
||||||
function handleYearChange(event) {
|
function handleYearChange(event) {
|
||||||
year = parseInt(event.target.value);
|
year = parseInt(event.target.value);
|
||||||
@@ -32,376 +33,226 @@
|
|||||||
function updateHolidays() {
|
function updateHolidays() {
|
||||||
const countryCode = Object.keys(countriesList).find(code => countriesList[code] === selectedCountry);
|
const countryCode = Object.keys(countriesList).find(code => countriesList[code] === selectedCountry);
|
||||||
if (countryCode) {
|
if (countryCode) {
|
||||||
const hd = new Holidays(countryCode);
|
holidays = getHolidaysForYear(countryCode, year);
|
||||||
holidays = hd.getHolidays(year)
|
optimizedDaysOff = optimizeDaysOff(holidays, year, daysOff);
|
||||||
.filter(holiday => holiday.type === 'public') // Filter for public holidays
|
consecutiveDaysOff = calculateConsecutiveDaysOff(holidays, optimizedDaysOff, year);
|
||||||
.map(holiday => ({
|
|
||||||
date: new Date(holiday.date),
|
|
||||||
name: holiday.name
|
|
||||||
}));
|
|
||||||
console.log('Holidays:', holidays);
|
|
||||||
optimizeDaysOff();
|
|
||||||
calculateExtendedHolidays();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function optimizeDaysOff() {
|
function handleKeyDown(event) {
|
||||||
// Reset optimized days off
|
switch (event.key) {
|
||||||
optimizedDaysOff = [];
|
case 'ArrowRight':
|
||||||
|
event.preventDefault();
|
||||||
// Combine holidays and weekends
|
year++;
|
||||||
const allDays = holidays.map(h => h.date);
|
updateHolidays();
|
||||||
let daysToUse = daysOff;
|
|
||||||
|
|
||||||
// Create a list of potential days to take off, sorted by their potential to extend holidays
|
|
||||||
const potentialDaysOff = [];
|
|
||||||
|
|
||||||
for (let month = 0; month < 12; month++) {
|
|
||||||
for (let day = 1; day <= 31; day++) {
|
|
||||||
const date = new Date(year, month, day);
|
|
||||||
if (date.getMonth() !== month) break; // Skip invalid dates
|
|
||||||
|
|
||||||
const isWeekend = date.getDay() === 0 || date.getDay() === 6;
|
|
||||||
const isHoliday = allDays.some(d => d.getTime() === date.getTime());
|
|
||||||
|
|
||||||
if (!isWeekend && !isHoliday) {
|
|
||||||
const prevDay = new Date(date);
|
|
||||||
prevDay.setDate(date.getDate() - 1);
|
|
||||||
const nextDay = new Date(date);
|
|
||||||
nextDay.setDate(date.getDate() + 1);
|
|
||||||
|
|
||||||
const extendsHoliday =
|
|
||||||
allDays.some(d => d.getTime() === prevDay.getTime()) ||
|
|
||||||
allDays.some(d => d.getTime() === nextDay.getTime());
|
|
||||||
|
|
||||||
if (extendsHoliday) {
|
|
||||||
potentialDaysOff.push(date);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Sort potential days off by their ability to extend existing chains with multiple holidays
|
|
||||||
potentialDaysOff.sort((a, b) => {
|
|
||||||
const aConsecutive = calculateConsecutiveDaysIncludingHoliday(a, allDays);
|
|
||||||
const bConsecutive = calculateConsecutiveDaysIncludingHoliday(b, allDays);
|
|
||||||
return bConsecutive - aConsecutive || a.getTime() - b.getTime();
|
|
||||||
});
|
|
||||||
|
|
||||||
// Select days off from the sorted list, prioritizing those that extend chains with multiple holidays
|
|
||||||
for (let i = 0; i < potentialDaysOff.length && daysToUse > 0; i++) {
|
|
||||||
const date = potentialDaysOff[i];
|
|
||||||
const prevDay = new Date(date);
|
|
||||||
prevDay.setDate(date.getDate() - 1);
|
|
||||||
const nextDay = new Date(date);
|
|
||||||
nextDay.setDate(date.getDate() + 1);
|
|
||||||
|
|
||||||
// Check if adding this day creates a longer chain with multiple holidays
|
|
||||||
if (calculateConsecutiveDaysIncludingHoliday(date, allDays) > 0) {
|
|
||||||
optimizedDaysOff.push(date);
|
|
||||||
daysToUse--;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Attempt to create full week chains
|
|
||||||
if (daysToUse > 0) {
|
|
||||||
for (let i = 0; i < optimizedDaysOff.length && daysToUse > 0; i++) {
|
|
||||||
const date = optimizedDaysOff[i];
|
|
||||||
const startOfWeek = new Date(date);
|
|
||||||
startOfWeek.setDate(date.getDate() - date.getDay() + 1); // Start of the week (Monday)
|
|
||||||
const endOfWeek = new Date(startOfWeek);
|
|
||||||
endOfWeek.setDate(startOfWeek.getDate() + 4); // End of the week (Friday)
|
|
||||||
|
|
||||||
for (let d = new Date(startOfWeek); d <= endOfWeek && daysToUse > 0; d.setDate(d.getDate() + 1)) {
|
|
||||||
if (!optimizedDaysOff.some(optDate => optDate.getTime() === d.getTime()) && !allDays.some(holiday => holiday.getTime() === d.getTime())) {
|
|
||||||
optimizedDaysOff.push(new Date(d));
|
|
||||||
daysToUse--;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
console.log('Optimized Days Off:', optimizedDaysOff);
|
|
||||||
}
|
|
||||||
|
|
||||||
function calculateConsecutiveDaysIncludingHoliday(date, allDays) {
|
|
||||||
let consecutiveDays = 0;
|
|
||||||
let prevDay = new Date(date);
|
|
||||||
let nextDay = new Date(date);
|
|
||||||
let includesHoliday = false;
|
|
||||||
let holidayCount = 0;
|
|
||||||
|
|
||||||
// Count consecutive days before the date
|
|
||||||
while (true) {
|
|
||||||
prevDay.setDate(prevDay.getDate() - 1);
|
|
||||||
if (prevDay.getDay() === 0 || prevDay.getDay() === 6 || allDays.some(d => d.getTime() === prevDay.getTime())) {
|
|
||||||
consecutiveDays++;
|
|
||||||
if (allDays.some(d => d.getTime() === prevDay.getTime())) {
|
|
||||||
includesHoliday = true;
|
|
||||||
holidayCount++;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
break;
|
break;
|
||||||
}
|
case 'ArrowLeft':
|
||||||
}
|
event.preventDefault();
|
||||||
|
year--;
|
||||||
// Count consecutive days after the date
|
updateHolidays();
|
||||||
while (true) {
|
break;
|
||||||
nextDay.setDate(nextDay.getDate() + 1);
|
case 'ArrowUp':
|
||||||
if (nextDay.getDay() === 0 || nextDay.getDay() === 6 || allDays.some(d => d.getTime() === nextDay.getTime())) {
|
event.preventDefault();
|
||||||
consecutiveDays++;
|
daysOff++;
|
||||||
if (allDays.some(d => d.getTime() === nextDay.getTime())) {
|
updateHolidays();
|
||||||
includesHoliday = true;
|
break;
|
||||||
holidayCount++;
|
case 'ArrowDown':
|
||||||
}
|
event.preventDefault();
|
||||||
} else {
|
if (daysOff > 0) {
|
||||||
|
daysOff--;
|
||||||
|
updateHolidays();
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return includesHoliday ? holidayCount : 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
function formatDate(date) {
|
|
||||||
return date.toLocaleDateString('en-US', { month: 'short', day: 'numeric' });
|
|
||||||
}
|
|
||||||
|
|
||||||
function handleDaysOffChange(event) {
|
|
||||||
const value = parseInt(event.target.textContent);
|
|
||||||
if (!isNaN(value)) {
|
|
||||||
daysOff = value;
|
|
||||||
optimizeDaysOff();
|
|
||||||
calculateExtendedHolidays();
|
|
||||||
} else {
|
|
||||||
event.target.textContent = daysOff; // Revert to previous valid value if input is invalid
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function handleDaysOffInput(event) {
|
onMount(() => {
|
||||||
const value = event.target.textContent;
|
window.addEventListener('keydown', handleKeyDown);
|
||||||
event.target.textContent = value.replace(/\D/g, '');
|
return () => {
|
||||||
}
|
window.removeEventListener('keydown', handleKeyDown);
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
function calculateExtendedHolidays() {
|
|
||||||
const allDays = holidays.map(h => h.date); // Include all holiday dates
|
|
||||||
let remainingDaysOff = daysOff; // Track remaining days off
|
|
||||||
|
|
||||||
extendedHolidays = holidays
|
|
||||||
.filter(holiday => holiday.date.getDay() !== 0 && holiday.date.getDay() !== 6) // Only non-weekend holidays
|
|
||||||
.map(holiday => {
|
|
||||||
let startDate = new Date(holiday.date);
|
|
||||||
let endDate = new Date(holiday.date);
|
|
||||||
let daysUsed = 0;
|
|
||||||
|
|
||||||
// Extend before the holiday
|
|
||||||
while (daysUsed < remainingDaysOff) {
|
|
||||||
const prevDay = new Date(startDate);
|
|
||||||
prevDay.setDate(startDate.getDate() - 1);
|
|
||||||
if (!allDays.some(d => d.getTime() === prevDay.getTime()) && prevDay.getDay() !== 0 && prevDay.getDay() !== 6) {
|
|
||||||
startDate = prevDay;
|
|
||||||
daysUsed++;
|
|
||||||
} else {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Extend after the holiday
|
|
||||||
while (daysUsed < remainingDaysOff) {
|
|
||||||
const nextDay = new Date(endDate);
|
|
||||||
nextDay.setDate(endDate.getDate() + 1);
|
|
||||||
if (!allDays.some(d => d.getTime() === nextDay.getTime()) && nextDay.getDay() !== 0 && nextDay.getDay() !== 6) {
|
|
||||||
endDate = nextDay;
|
|
||||||
daysUsed++;
|
|
||||||
} else {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
remainingDaysOff -= daysUsed; // Deduct used days from remaining
|
|
||||||
|
|
||||||
// Calculate total consecutive days including weekends
|
|
||||||
const totalDays = calculateTotalConsecutiveDays(startDate, endDate, allDays);
|
|
||||||
|
|
||||||
return {
|
|
||||||
holidayName: holiday.name,
|
|
||||||
startDate: formatDate(startDate),
|
|
||||||
endDate: formatDate(endDate),
|
|
||||||
totalDays
|
|
||||||
};
|
|
||||||
});
|
|
||||||
|
|
||||||
// If there are remaining days off, try to use them to extend any holiday further
|
|
||||||
if (remainingDaysOff > 0) {
|
|
||||||
extendedHolidays.forEach(extended => {
|
|
||||||
let startDate = new Date(extended.startDate);
|
|
||||||
let endDate = new Date(extended.endDate);
|
|
||||||
|
|
||||||
// Extend before the holiday
|
|
||||||
while (remainingDaysOff > 0) {
|
|
||||||
const prevDay = new Date(startDate);
|
|
||||||
prevDay.setDate(startDate.getDate() - 1);
|
|
||||||
if (!allDays.some(d => d.getTime() === prevDay.getTime()) && prevDay.getDay() !== 0 && prevDay.getDay() !== 6) {
|
|
||||||
startDate = prevDay;
|
|
||||||
remainingDaysOff--;
|
|
||||||
} else {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Extend after the holiday
|
|
||||||
while (remainingDaysOff > 0) {
|
|
||||||
const nextDay = new Date(endDate);
|
|
||||||
nextDay.setDate(endDate.getDate() + 1);
|
|
||||||
if (!allDays.some(d => d.getTime() === nextDay.getTime()) && nextDay.getDay() !== 0 && nextDay.getDay() !== 6) {
|
|
||||||
endDate = nextDay;
|
|
||||||
remainingDaysOff--;
|
|
||||||
} else {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
extended.startDate = formatDate(startDate);
|
|
||||||
extended.endDate = formatDate(endDate);
|
|
||||||
extended.totalDays = calculateTotalConsecutiveDays(startDate, endDate, allDays);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function calculateTotalConsecutiveDays(startDate, endDate, allDays) {
|
|
||||||
let totalDays = 0;
|
|
||||||
let currentDate = new Date(startDate);
|
|
||||||
|
|
||||||
while (currentDate <= endDate) {
|
|
||||||
const isWeekend = currentDate.getDay() === 0 || currentDate.getDay() === 6;
|
|
||||||
const isHoliday = allDays.some(d => d.getTime() === currentDate.getTime());
|
|
||||||
|
|
||||||
if (isWeekend || isHoliday || optimizedDaysOff.some(d => d.getTime() === currentDate.getTime())) {
|
|
||||||
totalDays++;
|
|
||||||
}
|
|
||||||
|
|
||||||
currentDate.setDate(currentDate.getDate() + 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
return totalDays;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Initialize holidays on load
|
|
||||||
updateHolidays();
|
updateHolidays();
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style>
|
<style>
|
||||||
header {
|
|
||||||
|
header, footer {
|
||||||
text-align: center;
|
text-align: center;
|
||||||
background-color: black;
|
color: #e0e0e0; /* Monochrome light text */
|
||||||
padding: 20px;
|
padding: 15px;
|
||||||
color: white;
|
border-bottom: 1px solid #333; /* Subtle border for separation */
|
||||||
|
}
|
||||||
|
|
||||||
|
h1 {
|
||||||
|
margin: 0;
|
||||||
font-size: 2em;
|
font-size: 2em;
|
||||||
font-family: 'Arial', sans-serif;
|
|
||||||
}
|
}
|
||||||
main {
|
|
||||||
max-width: 800px;
|
footer {
|
||||||
|
border-top: 1px solid #333;
|
||||||
|
font-size: 0.9em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.content-box {
|
||||||
|
max-width: 900px;
|
||||||
margin: 20px auto;
|
margin: 20px auto;
|
||||||
padding: 20px;
|
padding: 10px 0;
|
||||||
background-color: #333; /* Dark gray for main background */
|
background-color: #1e1e1e; /* Slightly lighter dark background for content boxes */
|
||||||
text-align: center;
|
text-align: center;
|
||||||
font-size: 1.2em;
|
font-size: 1em; /* Slightly smaller font size */
|
||||||
color: white; /* Ensure text is white for readability */
|
color: #e0e0e0; /* Light gray text */
|
||||||
border-radius: 10px; /* Add border-radius for a smoother look */
|
border-radius: 10px;
|
||||||
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.2); /* Add shadow for depth */
|
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.7);
|
||||||
|
margin-bottom: 40px;
|
||||||
}
|
}
|
||||||
select, input {
|
|
||||||
margin: 0 5px;
|
input {
|
||||||
font-size: 1em;
|
margin: 0 10px;
|
||||||
padding: 5px;
|
font-size: 0.9em; /* Slightly smaller font size */
|
||||||
background-color: #555; /* Darker background for inputs */
|
padding: 8px;
|
||||||
color: white; /* White text for inputs */
|
background-color: #2a2a2a; /* Darker gray for inputs */
|
||||||
border: none;
|
color: #e0e0e0; /* Light text for inputs */
|
||||||
|
border: 1px solid #444;
|
||||||
border-radius: 5px;
|
border-radius: 5px;
|
||||||
|
transition: background-color 0.3s;
|
||||||
|
width: auto; /* Dynamic width based on content */
|
||||||
}
|
}
|
||||||
|
|
||||||
|
input::hover {
|
||||||
|
background-color: #333; /* Slightly lighter on hover */
|
||||||
|
}
|
||||||
|
|
||||||
ul {
|
ul {
|
||||||
list-style-type: none; /* Remove bullet points */
|
list-style-type: none;
|
||||||
padding: 0;
|
padding: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
li {
|
li {
|
||||||
margin: 10px 0;
|
margin: 15px 0;
|
||||||
|
padding: 15px;
|
||||||
|
background-color: #2a2a2a;
|
||||||
|
border-radius: 5px;
|
||||||
|
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.5);
|
||||||
}
|
}
|
||||||
|
|
||||||
.calendar-grid {
|
.calendar-grid {
|
||||||
display: grid;
|
display: grid;
|
||||||
grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)); /* Adjust to fit available space */
|
grid-template-columns: repeat(3, 1fr); /* Default to 3 columns */
|
||||||
gap: 10px;
|
gap: 20px;
|
||||||
justify-items: center;
|
justify-items: center;
|
||||||
padding: 20px;
|
padding: 20px;
|
||||||
}
|
}
|
||||||
.calendar-container {
|
|
||||||
width: 100%;
|
|
||||||
aspect-ratio: 1;
|
|
||||||
background-color: #444; /* Slightly lighter gray for calendar */
|
|
||||||
color: white;
|
|
||||||
border-radius: 5px;
|
|
||||||
padding: 10px;
|
|
||||||
box-sizing: border-box; /* Ensure padding is included in width */
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Media query for smaller screens */
|
@media (max-width: 768px) {
|
||||||
@media (max-width: 600px) {
|
|
||||||
.calendar-grid {
|
.calendar-grid {
|
||||||
grid-template-columns: repeat(auto-fit, minmax(150px, 1fr)); /* Adjust for smaller screens */
|
grid-template-columns: repeat(2, 1fr); /* 2 columns on smaller screens */
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.editable-span {
|
.calendar-container {
|
||||||
display: inline-block;
|
width: 100%;
|
||||||
border-bottom: 1px dotted white; /* Dotted underline */
|
aspect-ratio: 1;
|
||||||
color: white;
|
background-color: #2a2a2a; /* Dark gray for calendar */
|
||||||
font-size: 1em;
|
color: #e0e0e0;
|
||||||
width: 3em; /* Adjust width as needed */
|
border-radius: 5px;
|
||||||
|
padding: 10px;
|
||||||
|
box-sizing: border-box;
|
||||||
|
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.5);
|
||||||
|
}
|
||||||
|
|
||||||
|
.editable-input {
|
||||||
|
border: none;
|
||||||
|
border-bottom: 1px dotted #e0e0e0;
|
||||||
|
background: none;
|
||||||
|
color: inherit;
|
||||||
|
font-size: inherit;
|
||||||
|
font-family: inherit;
|
||||||
|
width: auto;
|
||||||
text-align: center;
|
text-align: center;
|
||||||
margin: 0 5px;
|
margin: 0 10px;
|
||||||
outline: none;
|
outline: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
.editable-span:focus {
|
.arrow-controls {
|
||||||
border-bottom: 1px solid white; /* Solid underline on focus */
|
display: inline-flex;
|
||||||
|
align-items: center;
|
||||||
}
|
}
|
||||||
|
|
||||||
.highlight {
|
button {
|
||||||
background-color: #4caf50; /* Green color for highlighting */
|
background-color: #2a2a2a;
|
||||||
color: white;
|
border: 1px solid #444;
|
||||||
|
color: #e0e0e0;
|
||||||
|
font-size: 1em; /* Slightly smaller font size */
|
||||||
|
cursor: pointer;
|
||||||
|
padding: 5px 10px;
|
||||||
|
margin: 0 10px;
|
||||||
|
border-radius: 5px;
|
||||||
|
box-shadow: 0 4px 6px rgba(0, 0, 0, 0.5), inset 0 1px 0 rgba(255, 255, 255, 0.1);
|
||||||
|
transition: background-color 0.3s, color 0.3s, transform 0.1s;
|
||||||
|
}
|
||||||
|
|
||||||
|
button:hover {
|
||||||
|
background-color: #333;
|
||||||
|
color: #fff; /* Change color on hover */
|
||||||
|
}
|
||||||
|
|
||||||
|
button:active {
|
||||||
|
transform: translateY(2px); /* Simulate button press */
|
||||||
|
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.5), inset 0 1px 0 rgba(255, 255, 255, 0.1);
|
||||||
|
}
|
||||||
|
|
||||||
|
button:focus {
|
||||||
|
outline: 2px solid #61dafb; /* Accessibility focus outline */
|
||||||
|
}
|
||||||
|
|
||||||
|
.bold {
|
||||||
|
font-weight: bold;
|
||||||
|
font-size: 1.2em;
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
||||||
|
<header>
|
||||||
|
<h1>Stretch My Holidays</h1>
|
||||||
|
</header>
|
||||||
|
|
||||||
<main>
|
<main>
|
||||||
I live in
|
<div class="content-box">
|
||||||
<input list="countries" bind:value={selectedCountry} on:change={handleCountryChange} />
|
<p>
|
||||||
<datalist id="countries">
|
I live in
|
||||||
{#each Object.values(countriesList) as name}
|
<input list="countries" class="editable-input bold" bind:value={selectedCountry} on:change={handleCountryChange} aria-label="Select country" />
|
||||||
<option value={name}></option>
|
and have
|
||||||
{/each}
|
<span class="arrow-controls">
|
||||||
</datalist>
|
<button on:click={() => { daysOff++; updateHolidays(); }} aria-label="Increase days off">▲</button>
|
||||||
and have
|
<span class="bold">{daysOff}</span> days off
|
||||||
<span contenteditable="true" class="editable-span" on:input={handleDaysOffInput} on:blur={handleDaysOffChange}>{daysOff}</span> days off per year
|
<button on:click={() => { if (daysOff > 0) { daysOff--; updateHolidays(); } }} aria-label="Decrease days off">▼</button>
|
||||||
|
</span> in
|
||||||
|
<span class="arrow-controls">
|
||||||
|
<button on:click={() => { year--; updateHolidays(); }} aria-label="Previous year">◀</button>
|
||||||
|
<span class="bold">{year}</span>
|
||||||
|
<button on:click={() => { year++; updateHolidays(); }} aria-label="Next year">▶</button>
|
||||||
|
</span>
|
||||||
|
</p>
|
||||||
|
|
||||||
<div>
|
<datalist id="countries">
|
||||||
<label for="year">Select Year: </label>
|
{#each Object.values(countriesList) as name}
|
||||||
<input type="number" id="year" bind:value={year} on:input={handleYearChange} min="1900" max="2100" />
|
<option value={name}></option>
|
||||||
</div>
|
|
||||||
|
|
||||||
<div>
|
|
||||||
Extended Holidays:
|
|
||||||
<ul>
|
|
||||||
{#each extendedHolidays as extended}
|
|
||||||
<li>
|
|
||||||
{extended.totalDays} day holiday, including {extended.holidayName} from {extended.startDate} to {extended.endDate}
|
|
||||||
</li>
|
|
||||||
{/each}
|
{/each}
|
||||||
</ul>
|
</datalist>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="calendar-grid">
|
<div class="content-box">
|
||||||
{#each months as month}
|
<div class="calendar-grid">
|
||||||
<div class="calendar-container">
|
{#each months as month}
|
||||||
<CalendarMonth {year} {month} {holidays} {optimizedDaysOff} />
|
<div class="calendar-container">
|
||||||
</div>
|
<CalendarMonth {year} {month} {holidays} {optimizedDaysOff} />
|
||||||
{/each}
|
</div>
|
||||||
|
{/each}
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</main>
|
</main>
|
||||||
|
|
||||||
|
<footer>
|
||||||
|
<p>© 2023 Stretch My Holidays. All rights reserved.</p>
|
||||||
|
</footer>
|
||||||
Reference in New Issue
Block a user