may this be Big

This commit is contained in:
lda committed 2025-05-22 23:51:03 +07:00
1 parent 9100207c59
commit 12dc1c1550
7 files changed
+177 -83

No files matched your search

+10
View File
@@ -0,0 +1,10 @@
{
"compilerOptions": {
"checkJs": true,
"baseUrl": "./",
"paths": {
"@fullcalendar/*": ["node_modules/@fullcalendar/*"]
}
},
"include": ["src/**/*"]
}
+37 -4
View File
@@ -11,7 +11,21 @@ import { getNextColor } from "./color_generator";
import { glued } from "./main";
let calendar = null;
// /**
// * @typedef {import("@fullcalendar/core").Calendar} Calendar
// * @typedef {import("@fullcalendar/core").EventSourceInput} EventSourceInput
// */
let /** @type {Calendar | null} */ calendar = null;
/**
* Initializes and renders the FullCalendar instance.
*
* @param {HTMLElement} element - The DOM element to render the calendar into.
* @param {string | URL} url - The URL for the initial event source.
* @param {Function} onEventMouseEnter - Callback for mouse enter on events.
* @param {Function} onEventMouseLeave - Callback for mouse leave on events.
*/
export function initializeCalendar(
element,
@@ -49,25 +63,44 @@ export function initializeCalendar(
timeGridDay: { buttonText: "Day" },
listMonth: { buttonText: "List" },
},
events: init_ics_source(url),
eventMouseEnter: onEventMouseEnter,
eventMouseLeave: onEventMouseLeave,
events: init_ics_source(url.toString()), // Convert URL to string
eventMouseEnter:
/** @type {(arg: import("@fullcalendar/core").EventHoveringArg) => void} */ (
onEventMouseEnter
),
eventMouseLeave:
/** @type {(arg: import("@fullcalendar/core").EventHoveringArg) => void} */ (
onEventMouseLeave
),
loading: glued,
});
// console.log(calendar);
calendar.render();
}
/**
* Adds a new event source to the calendar.
* @param {string} url - The URL for the event source.
*/
export const addEventSource = (url) => {
if (calendar) {
calendar.addEventSource(init_ics_source(url));
}
};
/**
* Returns the current calendar instance.
* @returns {Calendar | null} The FullCalendar instance.
*/
export const getCalendarInstance = () => {
return calendar;
};
/**
* Initializes an event source object.
* @param {string} url - The URL for the event source.
* @returns {import("@fullcalendar/core").EventSourceInput} The event source object.
*/
const init_ics_source = (url) => {
return {
url: url,
+85
View File
@@ -0,0 +1,85 @@
/**
* Updates the event source list in the DOM.
* @param {HTMLElement | null} eventSourceList - The DOM element to display the event sources.
* @returns {Function} A function that updates the event source list for a given calendar.
*/
export const updateEventSourceList2 = (eventSourceList) =>
/**
* Updates the event source list.
* @param {import("@fullcalendar/core").Calendar} calendar - The FullCalendar instance.
*/
function updateEventSourceList(calendar) {
if (!calendar) return;
// const eventSourceList = document.getElementById("event-source-list");
if (!eventSourceList) return;
// Clear the current list
eventSourceList.innerHTML = "";
// Fetch all event sources
calendar.getEventSources().forEach((source) => {
console.log(source);
const card = document.createElement("div");
card.className = "event-source-card";
card.style.display = "flex";
card.style.justifyContent = "space-between";
card.style.alignItems = "center";
// Fix type for borderColor
const borderColor =
source.internalEventSource.ui?.borderColor || "#007bff";
card.style.borderLeft = `4px solid ${borderColor}`;
card.style.padding = "0.5em";
card.style.marginBottom = "0.5em";
const info = document.createElement("div");
const url = source.url || "(No URL)";
const format = source.format || "(Unknown Format)";
const sourceInfo = document.createElement("div");
sourceInfo.innerHTML = `<strong>Source:</strong> ${url}<br><strong>Format:</strong> ${format}`;
const loadingStatus = document.createElement("div");
loadingStatus.className = "loading-status";
loadingStatus.textContent = source.internalEventSource.isFetching
? "Fetching in progress"
: "Loaded";
info.appendChild(sourceInfo);
info.appendChild(loadingStatus);
// Add Refetch Button
const refetchButton = document.createElement("button");
refetchButton.textContent = "Refetch";
refetchButton.className = "btn btn-sm btn-primary";
refetchButton.style.marginRight = "0.5em";
refetchButton.addEventListener("click", () => {
source.refetch();
alert("Source refetched!");
});
// Add Remove Button
const removeButton = document.createElement("button");
removeButton.textContent = "Remove";
removeButton.className = "btn btn-sm btn-danger";
removeButton.addEventListener("click", () => {
source.remove();
updateEventSourceList(calendar); // Refresh the list after removal
});
// Create a button container
const buttonContainer = document.createElement("div");
buttonContainer.appendChild(refetchButton);
buttonContainer.appendChild(removeButton);
// Append info and buttons to the card
card.appendChild(info);
card.appendChild(buttonContainer);
// Append the card to the event source list
eventSourceList.appendChild(card);
});
};
+20 -68
View File
@@ -4,18 +4,36 @@ import {
getCalendarInstance,
} from "./calendar";
import { newTippy, tippyDie } from "./tooltip";
import { updateEventSourceList2 } from "./eventSourceManager";
import { create_url } from "./utils";
import "bootstrap/dist/css/bootstrap.css";
import "bootstrap-icons/font/bootstrap-icons.css";
import "tippy.js/dist/tippy.css";
// Fix type for text_field
/** @type {HTMLInputElement} */
// @ts-ignore
const text_field = document.getElementById("ics_file");
/** @type {HTMLButtonElement} */
// @ts-ignore
const confirm_button = document.getElementById("ics_run");
/** @type {HTMLButtonElement} */
// @ts-ignore
const add_button = document.getElementById("ics_add");
/** @type {HTMLElement} */
const cal_ele = document.getElementById("calendar");
/** @type {HTMLElement} */
const placeholder_message = document.getElementById("placeholder-message");
/** @type {HTMLElement} */
const eventSourceList = document.getElementById("event-source-list");
const updateEventSourceList = updateEventSourceList2(eventSourceList);
function showPlaceholder() {
placeholder_message.style.display = "block";
@@ -27,74 +45,7 @@ function hidePlaceholder() {
cal_ele.style.display = "block";
}
function updateEventSourceList(calendar) {
if (!calendar) return;
// Clear the current list
eventSourceList.innerHTML = "";
// Fetch all event sources
calendar.getEventSources().forEach((source) => {
const card = document.createElement("div");
card.className = "event-source-card";
card.style.display = "flex";
card.style.justifyContent = "space-between";
card.style.alignItems = "center";
card.style.borderLeft = `4px solid ${source.color || "#007bff"}`;
card.style.padding = "0.5em";
card.style.marginBottom = "0.5em";
// Create info container
const info = document.createElement("div");
const url = source.url || "(No URL)";
const format = source.format || "(Unknown Format)";
const sourceInfo = document.createElement("div");
sourceInfo.innerHTML = `<strong>Source:</strong> ${url}<br><strong>Format:</strong> ${format}`;
const loadingStatus = document.createElement("div");
loadingStatus.className = "loading-status";
loadingStatus.textContent = source.internalEventSource.isFetching
? "Fetching in progress"
: "Loaded";
info.appendChild(sourceInfo);
info.appendChild(loadingStatus);
// Add Refetch Button
const refetchButton = document.createElement("button");
refetchButton.textContent = "Refetch";
refetchButton.className = "btn btn-sm btn-primary";
refetchButton.style.marginRight = "0.5em";
refetchButton.addEventListener("click", () => {
source.refetch();
alert("Source refetched!");
});
// Add Remove Button
const removeButton = document.createElement("button");
removeButton.textContent = "Remove";
removeButton.className = "btn btn-sm btn-danger";
removeButton.addEventListener("click", () => {
source.remove();
updateEventSourceList(calendar); // Refresh the list after removal
});
// Create a button container
const buttonContainer = document.createElement("div");
buttonContainer.appendChild(refetchButton);
buttonContainer.appendChild(removeButton);
// Append info and buttons to the card
card.appendChild(info);
card.appendChild(buttonContainer);
// Append the card to the event source list
eventSourceList.appendChild(card);
});
}
export const glued = (e) => {
export const glued = (_e) => {
// console.log("loading:", e); // half of it will be true false and half of it will be events
setTimeout(() => updateEventSourceList(getCalendarInstance()));
@@ -120,4 +71,5 @@ add_button.addEventListener("click", () => {
return;
}
addEventSource(url);
updateEventSourceList(getCalendarInstance());
});
-2
View File
@@ -31,5 +31,3 @@ export function tippyDie(info) {
currentTippy.hideWithInteractivity(info.jsEvent);
}
}
+16 -3
View File
@@ -1,11 +1,19 @@
/** Escapes HTML special characters to prevent XSS */
/**
* Escapes HTML special characters to prevent XSS.
* @param {string} text - The input text to escape.
* @returns {string} The escaped HTML string.
*/
export function escapeHtml(text) {
const div = document.createElement("div");
div.innerText = text;
return div.innerHTML;
}
/** Converts URLs in text to clickable links, after escaping */
/**
* Converts URLs in text to clickable links, after escaping.
* @param {string} text - The input text containing URLs.
* @returns {string} The linkified HTML string.
*/
export function linkify(text) {
let safe = escapeHtml(text);
return safe.replace(
@@ -15,7 +23,12 @@ export function linkify(text) {
);
}
/** Formats tooltip content: escapes, linkifies, and replaces newlines with \<br> */
/**
* Formats tooltip content: escapes, linkifies, and replaces newlines with <br>.
* @param {string} title - The title of the tooltip.
* @param {string} [description] - The optional description of the tooltip.
* @returns {string} The formatted tooltip content.
*/
export function formatTooltip(title, description) {
const safeTitle = escapeHtml(title);
const safeDesc = description
+9 -6
View File
@@ -1,10 +1,13 @@
function create_url(url) {
/**
* Creates a valid URL from the given input.
* @param {string} input - The input string to convert into a URL.
* @returns {string | null} - The valid URL or null if invalid.
*/
export function create_url(input) {
try {
return new URL(url);
} catch (e) {
// console.info(e);
const url = new URL(input);
return url.href;
} catch {
return null;
}
}
export { create_url };