Refactor and polish calendar app: improved accessibility, cleaned up styles, and ensured responsiveness

This commit is contained in:
lda committed 2025-05-26 03:25:00 +07:00
1 parent 6b27ec8baa
commit 0a0e623dad
10 files changed
+64 -43

No files matched your search

-1
View File
@@ -25,4 +25,3 @@ dist-ssr
# lda
ass/
+15 -8
View File
@@ -15,12 +15,12 @@
<h1>🗓️ Calendar Filter App</h1>
<p class="subtitle">Load, filter, and combine calendars with style.</p>
</header>
<div class="flex_input">
<form class="flex_input" onsubmit="return false;">
<input type="text" id="ics_file" placeholder="Paste .ics URL or API endpoint here" />
<input type="button" value="new calendar" id="ics_run" />
<input type="button" value="add a calendar" id="ics_add" />
</div>
<div class="info-bar" style="text-align: center">
<button type="button" class="btn btn-primary" id="ics_run">New Calendar</button>
<button type="button" class="btn btn-primary" id="ics_add">Add a Calendar</button>
</form>
<div class="info-bar">
<span>Tip: You can add multiple calendars. Hover events for details.
<a href="https://fullcalendar.io/" target="_blank" rel="noopener noreferrer">Powered by
FullCalendar</a></span>
@@ -30,18 +30,25 @@
No calendar loaded yet. Paste a URL to get started!
</div>
<div id="calendar">
<div id="calendar"></div>
<!-- <div id="calendar"></div> -->
</div>
</div>
<div class="event-source-management" id="event-source-management">
<h2>Event Sources</h2>
<div class="d-flex justify-content-between align-items-center mb-3">
<h4>Event Sources</h4>
<button id="reload" class="btn btn-outline-primary">
<i class="bi bi-arrow-clockwise"></i> Reload
</button>
</div>
<div id="event-source-list">
<!-- Event source cards will be dynamically added here -->
</div>
</div>
<footer class="app-footer">
<p>Made with Rust, JS, and ❤️</p>
<p>by
<p>
by
<a href="//ldlda.com" target="_blank" rel="noopener noreferrer">lđa</a>
</p>
</footer>
+2 -1
View File
@@ -23,7 +23,7 @@ export function initializeCalendar(
url: string | URL,
onEventMouseEnter: (arg: EventHoveringArg) => void,
onEventMouseLeave: (arg: EventHoveringArg) => void
): void {
): Calendar {
if (calendar) {
calendar.destroy();
}
@@ -60,6 +60,7 @@ export function initializeCalendar(
loading: glued,
});
calendar.render();
return calendar // its real now
}
/**
+1 -1
View File
@@ -1,5 +1,5 @@
// Start with a random hue, but keep saturation and lightness fixed for "nice" colors
let sourceCount = Math.floor(Math.random() * 360);
let sourceCount = Math.floor(Math.random() * 25);
/**
* Generates the next color in a sequence using the golden angle.
+3 -3
View File
@@ -7,8 +7,8 @@ import { Calendar } from "@fullcalendar/core";
*/
export const updateEventSourceList2 = (eventSourceList: HTMLElement | null) => {
return function updateEventSourceList(calendar: Calendar): void {
if (!calendar) return;
if (!eventSourceList) return;
if (!calendar) return;
// Clear the current list
eventSourceList.innerHTML = "";
@@ -36,8 +36,8 @@ export const updateEventSourceList2 = (eventSourceList: HTMLElement | null) => {
refetchButton.textContent = "Refetch";
refetchButton.className = "btn btn-sm btn-primary align-right";
refetchButton.addEventListener("click", () => {
source.refetch();
alert("Source refetched!");
source.refetch(); // loading callback handled thing
// alert("Source refetched!");
});
// Add Remove Button
+32 -14
View File
@@ -26,9 +26,13 @@ const placeholder_message = document.getElementById(
const eventSourceList = document.getElementById(
"event-source-list"
) as HTMLElement;
const eventmanage = document.getElementById(
"event-source-management"
) as HTMLElement;
const reload_button = document.getElementById("reload") as HTMLButtonElement;
const updateEventSourceList = updateEventSourceList2(eventSourceList);
/**
@@ -56,15 +60,22 @@ function hidePlaceholder(): void {
export const glued = (_e: unknown): void => {
const calendar = getCalendarInstance();
if (calendar) {
setTimeout(() => updateEventSourceList(calendar));
setTimeout(() => updateEventSourceList(calendar)); // ahh
}
};
/**
* Handles the click event for the confirm button.
* Validates the input URL and initializes the calendar.
*/
confirm_button.addEventListener("click", () => {
// Remove existing listeners before adding new ones
confirm_button.removeEventListener("click", handleConfirmClick);
confirm_button.addEventListener("click", handleConfirmClick);
add_button.removeEventListener("click", handleAddClick);
add_button.addEventListener("click", handleAddClick);
reload_button.removeEventListener("click", handleReloadClick);
reload_button.addEventListener("click", handleReloadClick);
// Define the event handler functions
function handleConfirmClick(): void {
const url = create_url(text_field.value);
if (!url)
return text_field.value
@@ -72,14 +83,11 @@ confirm_button.addEventListener("click", () => {
: showPlaceholder();
hidePlaceholder();
initializeCalendar(cal_ele, url, newTippy, tippyDie);
});
const calendar = initializeCalendar(cal_ele, url, newTippy, tippyDie);
updateEventSourceList(calendar);
}
/**
* Handles the click event for the add button.
* Validates the input URL and adds a new event source to the calendar.
*/
add_button.addEventListener("click", () => {
function handleAddClick(): void {
const url = create_url(text_field.value);
if (!url) {
if (text_field.value) alert("Please input valid url.");
@@ -90,4 +98,14 @@ add_button.addEventListener("click", () => {
addEventSource(url);
updateEventSourceList(calendar);
}
});
}
function handleReloadClick(): void {
const calendar = getCalendarInstance();
if (calendar) {
updateEventSourceList(calendar);
alert("Event sources reloaded!");
} else {
alert("No calendar instance found.");
}
}
+8 -13
View File
@@ -11,16 +11,12 @@ body {
justify-content: center;
align-items: center;
min-height: 100vh;
/* max-width: 900px; */
/* Removed to allow full responsiveness */
}
.container {
width: 100%;
padding: 1em;
box-sizing: border-box;
/* max-width: 900px; */
/* Removed to allow Bootstrap container to handle layout */
}
.app-header {
@@ -34,13 +30,11 @@ body {
margin: 2em auto 1em auto;
align-items: center;
justify-content: flex-end;
/* max-width: 900px; */
/* Removed to allow full responsiveness */
}
#ics_file {
flex: 1 1 0;
min-width: 200px;
/* min-width: 200px; */
max-width: 100%;
font-size: 1.1em;
padding: 0.5em 0.8em;
@@ -50,7 +44,8 @@ body {
box-sizing: border-box;
}
input[type="button"] {
/* trying bootstraps button */
/* input[type="button"] {
padding: 0.5em 1.2em;
font-size: 1em;
border: none;
@@ -62,6 +57,10 @@ input[type="button"] {
}
input[type="button"]:hover {
background: #0056b3;
} */
.info-bar {
text-align: center;
}
#calendar {
@@ -76,8 +75,6 @@ input[type="button"]:hover {
border-radius: 8px;
box-shadow: 0 2px 12px rgba(0, 0, 0, 0.07);
padding: 1em 0.5em 1em 0.5em;
/* max-width: 900px; */
/* Removed to allow full responsiveness */
}
/* Placeholder message */
@@ -92,8 +89,6 @@ input[type="button"]:hover {
.flex_input {
flex-wrap: wrap;
gap: 0.7em;
/* max-width: 98vw; */
/* Removed to allow Bootstrap container to handle layout */
}
#calendar {
padding: 0.5em 0.1em 1em 0.1em;
@@ -125,13 +120,13 @@ input[type="button"]:hover {
display: flex;
justify-content: space-between;
align-items: stretch;
border: 1px solid #ddd;
border-left: 4px solid #007bff; /* Default border color */
padding: 0.5em;
margin-bottom: 0.5em;
background: #ffffff;
border-radius: 4px;
box-shadow: 0 1px 4px rgba(0, 0, 0, 0.1);
/* border: 1px solid #ddd; */
background-color: #f9f9f9;
flex-wrap: wrap; /* Allow wrapping for smaller screens */
}
+1 -1
View File
@@ -36,5 +36,5 @@ export function formatTooltip(title: string, description?: string): string {
const escapedTitle = escapeHtml(title);
const escapedDescription = description ? escapeHtml(description) : "";
const linkifiedDescription = linkify(escapedDescription);
return `${escapedTitle}<br>${linkifiedDescription}`;
return `<p>${escapedTitle}</p><p>${linkifiedDescription}</p>`;
}
+1 -1
View File
@@ -12,6 +12,6 @@
"@fullcalendar/*": ["node_modules/@fullcalendar/*"]
}
},
"include": ["src/**/*"],
"include": ["src/**/*", "vite-env.d.ts"],
"exclude": ["node_modules", "dist"]
}
+1
View File
@@ -0,0 +1 @@
/// <reference types="vite/client" />