mirror of
https://github.com/AmruthPillai/Reactive-Resume.git
synced 2025-11-17 10:11:31 +10:00
refactor(v4.0.0-alpha): beginning of a new era
This commit is contained in:
11
libs/utils/src/index.ts
Normal file
11
libs/utils/src/index.ts
Normal file
@ -0,0 +1,11 @@
|
||||
export * from "./namespaces/array";
|
||||
export * from "./namespaces/cefr";
|
||||
export * from "./namespaces/csv";
|
||||
export * from "./namespaces/date";
|
||||
export * from "./namespaces/fonts";
|
||||
export * from "./namespaces/object";
|
||||
export * from "./namespaces/page";
|
||||
export * from "./namespaces/promise";
|
||||
export * from "./namespaces/string";
|
||||
export * from "./namespaces/style";
|
||||
export * from "./namespaces/types";
|
||||
76
libs/utils/src/namespaces/array.ts
Normal file
76
libs/utils/src/namespaces/array.ts
Normal file
@ -0,0 +1,76 @@
|
||||
import { LayoutLocator } from "./types";
|
||||
|
||||
type CombinationsInput<T> = {
|
||||
[K in keyof T]: T[K][];
|
||||
};
|
||||
|
||||
export const generateCombinations = <T>(obj: CombinationsInput<T>): Array<T> => {
|
||||
const keys = Object.keys(obj) as (keyof T)[];
|
||||
|
||||
const results: Array<T> = [];
|
||||
|
||||
function backtrack(combination: Partial<T>, index: number): void {
|
||||
if (index === keys.length) {
|
||||
results.push(combination as T);
|
||||
return;
|
||||
}
|
||||
|
||||
const key = keys[index];
|
||||
const values = obj[key];
|
||||
|
||||
for (const value of values) {
|
||||
backtrack({ ...combination, [key]: value }, index + 1);
|
||||
}
|
||||
}
|
||||
|
||||
backtrack({}, 0);
|
||||
|
||||
return results;
|
||||
};
|
||||
|
||||
// Function to find a specific item in a layout
|
||||
export const findItemInLayout = (item: string, layout: string[][][]): LayoutLocator | null => {
|
||||
for (let page = 0; page < layout.length; page++) {
|
||||
for (let column = 0; column < layout[page].length; column++) {
|
||||
for (let section = 0; section < layout[page][column].length; section++) {
|
||||
if (layout[page][column][section] === item) {
|
||||
return { page, column, section };
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
};
|
||||
|
||||
// Function to remove a specific item in a layout
|
||||
export const removeItemInLayout = (item: string, layout: string[][][]): LayoutLocator | null => {
|
||||
const locator = findItemInLayout(item, layout);
|
||||
|
||||
if (locator) {
|
||||
layout[locator.page][locator.column].splice(locator.section, 1);
|
||||
}
|
||||
|
||||
return locator;
|
||||
};
|
||||
|
||||
// Function to move an item within a layout
|
||||
export const moveItemInLayout = (
|
||||
current: LayoutLocator,
|
||||
target: LayoutLocator,
|
||||
layout: string[][][],
|
||||
): string[][][] => {
|
||||
// Create a deep copy of the layout to avoid mutating the original array
|
||||
const newLayout = JSON.parse(JSON.stringify(layout)) as string[][][];
|
||||
|
||||
// Get the item from the current location
|
||||
const item = newLayout[current.page][current.column][current.section];
|
||||
|
||||
// Remove the item from the current location
|
||||
newLayout[current.page][current.column].splice(current.section, 1);
|
||||
|
||||
// Insert the item at the target location
|
||||
newLayout[target.page][target.column].splice(target.section, 0, item);
|
||||
|
||||
return newLayout;
|
||||
};
|
||||
13
libs/utils/src/namespaces/cefr.ts
Normal file
13
libs/utils/src/namespaces/cefr.ts
Normal file
@ -0,0 +1,13 @@
|
||||
// CEFR Levels
|
||||
const cefrMap = {
|
||||
1: "A1",
|
||||
2: "A2",
|
||||
3: "B1",
|
||||
4: "B2",
|
||||
5: "C1",
|
||||
6: "C2",
|
||||
};
|
||||
|
||||
export const getCEFRLevel = (level: number) => {
|
||||
return cefrMap[level as keyof typeof cefrMap];
|
||||
};
|
||||
14
libs/utils/src/namespaces/csv.ts
Normal file
14
libs/utils/src/namespaces/csv.ts
Normal file
@ -0,0 +1,14 @@
|
||||
import Papa from "papaparse";
|
||||
|
||||
import { Json } from "./types";
|
||||
|
||||
export const parseCSV = async (string: string) => {
|
||||
return new Promise<Json[]>((resolve, reject) => {
|
||||
Papa.parse(string, {
|
||||
header: true,
|
||||
skipEmptyLines: true,
|
||||
complete: (results) => resolve(results.data as Json[]),
|
||||
error: (error: Error) => reject(error),
|
||||
});
|
||||
});
|
||||
};
|
||||
39
libs/utils/src/namespaces/date.ts
Normal file
39
libs/utils/src/namespaces/date.ts
Normal file
@ -0,0 +1,39 @@
|
||||
import dayjs from "dayjs";
|
||||
|
||||
export const sortByDate = <T>(a: T, b: T, key: keyof T, desc = true) => {
|
||||
if (!a[key] || !b[key]) return 0;
|
||||
if (!(a[key] instanceof Date) || !(b[key] instanceof Date)) return 0;
|
||||
|
||||
if (desc) return dayjs(a[key] as Date).isBefore(dayjs(b[key] as Date)) ? 1 : -1;
|
||||
else return dayjs(a[key] as Date).isBefore(dayjs(b[key] as Date)) ? -1 : 1;
|
||||
};
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
export const deepSearchAndParseDates = (obj: any, dateKeys: string[]): any => {
|
||||
if (typeof obj !== "object" || obj === null) {
|
||||
return obj;
|
||||
}
|
||||
|
||||
const keys = Object.keys(obj);
|
||||
|
||||
if (keys.length === 0) {
|
||||
return obj;
|
||||
}
|
||||
|
||||
for (const key of keys) {
|
||||
let value = obj[key];
|
||||
|
||||
if (dateKeys.includes(key)) {
|
||||
if (typeof value === "string") {
|
||||
const parsedDate = new Date(value);
|
||||
if (!isNaN(parsedDate.getTime())) {
|
||||
value = parsedDate;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
obj[key] = deepSearchAndParseDates(value, dateKeys);
|
||||
}
|
||||
|
||||
return obj;
|
||||
};
|
||||
10379
libs/utils/src/namespaces/fonts.ts
Normal file
10379
libs/utils/src/namespaces/fonts.ts
Normal file
File diff suppressed because it is too large
Load Diff
7
libs/utils/src/namespaces/object.ts
Normal file
7
libs/utils/src/namespaces/object.ts
Normal file
@ -0,0 +1,7 @@
|
||||
export const exclude = <T, Key extends keyof T>(object: T, keys: Key[]): Omit<T, Key> => {
|
||||
if (!object) return object;
|
||||
|
||||
return Object.fromEntries(
|
||||
Object.entries(object).filter(([key]) => !keys.includes(key as Key)),
|
||||
) as Omit<T, Key>;
|
||||
};
|
||||
10
libs/utils/src/namespaces/page.ts
Normal file
10
libs/utils/src/namespaces/page.ts
Normal file
@ -0,0 +1,10 @@
|
||||
export const pageSizeMap = {
|
||||
a4: {
|
||||
width: 210,
|
||||
height: 297,
|
||||
},
|
||||
letter: {
|
||||
width: 216,
|
||||
height: 279,
|
||||
},
|
||||
};
|
||||
11
libs/utils/src/namespaces/promise.ts
Normal file
11
libs/utils/src/namespaces/promise.ts
Normal file
@ -0,0 +1,11 @@
|
||||
export const delay = (time: number) => new Promise((resolve) => setTimeout(resolve, time));
|
||||
|
||||
export const withTimeout = async <T>(promise: Promise<T>, time: number): Promise<T> => {
|
||||
const timeout = new Promise((_, reject) =>
|
||||
setTimeout(() => {
|
||||
reject(new Error(`Operation timed out after ${time} ms.`));
|
||||
}, time),
|
||||
);
|
||||
|
||||
return Promise.race([promise, timeout]) as T;
|
||||
};
|
||||
58
libs/utils/src/namespaces/string.ts
Normal file
58
libs/utils/src/namespaces/string.ts
Normal file
@ -0,0 +1,58 @@
|
||||
import { adjectives, animals, uniqueNamesGenerator } from "unique-names-generator";
|
||||
|
||||
import { LayoutLocator, SortablePayload } from "./types";
|
||||
|
||||
export const getInitials = (name: string) => {
|
||||
const regex = new RegExp(/(\p{L}{1})\p{L}+/, "gu");
|
||||
const initials = [...name.matchAll(regex)] || [];
|
||||
|
||||
return ((initials.shift()?.[1] || "") + (initials.pop()?.[1] || "")).toUpperCase();
|
||||
};
|
||||
|
||||
export const isUrl = (string: string) => {
|
||||
const urlRegex = /https?:\/\/[^ \n]+/i;
|
||||
|
||||
return urlRegex.test(string);
|
||||
};
|
||||
|
||||
export const extractUrl = (string: string) => {
|
||||
const urlRegex = /https?:\/\/[^ \n]+/i;
|
||||
|
||||
const result = string.match(urlRegex);
|
||||
return result ? result[0] : null;
|
||||
};
|
||||
|
||||
export const kebabCase = (string?: string | null) => {
|
||||
if (!string) return "";
|
||||
|
||||
return (
|
||||
string
|
||||
.match(/[A-Z]{2,}(?=[A-Z][a-z]+[0-9]*|\b)|[A-Z]?[a-z]+[0-9]*|[A-Z]|[0-9]+/g)
|
||||
?.join("-")
|
||||
.toLowerCase() ?? ""
|
||||
);
|
||||
};
|
||||
|
||||
export const generateRandomName = () => {
|
||||
return uniqueNamesGenerator({
|
||||
dictionaries: [adjectives, adjectives, animals],
|
||||
style: "capital",
|
||||
separator: " ",
|
||||
length: 3,
|
||||
});
|
||||
};
|
||||
|
||||
export const processUsername = (string?: string | null) => {
|
||||
if (!string) return "";
|
||||
|
||||
return string.replace(/[^a-zA-Z0-9-.]/g, "").toLowerCase();
|
||||
};
|
||||
|
||||
export const parseLayoutLocator = (payload: SortablePayload | null): LayoutLocator => {
|
||||
if (!payload) return { page: 0, column: 0, section: 0 };
|
||||
|
||||
const section = payload.index as number;
|
||||
const [page, column] = payload.containerId.split(".").map(Number);
|
||||
|
||||
return { page, column, section };
|
||||
};
|
||||
13
libs/utils/src/namespaces/style.ts
Normal file
13
libs/utils/src/namespaces/style.ts
Normal file
@ -0,0 +1,13 @@
|
||||
import { ClassValue, clsx } from "clsx";
|
||||
import { twMerge } from "tailwind-merge";
|
||||
|
||||
export const breakpoints = {
|
||||
xs: 0,
|
||||
sm: 640,
|
||||
md: 768,
|
||||
lg: 1024,
|
||||
xl: 1280,
|
||||
"2xl": 1400,
|
||||
};
|
||||
|
||||
export const cn = (...inputs: ClassValue[]) => twMerge(clsx(inputs));
|
||||
15
libs/utils/src/namespaces/types.ts
Normal file
15
libs/utils/src/namespaces/types.ts
Normal file
@ -0,0 +1,15 @@
|
||||
export type Json = Record<string, unknown>;
|
||||
|
||||
export type LayoutLocator = { page: number; column: number; section: number };
|
||||
|
||||
export type DeepPartial<T> = T extends object ? { [P in keyof T]?: DeepPartial<T[P]> } : T;
|
||||
|
||||
export type FilterKeys<T, Condition> = {
|
||||
[Key in keyof T]: T[Key] extends Condition ? Key : never;
|
||||
}[keyof T];
|
||||
|
||||
export type SortablePayload = {
|
||||
index: number;
|
||||
containerId: string;
|
||||
items: string[];
|
||||
};
|
||||
Reference in New Issue
Block a user