@studiocubics/utils 0.0.1 → 0.0.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +3 -3
- package/.turbo/turbo-build.log +0 -17
- package/CHANGELOG.md +0 -7
- package/src/API/_index.ts +0 -1
- package/src/API/apiRes.ts +0 -74
- package/src/Calculations/_index.ts +0 -1
- package/src/Calculations/calculateSafePosition.ts +0 -137
- package/src/Code/_index.ts +0 -1
- package/src/Code/delay.ts +0 -5
- package/src/Dates/_index.ts +0 -2
- package/src/Dates/formateDate.ts +0 -140
- package/src/Dates/relativeTime.ts +0 -64
- package/src/Forms/_index.ts +0 -1
- package/src/Forms/initialiseForm.ts +0 -14
- package/src/Numbers/_index.ts +0 -1
- package/src/Numbers/remap.ts +0 -28
- package/src/React/_index.ts +0 -1
- package/src/React/mergeRefs.ts +0 -16
- package/src/Strings/_index.ts +0 -3
- package/src/Strings/cn.ts +0 -3
- package/src/Strings/cssSafeString.ts +0 -13
- package/src/Strings/stringCases.ts +0 -50
- package/src/index.ts +0 -8
- package/tsconfig.json +0 -32
package/package.json
CHANGED
|
@@ -5,17 +5,17 @@
|
|
|
5
5
|
"access": "public"
|
|
6
6
|
},
|
|
7
7
|
"private": false,
|
|
8
|
-
"version": "0.0.
|
|
8
|
+
"version": "0.0.2",
|
|
9
9
|
"main": "./dist/index.js",
|
|
10
10
|
"exports": {
|
|
11
11
|
".": "./dist/index.js"
|
|
12
12
|
},
|
|
13
13
|
"types": "./dist/index.d.ts",
|
|
14
14
|
"peerDependencies": {
|
|
15
|
-
"react": "19"
|
|
16
|
-
"@types/react": "19"
|
|
15
|
+
"react": ">= 19"
|
|
17
16
|
},
|
|
18
17
|
"devDependencies": {
|
|
18
|
+
"@types/react": "^19",
|
|
19
19
|
"tsup": "^8.5.1"
|
|
20
20
|
},
|
|
21
21
|
"keywords": [
|
package/.turbo/turbo-build.log
DELETED
|
@@ -1,17 +0,0 @@
|
|
|
1
|
-
|
|
2
|
-
> @studiocubics/utils@0.0.1 build /home/runner/work/StudioCubics/StudioCubics/packages/utils
|
|
3
|
-
> tsup
|
|
4
|
-
|
|
5
|
-
[34mCLI[39m Building entry: src/index.ts
|
|
6
|
-
[34mCLI[39m Using tsconfig: tsconfig.json
|
|
7
|
-
[34mCLI[39m tsup v8.5.1
|
|
8
|
-
[34mCLI[39m Using tsup config: /home/runner/work/StudioCubics/StudioCubics/packages/utils/tsup.config.ts
|
|
9
|
-
[34mCLI[39m Target: es2022
|
|
10
|
-
[34mCLI[39m Cleaning output folder
|
|
11
|
-
[34mCJS[39m Build start
|
|
12
|
-
[32mCJS[39m [1mdist/index.js [22m[32m9.75 KB[39m
|
|
13
|
-
[32mCJS[39m [1mdist/index.js.map [22m[32m19.24 KB[39m
|
|
14
|
-
[32mCJS[39m ⚡️ Build success in 343ms
|
|
15
|
-
[34mDTS[39m Build start
|
|
16
|
-
[32mDTS[39m ⚡️ Build success in 2850ms
|
|
17
|
-
[32mDTS[39m [1mdist/index.d.ts [22m[32m5.81 KB[39m
|
package/CHANGELOG.md
DELETED
package/src/API/_index.ts
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export * from "./apiRes";
|
package/src/API/apiRes.ts
DELETED
|
@@ -1,74 +0,0 @@
|
|
|
1
|
-
export type ActionResponse<K extends string = string, E extends K[] = K[]> = {
|
|
2
|
-
success: boolean;
|
|
3
|
-
payload?: unknown;
|
|
4
|
-
error?: string;
|
|
5
|
-
fieldErrors?: Record<K, E>;
|
|
6
|
-
};
|
|
7
|
-
|
|
8
|
-
export const apiRes = {
|
|
9
|
-
actionFail: <K extends string = string>(
|
|
10
|
-
error: ActionResponse<K>["error"],
|
|
11
|
-
fieldErrors?: Record<string, string[]>
|
|
12
|
-
): ActionResponse<K> => ({
|
|
13
|
-
success: false,
|
|
14
|
-
error,
|
|
15
|
-
fieldErrors: fieldErrors as ActionResponse<K>["fieldErrors"],
|
|
16
|
-
}),
|
|
17
|
-
actionSuccess: <K extends string = string>(
|
|
18
|
-
payload?: unknown
|
|
19
|
-
): ActionResponse<K> => ({
|
|
20
|
-
success: true,
|
|
21
|
-
payload,
|
|
22
|
-
}),
|
|
23
|
-
/**
|
|
24
|
-
* "Failed to fetch data!"
|
|
25
|
-
*/
|
|
26
|
-
apiError: (message?: string) => (message ? message : "Failed to fetch data!"),
|
|
27
|
-
/**
|
|
28
|
-
* "You are not authenticated to access this resource."
|
|
29
|
-
*/
|
|
30
|
-
unauthorised: "You are not authenticated to access this resource.",
|
|
31
|
-
/**
|
|
32
|
-
* "You do not have the necessary permissions to access this resource."
|
|
33
|
-
*/
|
|
34
|
-
forbidden:
|
|
35
|
-
"You do not have the necessary permissions to access this resource.",
|
|
36
|
-
/**
|
|
37
|
-
* "${item} is of wrong type, expected type: ${expectedType}"
|
|
38
|
-
*/
|
|
39
|
-
wrongType: (item: string, expectedType?: string) =>
|
|
40
|
-
`${item} is of wrong type${expectedType ? `, expected type: ${expectedType}` : ""}.`,
|
|
41
|
-
/**
|
|
42
|
-
* "An error occured trying to read the formdata in these fields:
|
|
43
|
-
* ${fields}."
|
|
44
|
-
*/
|
|
45
|
-
formdataError: (fields: string | string[]) =>
|
|
46
|
-
`An error occured trying to read the formdata in these fields:\n${JSON.stringify(fields)}.`,
|
|
47
|
-
/**
|
|
48
|
-
* "Missing parameter: ${param}."
|
|
49
|
-
*/
|
|
50
|
-
missingParams: (param: string | string[]) =>
|
|
51
|
-
`Missing parameter: ${JSON.stringify(param)}.`,
|
|
52
|
-
/**
|
|
53
|
-
* "${item} not found!"
|
|
54
|
-
*/
|
|
55
|
-
notFound: (item: string) => `${item} not found!`,
|
|
56
|
-
/**
|
|
57
|
-
* "${item} already exists!"
|
|
58
|
-
*/
|
|
59
|
-
alreadyExists: (item: string) => `${item} already exists!`,
|
|
60
|
-
/**
|
|
61
|
-
* "${contentName} size exceeded the maximum of ${maxSize}"
|
|
62
|
-
*/
|
|
63
|
-
contentTooLarge: (contentName: string, maxSize?: string) =>
|
|
64
|
-
`${contentName} size exceeded${
|
|
65
|
-
maxSize ? ` the maximum of ${maxSize}` : ""
|
|
66
|
-
}.`,
|
|
67
|
-
/**
|
|
68
|
-
* An API Error has occurred:
|
|
69
|
-
* Check here => ${location}
|
|
70
|
-
* More Info: ${errorMessage}
|
|
71
|
-
*/
|
|
72
|
-
generalError: (location: string, errorMessage: string = "") =>
|
|
73
|
-
`An API Error has occurred:\nCheck here => ${location}\nMore Info: ${errorMessage}`,
|
|
74
|
-
};
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export * from "./calculateSafePosition";
|
|
@@ -1,137 +0,0 @@
|
|
|
1
|
-
type VerticalOrigin = "top" | "center" | "bottom";
|
|
2
|
-
type HorizontalOrigin = "left" | "center" | "right";
|
|
3
|
-
|
|
4
|
-
interface ParsedOrigin {
|
|
5
|
-
vertical: VerticalOrigin;
|
|
6
|
-
horizontal: HorizontalOrigin;
|
|
7
|
-
}
|
|
8
|
-
|
|
9
|
-
export interface SafePositionOptions {
|
|
10
|
-
anchorOrigin?: string;
|
|
11
|
-
transformOrigin?: string;
|
|
12
|
-
margin?: number;
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
export interface Position {
|
|
16
|
-
x: number;
|
|
17
|
-
y: number;
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
/**
|
|
21
|
-
* Calculates safe position of an element within viewport relative to an anchor element
|
|
22
|
-
* Works like Material UI Popover positioning:
|
|
23
|
-
* - anchorOrigin: point on the anchor element (e.g., "top center" = top-center of anchor)
|
|
24
|
-
* - transformOrigin: point on the positioned element that attaches to anchor (e.g., "bottom center" = bottom-center of popup attaches to anchor point)
|
|
25
|
-
*
|
|
26
|
-
* Example: anchorOrigin "top center" + transformOrigin "bottom center" positions popup above and centered
|
|
27
|
-
*
|
|
28
|
-
* @param element - The element to position
|
|
29
|
-
* @param anchorElement - The anchor element to position relative to
|
|
30
|
-
* @param options - Configuration options
|
|
31
|
-
* @returns Safe x, y coordinates
|
|
32
|
-
*/
|
|
33
|
-
export function calculateSafePosition(
|
|
34
|
-
element: HTMLElement | null,
|
|
35
|
-
anchorElement: HTMLElement | null,
|
|
36
|
-
options: SafePositionOptions = {}
|
|
37
|
-
): Position {
|
|
38
|
-
const {
|
|
39
|
-
anchorOrigin = "top left",
|
|
40
|
-
transformOrigin = "top left",
|
|
41
|
-
margin = 0,
|
|
42
|
-
} = options;
|
|
43
|
-
|
|
44
|
-
if (!element || !anchorElement) return { x: 0, y: 0 };
|
|
45
|
-
|
|
46
|
-
const parseOrigin = (origin: string): ParsedOrigin => {
|
|
47
|
-
const parts = origin.toLowerCase().trim().split(/\s+/);
|
|
48
|
-
let vertical: VerticalOrigin = "top";
|
|
49
|
-
let horizontal: HorizontalOrigin = "left";
|
|
50
|
-
|
|
51
|
-
for (const part of parts) {
|
|
52
|
-
if (["top", "bottom"].includes(part)) vertical = part as VerticalOrigin;
|
|
53
|
-
else if (["left", "right"].includes(part))
|
|
54
|
-
horizontal = part as HorizontalOrigin;
|
|
55
|
-
else if (part === "center") {
|
|
56
|
-
// Assign center depending on position in string
|
|
57
|
-
if (parts.length === 1) {
|
|
58
|
-
vertical = "center";
|
|
59
|
-
horizontal = "center";
|
|
60
|
-
} else if (["top", "bottom"].includes(parts[0])) horizontal = "center";
|
|
61
|
-
else vertical = "center";
|
|
62
|
-
}
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
return { vertical, horizontal };
|
|
66
|
-
};
|
|
67
|
-
|
|
68
|
-
const getAnchorOffset = (
|
|
69
|
-
size: number,
|
|
70
|
-
origin: VerticalOrigin | HorizontalOrigin
|
|
71
|
-
): number => {
|
|
72
|
-
if (origin === "center") return size / 2;
|
|
73
|
-
if (origin === "bottom" || origin === "right") return size;
|
|
74
|
-
return 0; // top or left
|
|
75
|
-
};
|
|
76
|
-
|
|
77
|
-
const getTransformOffset = (
|
|
78
|
-
size: number,
|
|
79
|
-
origin: VerticalOrigin | HorizontalOrigin
|
|
80
|
-
): number => {
|
|
81
|
-
if (origin === "center") return size / 2;
|
|
82
|
-
if (origin === "bottom" || origin === "right") return size;
|
|
83
|
-
return 0; // top or left
|
|
84
|
-
};
|
|
85
|
-
|
|
86
|
-
const elementRect = element.getBoundingClientRect();
|
|
87
|
-
const anchorRect = anchorElement.getBoundingClientRect();
|
|
88
|
-
const viewportWidth = window.innerWidth;
|
|
89
|
-
const viewportHeight = window.innerHeight;
|
|
90
|
-
|
|
91
|
-
const anchor = parseOrigin(anchorOrigin);
|
|
92
|
-
const transform = parseOrigin(transformOrigin);
|
|
93
|
-
|
|
94
|
-
// Calculate anchor point on the anchor element
|
|
95
|
-
const anchorX =
|
|
96
|
-
anchorRect.left + getAnchorOffset(anchorRect.width, anchor.horizontal);
|
|
97
|
-
const anchorY =
|
|
98
|
-
anchorRect.top + getAnchorOffset(anchorRect.height, anchor.vertical);
|
|
99
|
-
|
|
100
|
-
// Calculate where on the element we're attaching (transform origin point)
|
|
101
|
-
const elementOriginX = getTransformOffset(
|
|
102
|
-
elementRect.width,
|
|
103
|
-
transform.horizontal
|
|
104
|
-
);
|
|
105
|
-
const elementOriginY = getTransformOffset(
|
|
106
|
-
elementRect.height,
|
|
107
|
-
transform.vertical
|
|
108
|
-
);
|
|
109
|
-
|
|
110
|
-
// Position the element so its transform origin aligns with the anchor point
|
|
111
|
-
let x = anchorX - elementOriginX;
|
|
112
|
-
let y = anchorY - elementOriginY;
|
|
113
|
-
|
|
114
|
-
// Check and adjust for viewport boundaries with margin
|
|
115
|
-
// Right boundary
|
|
116
|
-
if (x + elementRect.width > viewportWidth - margin) {
|
|
117
|
-
x = viewportWidth - elementRect.width - margin;
|
|
118
|
-
}
|
|
119
|
-
// Left boundary
|
|
120
|
-
if (x < margin) {
|
|
121
|
-
x = margin;
|
|
122
|
-
}
|
|
123
|
-
|
|
124
|
-
// Bottom boundary
|
|
125
|
-
if (y + elementRect.height > viewportHeight - margin) {
|
|
126
|
-
y = viewportHeight - elementRect.height - margin;
|
|
127
|
-
}
|
|
128
|
-
// Top boundary
|
|
129
|
-
if (y < margin) {
|
|
130
|
-
y = margin;
|
|
131
|
-
}
|
|
132
|
-
|
|
133
|
-
return {
|
|
134
|
-
x: Math.max(margin, x),
|
|
135
|
-
y: Math.max(margin, y),
|
|
136
|
-
};
|
|
137
|
-
}
|
package/src/Code/_index.ts
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export * from "./delay";
|
package/src/Code/delay.ts
DELETED
package/src/Dates/_index.ts
DELETED
package/src/Dates/formateDate.ts
DELETED
|
@@ -1,140 +0,0 @@
|
|
|
1
|
-
type DateToken =
|
|
2
|
-
| "Day"
|
|
3
|
-
| "day"
|
|
4
|
-
| "DD"
|
|
5
|
-
| "D"
|
|
6
|
-
| "MMMM"
|
|
7
|
-
| "MMM"
|
|
8
|
-
| "MM"
|
|
9
|
-
| "YYYY"
|
|
10
|
-
| "YY"
|
|
11
|
-
| "HH"
|
|
12
|
-
| "H"
|
|
13
|
-
| "hh"
|
|
14
|
-
| "h"
|
|
15
|
-
| "mm"
|
|
16
|
-
| "m"
|
|
17
|
-
| "ss"
|
|
18
|
-
| "s"
|
|
19
|
-
| "A"
|
|
20
|
-
| "a";
|
|
21
|
-
|
|
22
|
-
/**
|
|
23
|
-
* Formats a date according to the provided format string.
|
|
24
|
-
*
|
|
25
|
-
* @param dateInput - The date to format (as Date object, timestamp, or date string)
|
|
26
|
-
* @param format - The format string using tokens (default: "day D MMM, YYYY")
|
|
27
|
-
*
|
|
28
|
-
* @returns The formatted date string
|
|
29
|
-
*
|
|
30
|
-
* @example
|
|
31
|
-
* formatDate(new Date(), "Day, DD MMMM YYYY")
|
|
32
|
-
* // => "Thursday, 02 January 2026"
|
|
33
|
-
*
|
|
34
|
-
* @example
|
|
35
|
-
* formatDate(new Date(), "DD/MM/YYYY HH:mm:ss")
|
|
36
|
-
* // => "02/01/2026 14:30:45"
|
|
37
|
-
*
|
|
38
|
-
* @example
|
|
39
|
-
* formatDate(new Date(), "h:mm A")
|
|
40
|
-
* // => "2:30 PM"
|
|
41
|
-
*
|
|
42
|
-
* Available tokens:
|
|
43
|
-
* - Day: Full day name (e.g., "Monday")
|
|
44
|
-
* - day: Short day name (e.g., "Mon")
|
|
45
|
-
* - DD: Day of month, zero-padded (e.g., "05")
|
|
46
|
-
* - D: Day of month (e.g., "5")
|
|
47
|
-
* - MMMM: Full month name (e.g., "January")
|
|
48
|
-
* - MMM: Short month name (e.g., "Jan")
|
|
49
|
-
* - MM: Month number, zero-padded (e.g., "01")
|
|
50
|
-
* - YYYY: Full year (e.g., "2026")
|
|
51
|
-
* - YY: Two-digit year (e.g., "26")
|
|
52
|
-
* - HH: Hours (24-hour), zero-padded (e.g., "14")
|
|
53
|
-
* - H: Hours (24-hour) (e.g., "14")
|
|
54
|
-
* - hh: Hours (12-hour), zero-padded (e.g., "02")
|
|
55
|
-
* - h: Hours (12-hour) (e.g., "2")
|
|
56
|
-
* - mm: Minutes, zero-padded (e.g., "05")
|
|
57
|
-
* - m: Minutes (e.g., "5")
|
|
58
|
-
* - ss: Seconds, zero-padded (e.g., "09")
|
|
59
|
-
* - s: Seconds (e.g., "9")
|
|
60
|
-
* - A: AM/PM uppercase (e.g., "PM")
|
|
61
|
-
* - a: am/pm lowercase (e.g., "pm")
|
|
62
|
-
*/
|
|
63
|
-
export function formatDate(
|
|
64
|
-
dateInput: number | string | Date,
|
|
65
|
-
format: string = "HH:mm:ss day D MMM, YYYY"
|
|
66
|
-
) {
|
|
67
|
-
const date = new Date(dateInput);
|
|
68
|
-
|
|
69
|
-
const pad = (n: number) => String(n).padStart(2, "0");
|
|
70
|
-
|
|
71
|
-
const daysFull = [
|
|
72
|
-
"Sunday",
|
|
73
|
-
"Monday",
|
|
74
|
-
"Tuesday",
|
|
75
|
-
"Wednesday",
|
|
76
|
-
"Thursday",
|
|
77
|
-
"Friday",
|
|
78
|
-
"Saturday",
|
|
79
|
-
];
|
|
80
|
-
const daysShort = ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"];
|
|
81
|
-
const monthsFull = [
|
|
82
|
-
"January",
|
|
83
|
-
"February",
|
|
84
|
-
"March",
|
|
85
|
-
"April",
|
|
86
|
-
"May",
|
|
87
|
-
"June",
|
|
88
|
-
"July",
|
|
89
|
-
"August",
|
|
90
|
-
"September",
|
|
91
|
-
"October",
|
|
92
|
-
"November",
|
|
93
|
-
"December",
|
|
94
|
-
];
|
|
95
|
-
const monthsShort = monthsFull.map((m) => m.slice(0, 3));
|
|
96
|
-
|
|
97
|
-
// Extract all values once
|
|
98
|
-
const dayIndex = date.getDay();
|
|
99
|
-
const dateNum = date.getDate();
|
|
100
|
-
const monthIndex = date.getMonth();
|
|
101
|
-
const year = date.getFullYear();
|
|
102
|
-
const hours24 = date.getHours();
|
|
103
|
-
const minutes = date.getMinutes();
|
|
104
|
-
const seconds = date.getSeconds();
|
|
105
|
-
|
|
106
|
-
const hours12 = hours24 % 12 || 12;
|
|
107
|
-
const isPM = hours24 >= 12;
|
|
108
|
-
const yearStr = String(year);
|
|
109
|
-
|
|
110
|
-
const replacements: Record<DateToken, string> = {
|
|
111
|
-
Day: daysFull[dayIndex],
|
|
112
|
-
day: daysShort[dayIndex],
|
|
113
|
-
DD: pad(dateNum),
|
|
114
|
-
D: String(dateNum),
|
|
115
|
-
MMMM: monthsFull[monthIndex],
|
|
116
|
-
MMM: monthsShort[monthIndex],
|
|
117
|
-
MM: pad(monthIndex + 1),
|
|
118
|
-
YYYY: yearStr,
|
|
119
|
-
YY: yearStr.slice(-2),
|
|
120
|
-
HH: pad(hours24),
|
|
121
|
-
H: String(hours24),
|
|
122
|
-
hh: pad(hours12),
|
|
123
|
-
h: String(hours12),
|
|
124
|
-
mm: pad(minutes),
|
|
125
|
-
m: String(minutes),
|
|
126
|
-
ss: pad(seconds),
|
|
127
|
-
s: String(seconds),
|
|
128
|
-
A: isPM ? "PM" : "AM",
|
|
129
|
-
a: isPM ? "pm" : "am",
|
|
130
|
-
};
|
|
131
|
-
|
|
132
|
-
// Sort tokens by length (longest first) to avoid partial replacements
|
|
133
|
-
const tokens = (Object.keys(replacements) as DateToken[]).sort(
|
|
134
|
-
(a, b) => b.length - a.length
|
|
135
|
-
);
|
|
136
|
-
|
|
137
|
-
// Use a single regex replacement to avoid overlapping matches
|
|
138
|
-
const pattern = new RegExp(tokens.join("|"), "g");
|
|
139
|
-
return format.replace(pattern, (match) => replacements[match as DateToken]);
|
|
140
|
-
}
|
|
@@ -1,64 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Returns a human-readable relative time string vs Date.now().
|
|
3
|
-
* Accepts Date | number (ms) | date-string.
|
|
4
|
-
* Fast, allocation-light, no Intl.
|
|
5
|
-
*/
|
|
6
|
-
export function relativeTime(
|
|
7
|
-
input: Date | number | string
|
|
8
|
-
): string {
|
|
9
|
-
const t: number =
|
|
10
|
-
input instanceof Date
|
|
11
|
-
? input.getTime()
|
|
12
|
-
: typeof input === "number"
|
|
13
|
-
? input
|
|
14
|
-
: Date.parse(input);
|
|
15
|
-
|
|
16
|
-
if (!Number.isFinite(t)) return "invalid date";
|
|
17
|
-
|
|
18
|
-
const now = Date.now();
|
|
19
|
-
const diffMs = t - now;
|
|
20
|
-
const absMs = Math.abs(diffMs);
|
|
21
|
-
const isFuture = diffMs > 0;
|
|
22
|
-
|
|
23
|
-
// time constants (ms)
|
|
24
|
-
const MIN = 60_000;
|
|
25
|
-
const HOUR = 3_600_000;
|
|
26
|
-
const DAY = 86_400_000;
|
|
27
|
-
const WEEK = 604_800_000;
|
|
28
|
-
const YEAR = 31_536_000_000;
|
|
29
|
-
|
|
30
|
-
// very far away → coarse wording
|
|
31
|
-
if (absMs > 5 * YEAR) {
|
|
32
|
-
return isFuture ? "a long time from now" : "a long time ago";
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
if (absMs < MIN) {
|
|
36
|
-
return "just now";
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
let value: number;
|
|
40
|
-
let unit: "minute" | "hour" | "day" | "week" | "year";
|
|
41
|
-
|
|
42
|
-
if (absMs < HOUR) {
|
|
43
|
-
value = Math.round(absMs / MIN);
|
|
44
|
-
unit = "minute";
|
|
45
|
-
} else if (absMs < DAY) {
|
|
46
|
-
value = Math.round(absMs / HOUR);
|
|
47
|
-
unit = "hour";
|
|
48
|
-
} else if (absMs < WEEK) {
|
|
49
|
-
value = Math.round(absMs / DAY);
|
|
50
|
-
unit = "day";
|
|
51
|
-
} else if (absMs < YEAR) {
|
|
52
|
-
value = Math.round(absMs / WEEK);
|
|
53
|
-
unit = "week";
|
|
54
|
-
} else {
|
|
55
|
-
value = Math.round(absMs / YEAR);
|
|
56
|
-
unit = "year";
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
if (value !== 1) unit += "s";
|
|
60
|
-
|
|
61
|
-
return isFuture
|
|
62
|
-
? `in ${value} ${unit}`
|
|
63
|
-
: `${value} ${unit} ago`;
|
|
64
|
-
}
|
package/src/Forms/_index.ts
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export * from "./initialiseForm";
|
|
@@ -1,14 +0,0 @@
|
|
|
1
|
-
import type { ActionResponse } from "../API/apiRes";
|
|
2
|
-
|
|
3
|
-
export function initialiseForm<T extends string = string>(
|
|
4
|
-
...fieldNames: T[]
|
|
5
|
-
): ActionResponse<T, T[]> {
|
|
6
|
-
let fieldErrors = {} as Record<T, T[]>;
|
|
7
|
-
for (const fieldName of fieldNames) {
|
|
8
|
-
fieldErrors[fieldName] = [] as T[];
|
|
9
|
-
}
|
|
10
|
-
return {
|
|
11
|
-
success: false,
|
|
12
|
-
fieldErrors,
|
|
13
|
-
};
|
|
14
|
-
}
|
package/src/Numbers/_index.ts
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export * from "./remap";
|
package/src/Numbers/remap.ts
DELETED
|
@@ -1,28 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* This function returns the rounded corresponding value in the target range using linear interpolation.
|
|
3
|
-
*
|
|
4
|
-
* @param value - The input value to remap.
|
|
5
|
-
* @param from - The source range as a tuple [min, max].
|
|
6
|
-
* @param to - The target range as a tuple [min, max].
|
|
7
|
-
* @returns The remapped value in the target range.
|
|
8
|
-
*
|
|
9
|
-
* @example
|
|
10
|
-
* remap(3, [1, 10], [1, 5]); // 2
|
|
11
|
-
*/
|
|
12
|
-
export function remap(
|
|
13
|
-
value: number,
|
|
14
|
-
from: [number, number],
|
|
15
|
-
to: [number, number],
|
|
16
|
-
roundTo: "floor" | "ceil" = "floor",
|
|
17
|
-
): number {
|
|
18
|
-
const [fromMin, fromMax] = from;
|
|
19
|
-
const [toMin, toMax] = to;
|
|
20
|
-
|
|
21
|
-
if (roundTo == "ceil")
|
|
22
|
-
return Math.ceil(
|
|
23
|
-
toMin + ((value - fromMin) * (toMax - toMin)) / (fromMax - fromMin),
|
|
24
|
-
);
|
|
25
|
-
return Math.floor(
|
|
26
|
-
toMin + ((value - fromMin) * (toMax - toMin)) / (fromMax - fromMin),
|
|
27
|
-
);
|
|
28
|
-
}
|
package/src/React/_index.ts
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export * from "./mergeRefs";
|
package/src/React/mergeRefs.ts
DELETED
|
@@ -1,16 +0,0 @@
|
|
|
1
|
-
import type { RefObject, Ref } from "react";
|
|
2
|
-
|
|
3
|
-
export function mergeRefs<T>(
|
|
4
|
-
...refs: (Ref<T> | undefined)[]
|
|
5
|
-
): (instance: T | null) => void {
|
|
6
|
-
return (instance) => {
|
|
7
|
-
for (const ref of refs) {
|
|
8
|
-
if (!ref) continue;
|
|
9
|
-
if (typeof ref === "function") {
|
|
10
|
-
ref(instance);
|
|
11
|
-
} else {
|
|
12
|
-
(ref as RefObject<T | null>).current = instance;
|
|
13
|
-
}
|
|
14
|
-
}
|
|
15
|
-
};
|
|
16
|
-
}
|
package/src/Strings/_index.ts
DELETED
package/src/Strings/cn.ts
DELETED
|
@@ -1,13 +0,0 @@
|
|
|
1
|
-
export function cssSafeString(str: string) {
|
|
2
|
-
const encoded = encodeURIComponent(str)
|
|
3
|
-
.toLowerCase()
|
|
4
|
-
.replace(/\.|%[0-9a-z]{2}/gi, "");
|
|
5
|
-
|
|
6
|
-
const firstChar = encoded.charAt(0);
|
|
7
|
-
if (firstChar.match(/^[0-9-]/)) {
|
|
8
|
-
// Check if the first character is a number or hyphen
|
|
9
|
-
return "_" + encoded;
|
|
10
|
-
}
|
|
11
|
-
|
|
12
|
-
return encoded;
|
|
13
|
-
}
|
|
@@ -1,50 +0,0 @@
|
|
|
1
|
-
function toWords(str: string): string[] {
|
|
2
|
-
return str
|
|
3
|
-
.replace(/([a-z])([A-Z])/g, "$1 $2") // camelCase → camel Case
|
|
4
|
-
.replace(/[_\-]+/g, " ") // snake_case, kebab-case → spaces
|
|
5
|
-
.trim()
|
|
6
|
-
.split(/\s+/)
|
|
7
|
-
.filter(Boolean)
|
|
8
|
-
.map((w) => w.toLowerCase());
|
|
9
|
-
}
|
|
10
|
-
|
|
11
|
-
export function toCapitalCase(str: string | undefined): string {
|
|
12
|
-
if (!str) return "";
|
|
13
|
-
const words = toWords(str);
|
|
14
|
-
return words.map((w) => w[0].toUpperCase() + w.slice(1)).join("");
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
export function toCamelCase(str: string | undefined): string {
|
|
18
|
-
if (!str) return "";
|
|
19
|
-
|
|
20
|
-
const words = toWords(str);
|
|
21
|
-
return words
|
|
22
|
-
.map((w, i) => (i === 0 ? w : w[0].toUpperCase() + w.slice(1)))
|
|
23
|
-
.join("");
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
export function toKebabCase(str: string | undefined): string {
|
|
27
|
-
if (!str) return "";
|
|
28
|
-
|
|
29
|
-
return toWords(str).join("-");
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
export function toSnakeCase(str: string | undefined): string {
|
|
33
|
-
if (!str) return "";
|
|
34
|
-
|
|
35
|
-
return toWords(str).join("_");
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
export function toConstantCase(str: string | undefined): string {
|
|
39
|
-
if (!str) return "";
|
|
40
|
-
|
|
41
|
-
return toWords(str).join("_").toUpperCase();
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
export function toCapitalised(str: string | undefined): string {
|
|
45
|
-
if (!str) return "";
|
|
46
|
-
|
|
47
|
-
const words = toWords(str);
|
|
48
|
-
if (words.length === 0) return "";
|
|
49
|
-
return words.map((w) => w[0].toUpperCase() + w.slice(1)).join(" ");
|
|
50
|
-
}
|
package/src/index.ts
DELETED
|
@@ -1,8 +0,0 @@
|
|
|
1
|
-
export * from "./API/_index";
|
|
2
|
-
export * from "./Calculations/_index";
|
|
3
|
-
export * from "./Code/_index";
|
|
4
|
-
export * from "./Dates/_index";
|
|
5
|
-
export * from "./Forms/_index";
|
|
6
|
-
export * from "./Numbers/_index";
|
|
7
|
-
export * from "./React/_index";
|
|
8
|
-
export * from "./Strings/_index";
|
package/tsconfig.json
DELETED
|
@@ -1,32 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"compilerOptions": {
|
|
3
|
-
"tsBuildInfoFile": "./node_modules/.tmp/tsconfig.app.tsbuildinfo",
|
|
4
|
-
"target": "ES2022",
|
|
5
|
-
"useDefineForClassFields": true,
|
|
6
|
-
"lib": ["ES2022", "DOM", "DOM.Iterable"],
|
|
7
|
-
"module": "ESNext",
|
|
8
|
-
"resolveJsonModule": true,
|
|
9
|
-
"allowJs": true,
|
|
10
|
-
"skipLibCheck": true,
|
|
11
|
-
|
|
12
|
-
/* Bundler mode */
|
|
13
|
-
"moduleResolution": "bundler",
|
|
14
|
-
"allowImportingTsExtensions": true,
|
|
15
|
-
"verbatimModuleSyntax": true,
|
|
16
|
-
"moduleDetection": "force",
|
|
17
|
-
"noEmit": true,
|
|
18
|
-
"jsx": "react-jsx",
|
|
19
|
-
// Output
|
|
20
|
-
"declaration": true,
|
|
21
|
-
"outDir": "./dist",
|
|
22
|
-
|
|
23
|
-
/* Linting */
|
|
24
|
-
"strict": true,
|
|
25
|
-
"noUnusedLocals": true,
|
|
26
|
-
"noUnusedParameters": true,
|
|
27
|
-
"erasableSyntaxOnly": true,
|
|
28
|
-
"noFallthroughCasesInSwitch": true,
|
|
29
|
-
"noUncheckedSideEffectImports": true
|
|
30
|
-
},
|
|
31
|
-
"include": ["src"]
|
|
32
|
-
}
|