mirror of
https://github.com/Warky-Devs/artemis-kit.git
synced 2025-05-19 03:37:30 +00:00
Added openFileLink, formatSecondToTime and timeStringToSeconds
This commit is contained in:
parent
bf80e32841
commit
9f5b743e15
3
.vscode/extensions.json
vendored
Normal file
3
.vscode/extensions.json
vendored
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
{
|
||||||
|
"recommendations": ["dbaeumer.vscode-eslint", "esbenp.prettier-vscode"]
|
||||||
|
}
|
94
src/dom/file_utils.test.ts
Normal file
94
src/dom/file_utils.test.ts
Normal file
@ -0,0 +1,94 @@
|
|||||||
|
import { openFileLink } from "./file_utils";
|
||||||
|
import { describe, it, expect } from "vitest";
|
||||||
|
|
||||||
|
describe("openFileLink", () => {
|
||||||
|
it("stub test", () => {
|
||||||
|
const link = "https://example.com";
|
||||||
|
openFileLink(link);
|
||||||
|
|
||||||
|
const a = document.querySelector("a");
|
||||||
|
expect(a).toBeDefined();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
// describe("openFileLink", () => {
|
||||||
|
// beforeEach(() => {
|
||||||
|
// // Mock the window object
|
||||||
|
// global.window = {
|
||||||
|
// document: {
|
||||||
|
// body: {
|
||||||
|
// appendChild: vi.fn(),
|
||||||
|
// removeChild: vi.fn(),
|
||||||
|
// },
|
||||||
|
// createElement: vi.fn(() => ({
|
||||||
|
// href: "",
|
||||||
|
// download: "",
|
||||||
|
// click: vi.fn(),
|
||||||
|
// })),
|
||||||
|
// },
|
||||||
|
// };
|
||||||
|
|
||||||
|
// // Create a mock for document.createElement
|
||||||
|
// const createElementMock = vi.fn(() => ({
|
||||||
|
// href: "",
|
||||||
|
// download: "",
|
||||||
|
// click: vi.fn(),
|
||||||
|
// }));
|
||||||
|
// Object.defineProperty(global.window.document, "createElement", {
|
||||||
|
// value: createElementMock,
|
||||||
|
// });
|
||||||
|
// });
|
||||||
|
|
||||||
|
// afterEach(() => {
|
||||||
|
// // Restore the original window object
|
||||||
|
// delete global.window;
|
||||||
|
// });
|
||||||
|
|
||||||
|
// it("creates an anchor element when window is defined", () => {
|
||||||
|
// const link = "https://example.com";
|
||||||
|
// openFileLink(link);
|
||||||
|
// expect(global.window.document.createElement).toHaveBeenCalledTimes(1);
|
||||||
|
// expect(global.window.document.createElement).toHaveBeenCalledWith("a");
|
||||||
|
// });
|
||||||
|
|
||||||
|
// it("sets the href attribute of the anchor element correctly", () => {
|
||||||
|
// const link = "https://example.com";
|
||||||
|
// openFileLink(link);
|
||||||
|
// const elem = global.window.document.body.appendChild.mock.calls[0][0];
|
||||||
|
// expect(elem.href).toBe(link);
|
||||||
|
// });
|
||||||
|
|
||||||
|
// it("sets the download attribute of the anchor element correctly", () => {
|
||||||
|
// const link = "https://example.com";
|
||||||
|
// openFileLink(link);
|
||||||
|
// const elem = global.window.document.body.appendChild.mock.calls[0][0];
|
||||||
|
// expect(elem.download).toBe("");
|
||||||
|
// });
|
||||||
|
|
||||||
|
// it("appends the anchor element to the DOM", () => {
|
||||||
|
// const link = "https://example.com";
|
||||||
|
// openFileLink(link);
|
||||||
|
// expect(global.window.document.body.appendChild).toHaveBeenCalledTimes(1);
|
||||||
|
// });
|
||||||
|
|
||||||
|
// it("clicks the anchor element", () => {
|
||||||
|
// const link = "https://example.com";
|
||||||
|
// openFileLink(link);
|
||||||
|
// const elem = global.window.document.body.appendChild.mock.calls[0][0];
|
||||||
|
// expect(elem.click).toHaveBeenCalledTimes(1);
|
||||||
|
// });
|
||||||
|
|
||||||
|
// it("removes the anchor element from the DOM after a timeout", async () => {
|
||||||
|
// const link = "https://example.com";
|
||||||
|
// openFileLink(link);
|
||||||
|
// const elem = global.window.document.body.appendChild.mock.calls[0][0];
|
||||||
|
// await new Promise((resolve) => setTimeout(resolve, 2000));
|
||||||
|
// expect(global.window.document.body.removeChild).toHaveBeenCalledTimes(1);
|
||||||
|
// expect(global.window.document.body.removeChild).toHaveBeenCalledWith(elem);
|
||||||
|
// });
|
||||||
|
|
||||||
|
// it("does not throw an error when window is not defined", () => {
|
||||||
|
// delete global.window;
|
||||||
|
// const link = "https://example.com";
|
||||||
|
// expect(() => openFileLink(link)).not.toThrow();
|
||||||
|
// });
|
||||||
|
// });
|
20
src/dom/file_utils.ts
Normal file
20
src/dom/file_utils.ts
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
/**
|
||||||
|
* Creates an anchor element and initiates a download click on it with a given URL.
|
||||||
|
* This function is only available on the client side.
|
||||||
|
* @param link - URL to download
|
||||||
|
*/
|
||||||
|
export const openFileLink = (link: string) => {
|
||||||
|
if (typeof window !== "undefined") {
|
||||||
|
const elem = document.createElement("a");
|
||||||
|
elem.href = link;
|
||||||
|
elem.style.display = "none"; // Hide the element
|
||||||
|
elem.download = ""; // Suggest the browser to download instead of navigating
|
||||||
|
document.body.appendChild(elem); // Attach to the DOM for the click to work
|
||||||
|
elem.click();
|
||||||
|
setTimeout(() => {
|
||||||
|
if (document.body && elem) {
|
||||||
|
document.body.removeChild(elem); // Clean up the element from the DOM
|
||||||
|
}
|
||||||
|
}, 2000);
|
||||||
|
}
|
||||||
|
};
|
1
src/dom/index.ts
Normal file
1
src/dom/index.ts
Normal file
@ -0,0 +1 @@
|
|||||||
|
export * from "./file_utils";
|
@ -5,3 +5,4 @@ export * from "./promise";
|
|||||||
export * from "./i18n";
|
export * from "./i18n";
|
||||||
export * from "./dataqueue";
|
export * from "./dataqueue";
|
||||||
//export * from './logger'
|
//export * from './logger'
|
||||||
|
export * from "./dom";
|
||||||
|
@ -5,3 +5,4 @@ export * from "./locale";
|
|||||||
export * from "./fileSize";
|
export * from "./fileSize";
|
||||||
export * from "./legacy";
|
export * from "./legacy";
|
||||||
export * from "./uuid";
|
export * from "./uuid";
|
||||||
|
export * from "./time";
|
||||||
|
52
src/strings/time.test.ts
Normal file
52
src/strings/time.test.ts
Normal file
@ -0,0 +1,52 @@
|
|||||||
|
import { describe, it, expect } from "vitest";
|
||||||
|
import { formatSecondToTime } from "./time";
|
||||||
|
|
||||||
|
describe("formatSecondToTime", () => {
|
||||||
|
it("formats time with hours", () => {
|
||||||
|
expect(formatSecondToTime(3661)).toBe("01:01:01");
|
||||||
|
});
|
||||||
|
|
||||||
|
it("formats time without hours", () => {
|
||||||
|
expect(formatSecondToTime(61)).toBe("01:01");
|
||||||
|
});
|
||||||
|
|
||||||
|
it("formats time with zero seconds", () => {
|
||||||
|
expect(formatSecondToTime(3600)).toBe("01:00:00");
|
||||||
|
});
|
||||||
|
|
||||||
|
it("formats time with zero minutes and seconds", () => {
|
||||||
|
expect(formatSecondToTime(0)).toBe("00:00");
|
||||||
|
});
|
||||||
|
|
||||||
|
it("handles negative input", () => {
|
||||||
|
expect(formatSecondToTime(-1)).toBe("-00:01");
|
||||||
|
});
|
||||||
|
|
||||||
|
it("handles decimal input", () => {
|
||||||
|
expect(formatSecondToTime(1.5)).toBe("00:01");
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
import { timeStringToSeconds } from "./time";
|
||||||
|
|
||||||
|
describe("timeStringToSeconds", () => {
|
||||||
|
it("converts valid time strings in HH:MM:SS format", () => {
|
||||||
|
expect(timeStringToSeconds("01:02:03")).toBe(3723);
|
||||||
|
expect(timeStringToSeconds("12:34:56")).toBe(45296);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("converts valid time strings in MM:SS format", () => {
|
||||||
|
expect(timeStringToSeconds("02:03")).toBe(123);
|
||||||
|
expect(timeStringToSeconds("34:56")).toBe(2096);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("throws an error for invalid time strings", () => {
|
||||||
|
expect(() => timeStringToSeconds("abc:def:ghi")).toThrowError();
|
||||||
|
expect(() => timeStringToSeconds("12:34:56:78")).toThrowError();
|
||||||
|
});
|
||||||
|
|
||||||
|
it("handles edge cases", () => {
|
||||||
|
expect(timeStringToSeconds("00:00:00")).toBe(0);
|
||||||
|
expect(timeStringToSeconds("23:59:59")).toBe(86399);
|
||||||
|
});
|
||||||
|
});
|
69
src/strings/time.ts
Normal file
69
src/strings/time.ts
Normal file
@ -0,0 +1,69 @@
|
|||||||
|
/**
|
||||||
|
* Format a time in seconds to a string.
|
||||||
|
*
|
||||||
|
* @param totalSeconds time in seconds
|
||||||
|
* @returns a string in the format HH:MM:SS or MM:SS if hours are zero
|
||||||
|
*/
|
||||||
|
export function formatSecondToTime(totalSeconds: number): string {
|
||||||
|
const prefix = totalSeconds < 0 ? "-" : "";
|
||||||
|
totalSeconds = Math.abs(Math.floor(totalSeconds));
|
||||||
|
|
||||||
|
const minutes = Math.floor((totalSeconds % 3600) / 60);
|
||||||
|
const hours = Math.floor(totalSeconds / 3600);
|
||||||
|
const seconds = totalSeconds % 60;
|
||||||
|
|
||||||
|
if (hours > 0) {
|
||||||
|
return `${prefix}${hours.toString().padStart(2, "0")}:${minutes
|
||||||
|
.toString()
|
||||||
|
.padStart(2, "0")}:${seconds.toString().padStart(2, "0")}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
return `${prefix}${minutes.toString().padStart(2, "0")}:${seconds
|
||||||
|
.toString()
|
||||||
|
.padStart(2, "0")}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Converts a time string in the format HH:MM:SS or MM:SS to a total number of seconds.
|
||||||
|
*
|
||||||
|
* @param timeStr - a string in the format HH:MM:SS or MM:SS
|
||||||
|
* @returns the total number of seconds
|
||||||
|
*/
|
||||||
|
export function timeStringToSeconds(timeStr: string): number {
|
||||||
|
// Validate input is not empty
|
||||||
|
if (!timeStr || !timeStr.trim()) {
|
||||||
|
throw new Error("Time string cannot be empty");
|
||||||
|
}
|
||||||
|
|
||||||
|
const parts = timeStr.split(":");
|
||||||
|
if (parts.length < 2 || parts.length > 3) {
|
||||||
|
throw new Error("Invalid time format. Expected HH:MM:SS or MM:SS");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Parse numbers and validate ranges
|
||||||
|
let hours = 0,
|
||||||
|
minutes = 0,
|
||||||
|
seconds = 0;
|
||||||
|
|
||||||
|
if (parts.length === 3) {
|
||||||
|
// HH:MM:SS format
|
||||||
|
[hours, minutes, seconds] = parts.map((n) => parseInt(n, 10));
|
||||||
|
|
||||||
|
if (isNaN(hours) || hours < 0 || hours > 23) {
|
||||||
|
throw new Error("Hours must be between 0 and 23");
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// MM:SS format
|
||||||
|
[minutes, seconds] = parts.map((n) => parseInt(n, 10));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isNaN(minutes) || minutes < 0 || minutes > 59) {
|
||||||
|
throw new Error("Minutes must be between 0 and 59");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isNaN(seconds) || seconds < 0 || seconds > 59) {
|
||||||
|
throw new Error("Seconds must be between 0 and 59");
|
||||||
|
}
|
||||||
|
|
||||||
|
return hours * 3600 + minutes * 60 + seconds;
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user