refactor(v4.0.0-alpha): beginning of a new era

This commit is contained in:
Amruth Pillai
2023-11-05 12:31:42 +01:00
parent 0ba6a444e2
commit 22933bd412
505 changed files with 81829 additions and 0 deletions

11
libs/utils/src/index.ts Normal file
View 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";

View 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;
};

View 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];
};

View 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),
});
});
};

View 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;
};

File diff suppressed because it is too large Load Diff

View 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>;
};

View File

@ -0,0 +1,10 @@
export const pageSizeMap = {
a4: {
width: 210,
height: 297,
},
letter: {
width: 216,
height: 279,
},
};

View 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;
};

View 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 };
};

View 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));

View 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[];
};