Repository navigation
Expand file tree
/
Copy pathstring.ts
More file actions
314 lines (274 loc) · 10.2 KB
/
Copy pathstring.ts
File metadata and controls
314 lines (274 loc) · 10.2 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
import DOMPurify from "isomorphic-dompurify";
/**
* @description Adds space between camelCase words
* @param {string} str - String to add spaces to
* @returns {string} String with spaces between camelCase words
* @example
* addSpaceIfCamelCase("camelCase") // returns "camel Case"
* addSpaceIfCamelCase("thisIsATest") // returns "this Is A Test"
*/
export const addSpaceIfCamelCase = (str: string) => {
if (str === undefined || str === null) return "";
if (typeof str !== "string") str = `${str}`;
return str.replace(/([a-z])([A-Z])/g, "$1 $2");
};
/**
* @description Replaces underscores with spaces in snake_case strings
* @param {string} str - String to replace underscores in
* @returns {string} String with underscores replaced by spaces
* @example
* replaceUnderscoreIfSnakeCase("snake_case") // returns "snake case"
*/
export const replaceUnderscoreIfSnakeCase = (str: string) => str.replace(/_/g, " ");
/**
* @description Truncates text to specified length and adds ellipsis
* @param {string} str - String to truncate
* @param {number} length - Maximum length before truncation
* @returns {string} Truncated string with ellipsis if needed
* @example
* truncateText("This is a long text", 7) // returns "This is..."
*/
export const truncateText = (str: string, length: number) => {
if (!str || str === "") return "";
return str.length > length ? `${str.substring(0, length)}...` : str;
};
/**
* @description Creates a similar string by randomly shuffling characters
* @param {string} str - String to shuffle
* @returns {string} Shuffled string with same characters
* @example
* createSimilarString("hello") // might return "olleh" or "lehol"
*/
export const createSimilarString = (str: string) => {
const shuffled = str
.split("")
.sort(() => Math.random() - 0.5)
.join("");
return shuffled;
};
/**
* @description Copies text to clipboard
* @param {string} text - Text to copy
* @returns {Promise<void>} Promise that resolves when copying is complete
* @example
* await copyTextToClipboard("Hello, World!") // copies "Hello, World!" to clipboard
*/
export const copyTextToClipboard = async (text: string): Promise<void> => {
if (typeof navigator === "undefined") return;
try {
await navigator.clipboard.writeText(text);
} catch (err) {
console.error("Failed to copy text: ", err);
}
};
/**
* @description Copies full URL (origin + path) to clipboard
* @param {string} path - URL path to copy
* @returns {Promise<void>} Promise that resolves when copying is complete
* @example
* await copyUrlToClipboard("issues/123") // copies "https://example.com/issues/123"
*/
export const copyUrlToClipboard = async (path: string) => {
// get origin or default to empty string if not in browser
const originUrl = typeof window !== "undefined" ? window.location.origin : "";
// create URL object and ensure proper path formatting
const url = new URL(path, originUrl);
await copyTextToClipboard(url.toString());
};
/**
* @description Gets first character of first word or first characters of first two words
* @param {string} str - Input string
* @returns {string} First character(s)
* @example
* getFirstCharacters("John") // returns "J"
* getFirstCharacters("John Doe") // returns "JD"
*/
export const getFirstCharacters = (str: string) => {
const words = str.trim().split(" ");
if (words.length === 1) {
return words[0].charAt(0);
} else {
return words[0].charAt(0) + words[1].charAt(0);
}
};
/**
* @description Formats number count, showing "99+" for numbers over 99
* @param {number} number - Number to format
* @returns {string} Formatted number string
* @example
* getNumberCount(50) // returns "50"
* getNumberCount(100) // returns "99+"
*/
export const getNumberCount = (number: number): string => {
if (number > 99) {
return "99+";
}
return number.toString();
};
/**
* @description Converts object to URL query parameters string
* @param {Object} obj - Object to convert
* @returns {string} URL query parameters string
* @example
* objToQueryParams({ page: 1, search: "test" }) // returns "page=1&search=test"
* objToQueryParams({ a: null, b: "test" }) // returns "b=test"
*/
export const objToQueryParams = (obj: any) => {
const params = new URLSearchParams();
if (!obj) return params.toString();
for (const [key, value] of Object.entries(obj)) {
if (value !== undefined && value !== null) params.append(key, value as string);
}
return params.toString();
};
/**
* @description: This function will capitalize the first letter of a string
* @param str String
* @returns String
*/
export const capitalizeFirstLetter = (str: string) => str.charAt(0).toUpperCase() + str.slice(1);
/**
* @description: This function will remove all the HTML tags from the string
* @param {string} html
* @return {string}
* @example:
* const html = "<p>Some text</p>";
* const text = stripHTML(html);
* console.log(text); // Some text
*/
/**
* @description Sanitizes HTML string by removing tags and properly escaping entities
* @param {string} htmlString - HTML string to sanitize
* @returns {string} Sanitized string with escaped HTML entities
* @example
* sanitizeHTML("<p>Hello & 'world'</p>") // returns "Hello & 'world'"
*/
export const sanitizeHTML = (htmlString: string) => {
if (!htmlString) return "";
// First use DOMPurify to remove all HTML tags while preserving text content
const sanitizedText = DOMPurify.sanitize(htmlString, {
ALLOWED_TAGS: [],
ALLOWED_ATTR: [],
USE_PROFILES: {
html: false,
svg: false,
svgFilters: false,
mathMl: false,
},
});
// Additional escaping for quotes and apostrophes
return sanitizedText.trim().replace(/'/g, "'").replace(/"/g, """);
};
/**
* @returns {boolean} true if email is valid, false otherwise
* @description Returns true if email is valid, false otherwise
* @param {string} email string to check if it is a valid email
* @example checkEmailValidity("hello world") => false
* @example checkEmailValidity("example@plane.so") => true
*/
export const checkEmailValidity = (email: string): boolean => {
if (!email) return false;
const isEmailValid =
/^(([^<>()[\]\\.,;:\s@\"]+(\.[^<>()[\]\\.,;:\s@\"]+)*)|(\".+\"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/.test(
email
);
return isEmailValid;
};
export const isEmptyHtmlString = (htmlString: string, allowedHTMLTags: string[] = []) => {
// Remove HTML tags using DOMPurify
const cleanText = DOMPurify.sanitize(htmlString, { ALLOWED_TAGS: allowedHTMLTags });
// Trim the string and check if it's empty
return cleanText.trim() === "";
};
/**
* @description this function returns whether a comment is empty or not by checking for the following conditions-
* 1. If comment is undefined
* 2. If comment is an empty string
* 3. If comment is "<p></p>"
* @param {string | undefined} comment
* @returns {boolean}
*/
export const isCommentEmpty = (comment: string | undefined): boolean => {
// return true if comment is undefined
if (!comment) return true;
return (
comment?.trim() === "" ||
comment === "<p></p>" ||
isEmptyHtmlString(comment ?? "", ["img", "mention-component", "image-component"])
);
};
/**
* @description
* This function test whether a URL is valid or not.
*
* It accepts URLs with or without the protocol.
* @param {string} url
* @returns {boolean}
* @example
* checkURLValidity("https://example.com") => true
* checkURLValidity("example.com") => true
* checkURLValidity("example") => false
*/
export const checkURLValidity = (url: string): boolean => {
if (!url) return false;
// regex to support complex query parameters and fragments
const urlPattern =
/^(https?:\/\/)?((([a-z\d-]+\.)*[a-z\d-]+\.[a-z]{2,6})|(\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}))(:\d+)?(\/[\w.-]*)*(\?[^#\s]*)?(#[\w-]*)?$/i;
return urlPattern.test(url);
};
/**
* Combines array elements with a separator and adds a conjunction before the last element
* @param array Array of strings to combine
* @param separator Separator to use between elements (default: ", ")
* @param conjunction Conjunction to use before last element (default: "and")
* @returns Combined string with conjunction before the last element
*/
export const joinWithConjunction = (array: string[], separator: string = ", ", conjunction: string = "and"): string => {
if (!array || array.length === 0) return "";
if (array.length === 1) return array[0];
if (array.length === 2) return `${array[0]} ${conjunction} ${array[1]}`;
const lastElement = array[array.length - 1];
const elementsExceptLast = array.slice(0, -1);
return `${elementsExceptLast.join(separator)}${separator}${conjunction} ${lastElement}`;
};
/**
* @description Ensures a URL has a protocol
* @param {string} url
* @returns {string}
* @example
* ensureUrlHasProtocol("example.com") => "http://example.com"
*/
export const ensureUrlHasProtocol = (url: string): string => (url.startsWith("http") ? url : `http://${url}`);
// Browser-only clipboard functions
// let copyTextToClipboard: (text: string) => Promise<void>;
// if (typeof window !== "undefined") {
// const fallbackCopyTextToClipboard = (text: string) => {
// const textArea = document.createElement("textarea");
// textArea.value = text;
// // Avoid scrolling to bottom
// textArea.style.top = "0";
// textArea.style.left = "0";
// textArea.style.position = "fixed";
// document.body.appendChild(textArea);
// textArea.focus();
// textArea.select();
// try {
// // FIXME: Even though we are using this as a fallback, execCommand is deprecated 👎. We should find a better way to do this.
// // https://developer.mozilla.org/en-US/docs/Web/API/Document/execCommand
// document.execCommand("copy");
// } catch (err) {}
// document.body.removeChild(textArea);
// };
// copyTextToClipboard = async (text: string) => {
// if (!navigator.clipboard) {
// fallbackCopyTextToClipboard(text);
// return;
// }
// await navigator.clipboard.writeText(text);
// };
// } else {
// copyTextToClipboard = async () => {
// throw new Error("copyTextToClipboard is only available in browser environments");
// };
// }
// export { copyTextToClipboard };