Cleanup
This commit is contained in:
18
src/app.html
18
src/app.html
@@ -3,15 +3,25 @@
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<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.0" />
|
||||
<title>Stretch My Time Off - Optimise Your Vacation Days</title>
|
||||
<meta name="description" content="Unlock the secret to extending your vacation days by strategically aligning public holidays and personal leave. Plan your ultimate getaway with ease and efficiency." />
|
||||
<meta name="keywords" content="vacation, holidays, time off, optimise, travel, planning, calendar" />
|
||||
<meta name="author" content="Zach" />
|
||||
<meta property="og:title" content="Stretch My Time Off - Optimise Your Vacation Days" />
|
||||
<meta property="og:description" content="Unlock the secret to extending your vacation days by strategically aligning public holidays and personal leave. Plan your ultimate getaway with ease and efficiency." />
|
||||
<meta property="og:type" content="website" />
|
||||
<meta property="og:url" content="https://stretchmytimeoff.com" />
|
||||
<meta property="og:image" content="https://stretchmytimeoff.com/og-image.jpg" />
|
||||
<link rel="canonical" href="https://stretchmytimeoff.com" />
|
||||
%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 */
|
||||
font-family: 'Poppins', sans-serif;
|
||||
background-color: #000;
|
||||
color: #fff;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
line-height: 1.6;
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
export let month;
|
||||
export let holidays = [];
|
||||
export let optimizedDaysOff = [];
|
||||
export let consecutiveDaysOff = [];
|
||||
|
||||
// Reactive declarations
|
||||
$: daysInMonth = getDaysInMonth(year, month);
|
||||
@@ -33,16 +34,55 @@
|
||||
date.getDate() === day
|
||||
);
|
||||
}
|
||||
|
||||
// Determine the dominant month for each consecutive days off period
|
||||
function getDominantMonth(period) {
|
||||
const startMonth = period.startDate.getMonth();
|
||||
const endMonth = period.endDate.getMonth();
|
||||
|
||||
if (startMonth === endMonth) {
|
||||
return startMonth;
|
||||
}
|
||||
|
||||
const startDays = getDaysInMonth(year, startMonth) - period.startDate.getDate() + 1;
|
||||
const endDays = period.endDate.getDate();
|
||||
|
||||
return startDays > endDays ? startMonth : endMonth;
|
||||
}
|
||||
</script>
|
||||
|
||||
<div class="calendar">
|
||||
<div class="month-name">{new Date(year, month).toLocaleString('default', { month: 'long' })}</div>
|
||||
{#each Array.from({ length: firstDay }) as _}
|
||||
<div class="day"></div>
|
||||
{/each}
|
||||
{#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' : ''}">
|
||||
{day}
|
||||
{#if getHoliday(day)}
|
||||
<Tooltip text={getHoliday(day).name} />
|
||||
{/if}
|
||||
</div>
|
||||
{/each}
|
||||
</div>
|
||||
|
||||
<div class="consecutive-days-off">
|
||||
<ul>
|
||||
{#each consecutiveDaysOff.filter(period => getDominantMonth(period) === month) as period}
|
||||
<li>
|
||||
{period.startDate.toLocaleDateString('en-US', { month: 'short', day: 'numeric' })} to
|
||||
{period.endDate.toLocaleDateString('en-US', { month: 'short', day: 'numeric' })}:
|
||||
<strong>{period.totalDays} days</strong>
|
||||
</li>
|
||||
{/each}
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<style>
|
||||
.calendar {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(7, 1fr);
|
||||
gap: 1px;
|
||||
border: 1px solid #444;
|
||||
padding: 5px;
|
||||
margin: 5px;
|
||||
box-sizing: border-box;
|
||||
width: 100%;
|
||||
height: auto;
|
||||
@@ -58,7 +98,6 @@
|
||||
background-color: #222;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.day:hover {
|
||||
:global(.tooltip) {
|
||||
opacity: 1;
|
||||
@@ -66,37 +105,34 @@
|
||||
}
|
||||
}
|
||||
.weekend {
|
||||
background-color: #333;
|
||||
background-color: #585858;
|
||||
}
|
||||
.optimized {
|
||||
background-color: #4caf50;
|
||||
}
|
||||
.holiday {
|
||||
background-color: #3b1e6e;
|
||||
cursor: pointer;
|
||||
}
|
||||
.optimized {
|
||||
background-color: #4caf50;
|
||||
color: white;
|
||||
}
|
||||
.month-name {
|
||||
grid-column: span 7;
|
||||
text-align: center;
|
||||
font-weight: bold;
|
||||
font-size: 0.8em;
|
||||
margin-bottom: 2px;
|
||||
letter-spacing: 0.1em;
|
||||
font-size: 0.9em;
|
||||
text-transform: uppercase;
|
||||
color: #c5c6c7;
|
||||
margin-bottom: 5px;
|
||||
}
|
||||
.consecutive-days-off {
|
||||
margin-top: 10px;
|
||||
color: #fff;
|
||||
}
|
||||
</style>
|
||||
|
||||
<div class="calendar">
|
||||
<div class="month-name">{new Date(year, month).toLocaleString('default', { month: 'long' })}</div>
|
||||
{#each Array.from({ length: firstDay }) as _}
|
||||
<div class="day"></div>
|
||||
{/each}
|
||||
{#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' : ''}">
|
||||
{day}
|
||||
{#if getHoliday(day)}
|
||||
<Tooltip text={getHoliday(day).name} />
|
||||
{/if}
|
||||
</div>
|
||||
{/each}
|
||||
</div>
|
||||
.consecutive-days-off ul {
|
||||
list-style: none;
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
}
|
||||
.consecutive-days-off li {
|
||||
font-size: 0.9em;
|
||||
}
|
||||
</style>
|
||||
@@ -9,7 +9,7 @@
|
||||
color: #fff;
|
||||
padding: 5px;
|
||||
border-radius: 3px;
|
||||
font-size: 0.8em;
|
||||
font-size: 1.2em;
|
||||
white-space: nowrap;
|
||||
z-index: 1000;
|
||||
pointer-events: none;
|
||||
|
||||
@@ -41,20 +41,51 @@ export function getHolidaysForYear(countryCode, year) {
|
||||
|
||||
// Optimize days off to create the longest possible chains
|
||||
export function optimizeDaysOff(holidays, year, daysOff) {
|
||||
// Filter holidays to include only those in the current year
|
||||
const currentYearHolidays = holidays.filter(h => h.date.getFullYear() === year);
|
||||
|
||||
// Recalculate weekends for the current year
|
||||
const weekends = getWeekends(year);
|
||||
const allDaysOffSet = new Set([...holidays.map(h => dateKey(h.date)), ...weekends.map(d => dateKey(d))]);
|
||||
|
||||
// Initialize a new Set for all days off
|
||||
const allDaysOffSet = new Set([
|
||||
...currentYearHolidays.map(h => dateKey(h.date)),
|
||||
...weekends.map(d => dateKey(d))
|
||||
]);
|
||||
|
||||
let rankedGaps = rankGapsByEfficiency(findGaps(allDaysOffSet, year), allDaysOffSet);
|
||||
|
||||
return selectDaysOff(rankedGaps, daysOff, allDaysOffSet);
|
||||
return selectDaysOff(rankedGaps, daysOff, allDaysOffSet, year);
|
||||
}
|
||||
|
||||
// Calculate consecutive days off
|
||||
export function calculateConsecutiveDaysOff(holidays, optimizedDaysOff, year) {
|
||||
const allDays = [...holidays.map(h => h.date), ...optimizedDaysOff];
|
||||
allDays.sort((a, b) => a - b);
|
||||
const allDaysOffSet = new Set([...holidays.map(h => dateKey(h.date)), ...optimizedDaysOff.map(d => dateKey(d))]);
|
||||
const consecutiveDaysOff = [];
|
||||
let currentGroup = [];
|
||||
|
||||
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
|
||||
|
||||
return findConsecutiveDaysOff(allDays, holidays, optimizedDaysOff);
|
||||
if (isWeekend(date) || isHoliday(date, holidays) || isDayOff(date, allDaysOffSet)) {
|
||||
currentGroup.push(date);
|
||||
} else {
|
||||
if (currentGroup.length > 2) {
|
||||
addConsecutiveDaysOff(consecutiveDaysOff, currentGroup, optimizedDaysOff);
|
||||
}
|
||||
currentGroup = [];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Check the last group at the end of the year
|
||||
if (currentGroup.length > 2) {
|
||||
addConsecutiveDaysOff(consecutiveDaysOff, currentGroup, optimizedDaysOff);
|
||||
}
|
||||
|
||||
return consecutiveDaysOff;
|
||||
}
|
||||
|
||||
// Get all weekends for a specific year
|
||||
@@ -143,7 +174,7 @@ function calculateChain(startDate, gapLength, allDaysOffSet, direction) {
|
||||
}
|
||||
|
||||
// Select days off based on ranked gaps
|
||||
function selectDaysOff(rankedGaps, daysOff, allDaysOffSet) {
|
||||
function selectDaysOff(rankedGaps, daysOff, allDaysOffSet, year) {
|
||||
const selectedDays = [];
|
||||
|
||||
while (daysOff > 0 && rankedGaps.length > 0) {
|
||||
@@ -165,51 +196,26 @@ function selectDaysOff(rankedGaps, daysOff, allDaysOffSet) {
|
||||
}
|
||||
|
||||
// Recalculate gaps and re-rank them after each assignment
|
||||
const newGaps = findGaps(allDaysOffSet, new Date().getFullYear());
|
||||
const newGaps = findGaps(allDaysOffSet, year);
|
||||
rankedGaps = rankGapsByEfficiency(newGaps, allDaysOffSet);
|
||||
}
|
||||
|
||||
return selectedDays;
|
||||
}
|
||||
|
||||
// Find consecutive days off
|
||||
function findConsecutiveDaysOff(allDays, holidays, optimizedDaysOff) {
|
||||
let consecutiveDaysOff = [];
|
||||
let currentGroup = [];
|
||||
let includesHoliday = false;
|
||||
|
||||
allDays.forEach(date => {
|
||||
if (isWeekend(date) || isHoliday(date, holidays) || isDayOff(date, new Set(optimizedDaysOff.map(d => dateKey(d))))) {
|
||||
currentGroup.push(date);
|
||||
if (isHoliday(date, holidays)) includesHoliday = true;
|
||||
} else if (currentGroup.length > 0) {
|
||||
addConsecutiveDaysOff(consecutiveDaysOff, currentGroup, optimizedDaysOff, includesHoliday);
|
||||
currentGroup = [];
|
||||
includesHoliday = false;
|
||||
}
|
||||
});
|
||||
|
||||
if (currentGroup.length > 0) {
|
||||
addConsecutiveDaysOff(consecutiveDaysOff, currentGroup, optimizedDaysOff, includesHoliday);
|
||||
}
|
||||
|
||||
return consecutiveDaysOff;
|
||||
}
|
||||
|
||||
// Add consecutive days off to the list
|
||||
function addConsecutiveDaysOff(consecutiveDaysOff, currentGroup, optimizedDaysOff, includesHoliday) {
|
||||
if (currentGroup.some(d => isDayOff(d, new Set(optimizedDaysOff.map(d => dateKey(d)))))) {
|
||||
const startDate = currentGroup[0];
|
||||
const endDate = currentGroup[currentGroup.length - 1];
|
||||
const totalDays = daysBetween(startDate, endDate) + 1;
|
||||
const usedDaysOff = currentGroup.filter(d => isDayOff(d, new Set(optimizedDaysOff.map(d => dateKey(d))))).length;
|
||||
const message = `${usedDaysOff} days off -> ${totalDays} days`;
|
||||
function addConsecutiveDaysOff(consecutiveDaysOff, currentGroup, optimizedDaysOff) {
|
||||
const startDate = currentGroup[0];
|
||||
const endDate = currentGroup[currentGroup.length - 1];
|
||||
const totalDays = daysBetween(startDate, endDate) + 1;
|
||||
const usedDaysOff = currentGroup.filter(d => isDayOff(d, new Set(optimizedDaysOff.map(d => dateKey(d))))).length;
|
||||
|
||||
if (totalDays > 2) {
|
||||
consecutiveDaysOff.push({
|
||||
startDate: formatDate(startDate),
|
||||
endDate: formatDate(endDate),
|
||||
includesHoliday,
|
||||
message
|
||||
startDate,
|
||||
endDate,
|
||||
usedDaysOff,
|
||||
totalDays
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -17,14 +17,8 @@
|
||||
let optimizedDaysOff = [];
|
||||
let consecutiveDaysOff = [];
|
||||
let placeholder = "Country";
|
||||
let isFirstClick = true;
|
||||
let inputElement;
|
||||
|
||||
function handleYearChange(event) {
|
||||
year = parseInt(event.target.value);
|
||||
updateHolidays();
|
||||
}
|
||||
|
||||
function handleCountryChange(event) {
|
||||
const fullValue = event.target.value;
|
||||
const countryCode = Object.keys(countriesList).find(code => countriesList[code] === fullValue);
|
||||
@@ -43,7 +37,7 @@
|
||||
tempSpan.style.whiteSpace = 'nowrap';
|
||||
tempSpan.textContent = inputElement.value || inputElement.placeholder;
|
||||
document.body.appendChild(tempSpan);
|
||||
inputElement.style.width = `${tempSpan.offsetWidth + 30}px`;
|
||||
inputElement.style.width = `${tempSpan.offsetWidth + 50}px`;
|
||||
document.body.removeChild(tempSpan);
|
||||
}
|
||||
|
||||
@@ -53,34 +47,15 @@
|
||||
holidays = getHolidaysForYear(countryCode, year);
|
||||
optimizedDaysOff = optimizeDaysOff(holidays, year, daysOff);
|
||||
consecutiveDaysOff = calculateConsecutiveDaysOff(holidays, optimizedDaysOff, year);
|
||||
} else {
|
||||
holidays = [];
|
||||
optimizedDaysOff = [];
|
||||
consecutiveDaysOff = [];
|
||||
}
|
||||
}
|
||||
|
||||
function handleKeyDown(event) {
|
||||
switch (event.key) {
|
||||
case 'ArrowRight':
|
||||
event.preventDefault();
|
||||
year++;
|
||||
updateHolidays();
|
||||
break;
|
||||
case 'ArrowLeft':
|
||||
event.preventDefault();
|
||||
year--;
|
||||
updateHolidays();
|
||||
break;
|
||||
case 'ArrowUp':
|
||||
event.preventDefault();
|
||||
daysOff++;
|
||||
updateHolidays();
|
||||
break;
|
||||
case 'ArrowDown':
|
||||
event.preventDefault();
|
||||
if (daysOff > 0) {
|
||||
daysOff--;
|
||||
updateHolidays();
|
||||
}
|
||||
break;
|
||||
}
|
||||
console.log('Year:', year);
|
||||
console.log('Holidays updated:', holidays);
|
||||
console.log('Optimized Days Off:', optimizedDaysOff);
|
||||
console.log('Consecutive Days Off:', consecutiveDaysOff);
|
||||
}
|
||||
|
||||
function getFlagEmoji(countryCode) {
|
||||
@@ -89,81 +64,98 @@
|
||||
.replace(/./g, char => String.fromCodePoint(127397 + char.charCodeAt()));
|
||||
}
|
||||
|
||||
function handleKeyDown(event) {
|
||||
switch (event.key) {
|
||||
case 'ArrowRight':
|
||||
event.preventDefault();
|
||||
year++;
|
||||
updateHolidays(); // Recalculate holidays for the new year
|
||||
break;
|
||||
case 'ArrowLeft':
|
||||
event.preventDefault();
|
||||
year--;
|
||||
updateHolidays(); // Recalculate holidays for the new year
|
||||
break;
|
||||
case 'ArrowUp':
|
||||
event.preventDefault();
|
||||
daysOff++;
|
||||
updateHolidays(); // Recalculate holidays with updated days off
|
||||
break;
|
||||
case 'ArrowDown':
|
||||
event.preventDefault();
|
||||
if (daysOff > 0) {
|
||||
daysOff--;
|
||||
updateHolidays(); // Recalculate holidays with updated days off
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
onMount(() => {
|
||||
window.addEventListener('keydown', handleKeyDown);
|
||||
return () => {
|
||||
window.removeEventListener('keydown', handleKeyDown);
|
||||
};
|
||||
adjustInputWidth(inputElement);
|
||||
inputElement.addEventListener('input', () => adjustInputWidth(inputElement));
|
||||
inputElement.addEventListener('input', () => {
|
||||
adjustInputWidth(inputElement);
|
||||
const countryCode = Object.keys(countriesList).find(code => countriesList[code] === inputElement.value);
|
||||
});
|
||||
inputElement.addEventListener('focus', () => {
|
||||
inputElement.value = '';
|
||||
adjustInputWidth(inputElement);
|
||||
});
|
||||
window.addEventListener('keydown', handleKeyDown);
|
||||
});
|
||||
|
||||
updateHolidays();
|
||||
console.log(consecutiveDaysOff);
|
||||
</script>
|
||||
|
||||
<style>
|
||||
|
||||
header, footer {
|
||||
.header {
|
||||
max-width: 800px;
|
||||
margin: 40px auto;
|
||||
text-align: center;
|
||||
color: #e0e0e0; /* Monochrome light text */
|
||||
padding: 15px;
|
||||
border-bottom: 1px solid #333; /* Subtle border for separation */
|
||||
}
|
||||
|
||||
h1 {
|
||||
.header h2 {
|
||||
font-size: 2.5em; /* Slightly larger font size */
|
||||
margin: 0;
|
||||
font-size: 2em;
|
||||
}
|
||||
|
||||
footer {
|
||||
border-top: 1px solid #333;
|
||||
font-size: 0.9em;
|
||||
.header p {
|
||||
font-size: 1.1em;
|
||||
}
|
||||
|
||||
.content-box {
|
||||
max-width: 900px;
|
||||
margin: 20px auto;
|
||||
padding: 10px 0;
|
||||
background-color: #1e1e1e; /* Slightly lighter dark background for content boxes */
|
||||
max-width: 1200px;
|
||||
margin: 40px auto;
|
||||
padding: 20px;
|
||||
background-color: #111;
|
||||
border-radius: 8px;
|
||||
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.2);
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.content-box p {
|
||||
text-align: center;
|
||||
font-size: 1em; /* Slightly smaller font size */
|
||||
color: #e0e0e0; /* Light gray text */
|
||||
border-radius: 10px;
|
||||
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.7);
|
||||
margin-bottom: 40px;
|
||||
}
|
||||
|
||||
input {
|
||||
margin: 0 10px;
|
||||
font-size: 0.9em; /* Slightly smaller font size */
|
||||
font-size: 1em;
|
||||
padding: 8px;
|
||||
background-color: #2a2a2a; /* Darker gray for inputs */
|
||||
color: #e0e0e0; /* Light text for inputs */
|
||||
border: 1px solid #444;
|
||||
background-color: transparent;
|
||||
border: 1px solid #555;
|
||||
border-radius: 5px;
|
||||
transition: background-color 0.3s;
|
||||
width: auto; /* Dynamic width based on content */
|
||||
color: #fff;
|
||||
transition: background-color 0.3s, color 0.3s;
|
||||
width: auto;
|
||||
}
|
||||
|
||||
input::hover {
|
||||
background-color: #333; /* Slightly lighter on hover */
|
||||
input:hover {
|
||||
background-color: rgba(255, 255, 255, 0.1);
|
||||
}
|
||||
|
||||
ul {
|
||||
list-style-type: none;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
li {
|
||||
margin: 15px 0;
|
||||
padding: 15px;
|
||||
background-color: #2a2a2a;
|
||||
border-radius: 5px;
|
||||
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.5);
|
||||
input:focus {
|
||||
outline: 2px solid #61dafb;
|
||||
}
|
||||
|
||||
.calendar-grid {
|
||||
@@ -174,83 +166,40 @@
|
||||
padding: 20px;
|
||||
}
|
||||
|
||||
@media (max-width: 768px) {
|
||||
@media (max-width: 1024px) {
|
||||
.calendar-grid {
|
||||
grid-template-columns: repeat(2, 1fr); /* 2 columns on smaller screens */
|
||||
grid-template-columns: repeat(2, 1fr); /* Adjust to 2 columns for medium screens */
|
||||
gap: 10px;
|
||||
padding: 10px;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 600px) {
|
||||
.calendar-grid {
|
||||
grid-template-columns: repeat(2, 1fr); /* Allow 2 columns on smaller screens */
|
||||
gap: 5px;
|
||||
padding: 5px;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 400px) {
|
||||
.calendar-grid {
|
||||
grid-template-columns: repeat(1, 1fr); /* Adjust to 1 column for very small screens */
|
||||
}
|
||||
}
|
||||
|
||||
.calendar-container {
|
||||
width: 100%;
|
||||
aspect-ratio: 1;
|
||||
background-color: #2a2a2a; /* Dark gray for calendar */
|
||||
color: #e0e0e0;
|
||||
max-width: 300px;
|
||||
background-color: #111;
|
||||
color: #fff;
|
||||
border-radius: 5px;
|
||||
padding: 10px;
|
||||
padding: 15px;
|
||||
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;
|
||||
text-align: center;
|
||||
margin: 0 10px;
|
||||
outline: none;
|
||||
transition: width 0.2s;
|
||||
-webkit-appearance: none; /* Remove default styling */
|
||||
-moz-appearance: none;
|
||||
appearance: none;
|
||||
color: #e0e0e0; /* Default text color */
|
||||
}
|
||||
|
||||
.editable-input::placeholder {
|
||||
color: #a0a0a0; /* Slightly grayer text for placeholder */
|
||||
}
|
||||
|
||||
.arrow-controls {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
button {
|
||||
background-color: #2a2a2a;
|
||||
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;
|
||||
}
|
||||
|
||||
.flag {
|
||||
font-size: 2em; /* Adjust the size as needed */
|
||||
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
|
||||
overflow: visible;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.calendar-key {
|
||||
@@ -258,9 +207,8 @@
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
padding: 10px;
|
||||
background-color: #1e1e1e;
|
||||
border-radius: 5px;
|
||||
color: #e0e0e0;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.key-item {
|
||||
@@ -277,7 +225,7 @@
|
||||
}
|
||||
|
||||
.color-box.weekend {
|
||||
background-color: #333;
|
||||
background-color: #585858; /* Muted gray/white */
|
||||
}
|
||||
|
||||
.color-box.optimized {
|
||||
@@ -287,18 +235,91 @@
|
||||
.color-box.holiday {
|
||||
background-color: #3b1e6e;
|
||||
}
|
||||
|
||||
footer {
|
||||
text-align: center;
|
||||
padding: 20px;
|
||||
color: #c5c6c7;
|
||||
font-size: 0.9em;
|
||||
}
|
||||
|
||||
footer a {
|
||||
color: #66fcf1;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
footer a:hover {
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
.arrow-controls {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
button {
|
||||
background-color: #333;
|
||||
border: 1px solid #444;
|
||||
color: #fff;
|
||||
font-size: 1em;
|
||||
cursor: pointer;
|
||||
padding: 5px 10px;
|
||||
margin: 0 10px;
|
||||
border-radius: 5px;
|
||||
box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1), 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: #444;
|
||||
}
|
||||
|
||||
button:active {
|
||||
transform: translateY(2px);
|
||||
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1), inset 0 1px 0 rgba(255, 255, 255, 0.1);
|
||||
}
|
||||
|
||||
button:focus {
|
||||
outline: 2px solid #61dafb;
|
||||
}
|
||||
|
||||
.bold {
|
||||
font-weight: bold;
|
||||
font-size: 1.2em;
|
||||
}
|
||||
|
||||
.flag {
|
||||
font-size: 2em;
|
||||
}
|
||||
|
||||
.day {
|
||||
aspect-ratio: 1;
|
||||
text-align: center;
|
||||
font-size: 0.7em; /* Adjust font size for smaller screens */
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
color: #ddd;
|
||||
background-color: #222;
|
||||
position: relative;
|
||||
}
|
||||
</style>
|
||||
|
||||
<header>
|
||||
<h1>Stretch My Time Off</h1>
|
||||
</header>
|
||||
|
||||
<main>
|
||||
<div class="header">
|
||||
<h2>Stretch My Time Off</h2>
|
||||
<p>
|
||||
In {selectedCountry}, there are {holidays.length} public holidays in {year}.
|
||||
<br />
|
||||
Let's stretch your {daysOff} days off to {consecutiveDaysOff.reduce((total, group) => total + group.totalDays, 0)} vacation days.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div class="content-box">
|
||||
<p>
|
||||
I live in
|
||||
<span class="flag">{getFlagEmoji(Object.keys(countriesList).find(code => countriesList[code] === selectedCountry))}</span>
|
||||
<input bind:this={inputElement} list="countries" class="editable-input bold" bind:value={selectedCountry} placeholder={placeholder} on:input={adjustInputWidth} on:focus={() => { inputElement.value = ''; adjustInputWidth(); }} on:change={handleCountryChange} aria-label="Select country" />
|
||||
<span class="flag" style="vertical-align: middle;">{getFlagEmoji(Object.keys(countriesList).find(code => countriesList[code] === selectedCountry))}</span>
|
||||
<input bind:this={inputElement} list="countries" class="editable-input bold" bind:value={selectedCountry} placeholder={placeholder} on:input={handleCountryChange} on:focus={() => { inputElement.value = ''; adjustInputWidth(); }} aria-label="Select country" />
|
||||
and have
|
||||
<span class="arrow-controls">
|
||||
<button on:click={() => { daysOff++; updateHolidays(); }} aria-label="Increase days off">▲</button>
|
||||
@@ -319,7 +340,7 @@
|
||||
</datalist>
|
||||
</div>
|
||||
|
||||
<div class="content-box">
|
||||
<div class="content-box" id="calendar">
|
||||
<div class="calendar-key">
|
||||
<div class="key-item">
|
||||
<span class="color-box weekend"></span> Weekend
|
||||
@@ -334,7 +355,7 @@
|
||||
<div class="calendar-grid">
|
||||
{#each months as month}
|
||||
<div class="calendar-container">
|
||||
<CalendarMonth {year} {month} {holidays} {optimizedDaysOff} />
|
||||
<CalendarMonth {year} {month} {holidays} {optimizedDaysOff} {consecutiveDaysOff} />
|
||||
</div>
|
||||
{/each}
|
||||
</div>
|
||||
@@ -342,5 +363,5 @@
|
||||
</main>
|
||||
|
||||
<footer>
|
||||
<p>© { new Date().getFullYear() } Stretch My Time Off. All rights reserved.</p>
|
||||
<p>Made with <span style="color: red;">📅</span> by <a href="https://zach.ie" target="_blank">Zach</a></p>
|
||||
</footer>
|
||||
Reference in New Issue
Block a user