@mhmdhammoud/meritt-utils 1.0.6 → 1.2.0
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/dist/__tests__/colorful.test.d.ts +1 -0
- package/dist/__tests__/colorful.test.js +64 -0
- package/dist/__tests__/formatter.test.d.ts +1 -0
- package/dist/__tests__/formatter.test.js +69 -0
- package/dist/index.d.ts +1 -2
- package/dist/index.js +3 -3
- package/dist/lib/colorful.d.ts +46 -0
- package/dist/lib/colorful.js +139 -0
- package/dist/lib/formatter.d.ts +4 -4
- package/dist/lib/formatter.js +18 -49
- package/dist/lib/imagefull.d.ts +23 -0
- package/dist/lib/imagefull.js +43 -0
- package/dist/lib/index.d.ts +2 -0
- package/dist/lib/index.js +5 -1
- package/imagesloaded.d.ts +66 -0
- package/jest.config.js +13 -0
- package/package.json +10 -4
- package/src/__tests__/colorful.test.ts +77 -0
- package/src/__tests__/formatter.test.ts +96 -0
- package/src/index.ts +1 -2
- package/src/lib/colorful.ts +167 -0
- package/src/lib/formatter.ts +26 -52
- package/src/lib/imagefull.ts +41 -0
- package/src/lib/index.ts +2 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
const lib_1 = require("../lib");
|
|
4
|
+
describe('Colorful Class', () => {
|
|
5
|
+
describe('rgbToHex method', () => {
|
|
6
|
+
it('should convert RGB to HEX', () => {
|
|
7
|
+
const rgbColor = 'rgb(255, 255, 255)';
|
|
8
|
+
const hexColor = lib_1.Colorful.rgbToHex(rgbColor);
|
|
9
|
+
expect(hexColor).toBe('#ffffff');
|
|
10
|
+
});
|
|
11
|
+
it('should convert RGBA to HEX', () => {
|
|
12
|
+
const rgbaColor = 'rgba(255, 0, 0, 0.5)';
|
|
13
|
+
const hexColor = lib_1.Colorful.rgbToHex(rgbaColor);
|
|
14
|
+
expect(hexColor).toBe('#ff0000');
|
|
15
|
+
});
|
|
16
|
+
it('should throw an error for invalid color format', () => {
|
|
17
|
+
const invalidColor = 'invalidColor';
|
|
18
|
+
expect(() => lib_1.Colorful.rgbToHex(invalidColor)).toThrowError(`Invalid color format '${invalidColor}'. Please provide a valid RGB or RGBA color.`);
|
|
19
|
+
});
|
|
20
|
+
});
|
|
21
|
+
describe('hexToRgb method', () => {
|
|
22
|
+
it('should convert HEX to RGB', () => {
|
|
23
|
+
const hexColor = '#ffffff';
|
|
24
|
+
const rgbColor = lib_1.Colorful.hexToRgb(hexColor);
|
|
25
|
+
expect(rgbColor).toBe('rgb(255, 255, 255)');
|
|
26
|
+
});
|
|
27
|
+
});
|
|
28
|
+
describe('isLightColor method', () => {
|
|
29
|
+
it('should return true for a light color (HEX format)', () => {
|
|
30
|
+
const lightHexColor = '#f0f0f0';
|
|
31
|
+
const isLight = lib_1.Colorful.isLightColor(lightHexColor);
|
|
32
|
+
expect(isLight).toBe(true);
|
|
33
|
+
});
|
|
34
|
+
it('should return false for a dark color (HEX format)', () => {
|
|
35
|
+
const darkHexColor = '#333333';
|
|
36
|
+
const isLight = lib_1.Colorful.isLightColor(darkHexColor);
|
|
37
|
+
expect(isLight).toBe(false);
|
|
38
|
+
});
|
|
39
|
+
it('should return true for a light color (RGBA format)', () => {
|
|
40
|
+
const lightRgbaColor = 'rgba(255, 255, 255, 0.5)';
|
|
41
|
+
const isLight = lib_1.Colorful.isLightColor(lightRgbaColor);
|
|
42
|
+
expect(isLight).toBe(true);
|
|
43
|
+
});
|
|
44
|
+
it('should return false for a dark color (RGBA format)', () => {
|
|
45
|
+
const darkRgbaColor = 'rgba(0, 0, 0, 0.8)';
|
|
46
|
+
const isLight = lib_1.Colorful.isLightColor(darkRgbaColor);
|
|
47
|
+
expect(isLight).toBe(false);
|
|
48
|
+
});
|
|
49
|
+
it('should return true for a light color (RGB format)', () => {
|
|
50
|
+
const lightRgbColor = 'rgb(200, 220, 255)';
|
|
51
|
+
const isLight = lib_1.Colorful.isLightColor(lightRgbColor);
|
|
52
|
+
expect(isLight).toBe(true);
|
|
53
|
+
});
|
|
54
|
+
it('should return false for a dark color (RGB format)', () => {
|
|
55
|
+
const darkRgbColor = 'rgb(10, 20, 30)';
|
|
56
|
+
const isLight = lib_1.Colorful.isLightColor(darkRgbColor);
|
|
57
|
+
expect(isLight).toBe(false);
|
|
58
|
+
});
|
|
59
|
+
it('should throw an error for an invalid color format', () => {
|
|
60
|
+
const invalidColor = 'invalid-color';
|
|
61
|
+
expect(() => lib_1.Colorful.isLightColor(invalidColor)).toThrowError("Invalid color format 'invalid-color'. Please provide a valid HEX, RGB, or RGBA color.");
|
|
62
|
+
});
|
|
63
|
+
});
|
|
64
|
+
});
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
const lib_1 = require("../lib");
|
|
4
|
+
describe('Formatter', () => {
|
|
5
|
+
describe('toUpperFirst method', () => {
|
|
6
|
+
it('should format string with - and upper first letter', () => {
|
|
7
|
+
const formattedString = lib_1.Formatter.toUpperFirst('hello world');
|
|
8
|
+
expect(formattedString).toBe('Hello-world');
|
|
9
|
+
});
|
|
10
|
+
it('should format string with spaces and upper first letter of every word', () => {
|
|
11
|
+
const formattedString = lib_1.Formatter.toUpperFirst('hello_world+test');
|
|
12
|
+
expect(formattedString).toBe('Hello-world-test');
|
|
13
|
+
});
|
|
14
|
+
it('should throw an error for non-string input', () => {
|
|
15
|
+
expect(() => lib_1.Formatter.toUpperFirst(123)).toThrowError('Provide a valid string');
|
|
16
|
+
});
|
|
17
|
+
});
|
|
18
|
+
describe('camelToKebab method', () => {
|
|
19
|
+
it('should convert camelCase to kebab-case', () => {
|
|
20
|
+
const kebabString = lib_1.Formatter.camelToKebab('camelCaseString');
|
|
21
|
+
expect(kebabString).toBe('camel-case-string');
|
|
22
|
+
});
|
|
23
|
+
it('should handle spaces and underscores', () => {
|
|
24
|
+
const kebabString = lib_1.Formatter.camelToKebab('hello World_with Spaces');
|
|
25
|
+
expect(kebabString).toBe('hello-world_with-spaces');
|
|
26
|
+
});
|
|
27
|
+
it('should throw an error for non-string input', () => {
|
|
28
|
+
expect(() => lib_1.Formatter.camelToKebab(123)).toThrowError('Invalid input. Please provide a valid string.');
|
|
29
|
+
});
|
|
30
|
+
});
|
|
31
|
+
describe('obfuscate method', () => {
|
|
32
|
+
it('should obfuscate email address', () => {
|
|
33
|
+
const obfuscatedEmail = lib_1.Formatter.obfuscate('johndoe@email.com');
|
|
34
|
+
expect(obfuscatedEmail).toMatch(/^jo.*@.*$/);
|
|
35
|
+
});
|
|
36
|
+
it('should throw an error for non-string input', () => {
|
|
37
|
+
//@ts-ignore
|
|
38
|
+
expect(() => lib_1.Formatter.obfuscate(123)).toThrowError('Provide a valid string');
|
|
39
|
+
});
|
|
40
|
+
it('should throw an error for an invalid email address', () => {
|
|
41
|
+
expect(() => lib_1.Formatter.obfuscate('invalid-email')).toThrowError('Provide a valid email address');
|
|
42
|
+
});
|
|
43
|
+
});
|
|
44
|
+
describe('toUpperTitle method', () => {
|
|
45
|
+
it('should capitalize first letter of each word and remove non-alphanumeric characters', () => {
|
|
46
|
+
const upperTitle = lib_1.Formatter.toUpperTitle('this is34354345a---sentence');
|
|
47
|
+
expect(upperTitle).toBe('This Is A Sentence');
|
|
48
|
+
});
|
|
49
|
+
it('should throw an error for non-string input', () => {
|
|
50
|
+
expect(() => lib_1.Formatter.toUpperTitle(123)).toThrowError('Provide a valid string');
|
|
51
|
+
});
|
|
52
|
+
it('should throw an error for empty string', () => {
|
|
53
|
+
expect(() => lib_1.Formatter.toUpperTitle('')).toThrowError('Provide a valid string');
|
|
54
|
+
});
|
|
55
|
+
});
|
|
56
|
+
describe('slugify method', () => {
|
|
57
|
+
it('should generate a slug from the given string', () => {
|
|
58
|
+
const slug = lib_1.Formatter.slugify('Hello World');
|
|
59
|
+
expect(slug).toBe('hello-world');
|
|
60
|
+
});
|
|
61
|
+
it('should handle special characters and spaces', () => {
|
|
62
|
+
const slug = lib_1.Formatter.slugify('Special Characters % #$ & Space');
|
|
63
|
+
expect(slug).toBe('special-characters-space');
|
|
64
|
+
});
|
|
65
|
+
it('should throw an error for non-string input', () => {
|
|
66
|
+
expect(() => lib_1.Formatter.slugify(123)).toThrowError('Provide a valid string');
|
|
67
|
+
});
|
|
68
|
+
});
|
|
69
|
+
});
|
package/dist/index.d.ts
CHANGED
|
@@ -1,2 +1 @@
|
|
|
1
|
-
export { Crypto } from './lib';
|
|
2
|
-
export { Formatter } from './lib';
|
|
1
|
+
export { Crypto, Formatter, Colorful } from './lib';
|
package/dist/index.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.Formatter = exports.Crypto = void 0;
|
|
3
|
+
exports.Colorful = exports.Formatter = exports.Crypto = void 0;
|
|
4
4
|
var lib_1 = require("./lib");
|
|
5
5
|
Object.defineProperty(exports, "Crypto", { enumerable: true, get: function () { return lib_1.Crypto; } });
|
|
6
|
-
|
|
7
|
-
Object.defineProperty(exports, "
|
|
6
|
+
Object.defineProperty(exports, "Formatter", { enumerable: true, get: function () { return lib_1.Formatter; } });
|
|
7
|
+
Object.defineProperty(exports, "Colorful", { enumerable: true, get: function () { return lib_1.Colorful; } });
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
declare class Colorful {
|
|
2
|
+
/**
|
|
3
|
+
* @remarks
|
|
4
|
+
* Receives a color in RGB format and returns it in HEX format
|
|
5
|
+
* @param color - Color in RGB format
|
|
6
|
+
* @example
|
|
7
|
+
* ```ts
|
|
8
|
+
* const color = 'rgb(255, 255, 255)';
|
|
9
|
+
* const hexColor = rgbToHex(color);
|
|
10
|
+
* console.log(hexColor) // #ffffff
|
|
11
|
+
* ```
|
|
12
|
+
*
|
|
13
|
+
* @returns Color in HEX format string
|
|
14
|
+
*/
|
|
15
|
+
rgbToHex: (color: string) => string;
|
|
16
|
+
/**
|
|
17
|
+
* @remarks
|
|
18
|
+
* Receives a color in HEX format and returns it in RGB format
|
|
19
|
+
* @param hexColor - Color in HEX format
|
|
20
|
+
* @example
|
|
21
|
+
* ```ts
|
|
22
|
+
* const hexColor = '#ffffff';
|
|
23
|
+
* const rgbColor = hexToRgb(hexColor);
|
|
24
|
+
* console.log(rgbColor) // 'rgb(255, 255, 255)'
|
|
25
|
+
* ```
|
|
26
|
+
*
|
|
27
|
+
* @returns Color in RGB format string
|
|
28
|
+
*/
|
|
29
|
+
hexToRgb: (hexColor: string) => string;
|
|
30
|
+
/**
|
|
31
|
+
* @remarks
|
|
32
|
+
* Checks whether a given HEX color is light or dark
|
|
33
|
+
* @param hexColor - Color in HEX format
|
|
34
|
+
* @example
|
|
35
|
+
* ```ts
|
|
36
|
+
* const hexColor = '#ffffff';
|
|
37
|
+
* const isLight = isLightColor(hexColor);
|
|
38
|
+
* console.log(isLight); // true or false
|
|
39
|
+
* ```
|
|
40
|
+
*
|
|
41
|
+
* @returns True if the color is light, false if it is dark
|
|
42
|
+
*/
|
|
43
|
+
isLightColor: (color: string) => boolean;
|
|
44
|
+
}
|
|
45
|
+
declare const colorful: Colorful;
|
|
46
|
+
export default colorful;
|
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
/*
|
|
4
|
+
Author : Mhmd Hammoud https://github.com/mhmdhammoud
|
|
5
|
+
Date : 2024-01-20
|
|
6
|
+
Description : Color adapter
|
|
7
|
+
Version : 1.0
|
|
8
|
+
*/
|
|
9
|
+
class Colorful {
|
|
10
|
+
constructor() {
|
|
11
|
+
/**
|
|
12
|
+
* @remarks
|
|
13
|
+
* Receives a color in RGB format and returns it in HEX format
|
|
14
|
+
* @param color - Color in RGB format
|
|
15
|
+
* @example
|
|
16
|
+
* ```ts
|
|
17
|
+
* const color = 'rgb(255, 255, 255)';
|
|
18
|
+
* const hexColor = rgbToHex(color);
|
|
19
|
+
* console.log(hexColor) // #ffffff
|
|
20
|
+
* ```
|
|
21
|
+
*
|
|
22
|
+
* @returns Color in HEX format string
|
|
23
|
+
*/
|
|
24
|
+
this.rgbToHex = (color) => {
|
|
25
|
+
const componentToHex = (c) => {
|
|
26
|
+
const hex = c.toString(16);
|
|
27
|
+
return hex.length == 1 ? '0' + hex : hex;
|
|
28
|
+
};
|
|
29
|
+
const rgbaMatch = color.match(/rgba\((\d{1,3}),\s*(\d{1,3}),\s*(\d{1,3}),\s*((\d+\.)?\d+)\)/);
|
|
30
|
+
const rgbMatch = color.match(/rgb\((\d{1,3}),\s*(\d{1,3}),\s*(\d{1,3})\)/);
|
|
31
|
+
let redColor, greenColor, blueColor;
|
|
32
|
+
if (rgbaMatch) {
|
|
33
|
+
redColor = parseInt(rgbaMatch[1], 10);
|
|
34
|
+
greenColor = parseInt(rgbaMatch[2], 10);
|
|
35
|
+
blueColor = parseInt(rgbaMatch[3], 10);
|
|
36
|
+
}
|
|
37
|
+
else if (rgbMatch) {
|
|
38
|
+
redColor = parseInt(rgbMatch[1], 10);
|
|
39
|
+
greenColor = parseInt(rgbMatch[2], 10);
|
|
40
|
+
blueColor = parseInt(rgbMatch[3], 10);
|
|
41
|
+
}
|
|
42
|
+
else {
|
|
43
|
+
// Both patterns failed to match
|
|
44
|
+
throw new Error(`Invalid color format '${color}'. Please provide a valid RGB or RGBA color.`);
|
|
45
|
+
}
|
|
46
|
+
const hexColor = '#' +
|
|
47
|
+
componentToHex(redColor) +
|
|
48
|
+
componentToHex(greenColor) +
|
|
49
|
+
componentToHex(blueColor);
|
|
50
|
+
return hexColor;
|
|
51
|
+
};
|
|
52
|
+
/**
|
|
53
|
+
* @remarks
|
|
54
|
+
* Receives a color in HEX format and returns it in RGB format
|
|
55
|
+
* @param hexColor - Color in HEX format
|
|
56
|
+
* @example
|
|
57
|
+
* ```ts
|
|
58
|
+
* const hexColor = '#ffffff';
|
|
59
|
+
* const rgbColor = hexToRgb(hexColor);
|
|
60
|
+
* console.log(rgbColor) // 'rgb(255, 255, 255)'
|
|
61
|
+
* ```
|
|
62
|
+
*
|
|
63
|
+
* @returns Color in RGB format string
|
|
64
|
+
*/
|
|
65
|
+
this.hexToRgb = (hexColor) => {
|
|
66
|
+
const hex = hexColor.replace(/^#/, '');
|
|
67
|
+
const bigint = parseInt(hex, 16);
|
|
68
|
+
const red = (bigint >> 16) & 255;
|
|
69
|
+
const green = (bigint >> 8) & 255;
|
|
70
|
+
const blue = bigint & 255;
|
|
71
|
+
return `rgb(${red}, ${green}, ${blue})`;
|
|
72
|
+
};
|
|
73
|
+
/**
|
|
74
|
+
* @remarks
|
|
75
|
+
* Checks whether a given HEX color is light or dark
|
|
76
|
+
* @param hexColor - Color in HEX format
|
|
77
|
+
* @example
|
|
78
|
+
* ```ts
|
|
79
|
+
* const hexColor = '#ffffff';
|
|
80
|
+
* const isLight = isLightColor(hexColor);
|
|
81
|
+
* console.log(isLight); // true or false
|
|
82
|
+
* ```
|
|
83
|
+
*
|
|
84
|
+
* @returns True if the color is light, false if it is dark
|
|
85
|
+
*/
|
|
86
|
+
this.isLightColor = (color) => {
|
|
87
|
+
// Remove spaces
|
|
88
|
+
const trimmedColor = color.replace(/\s/g, '');
|
|
89
|
+
// Check if the color is in RGBA format
|
|
90
|
+
const rgbaMatch = trimmedColor.match(/^rgba\((\d+),(\d+),(\d+),(\d+(\.\d+)?)\)$/);
|
|
91
|
+
if (rgbaMatch) {
|
|
92
|
+
// Extract RGB components from RGBA
|
|
93
|
+
const r = parseInt(rgbaMatch[1], 10);
|
|
94
|
+
const g = parseInt(rgbaMatch[2], 10);
|
|
95
|
+
const b = parseInt(rgbaMatch[3], 10);
|
|
96
|
+
// Calculate the relative luminance without considering alpha
|
|
97
|
+
const luminance = 0.299 * r + 0.587 * g + 0.114 * b;
|
|
98
|
+
// You can adjust the threshold to your preference
|
|
99
|
+
const threshold = 128;
|
|
100
|
+
// Determine whether the color is light or dark
|
|
101
|
+
return luminance > threshold;
|
|
102
|
+
}
|
|
103
|
+
// Check if the color is in RGB format
|
|
104
|
+
const rgbMatch = trimmedColor.match(/^rgb\((\d+),(\d+),(\d+)\)$/);
|
|
105
|
+
if (rgbMatch) {
|
|
106
|
+
// Extract RGB components from RGB
|
|
107
|
+
const r = parseInt(rgbMatch[1], 10);
|
|
108
|
+
const g = parseInt(rgbMatch[2], 10);
|
|
109
|
+
const b = parseInt(rgbMatch[3], 10);
|
|
110
|
+
// Calculate the relative luminance of the color
|
|
111
|
+
// This formula gives more weight to green as the human eye is more sensitive to it
|
|
112
|
+
const luminance = 0.299 * r + 0.587 * g + 0.114 * b;
|
|
113
|
+
// You can adjust the threshold to your preference
|
|
114
|
+
const threshold = 128;
|
|
115
|
+
// Determine whether the color is light or dark
|
|
116
|
+
return luminance > threshold;
|
|
117
|
+
}
|
|
118
|
+
// Remove the '#' character if present
|
|
119
|
+
const hexColor = trimmedColor.replace('#', '');
|
|
120
|
+
// Check if the hex color has a valid format
|
|
121
|
+
if (!/^[0-9A-Fa-f]{6}$/.test(hexColor)) {
|
|
122
|
+
throw new Error(`Invalid color format '${color}'. Please provide a valid HEX, RGB, or RGBA color.`);
|
|
123
|
+
}
|
|
124
|
+
// Convert the hex color to RGB components
|
|
125
|
+
const r = parseInt(hexColor.slice(0, 2), 16);
|
|
126
|
+
const g = parseInt(hexColor.slice(2, 4), 16);
|
|
127
|
+
const b = parseInt(hexColor.slice(4, 6), 16);
|
|
128
|
+
// Calculate the relative luminance of the color
|
|
129
|
+
// This formula gives more weight to green as the human eye is more sensitive to it
|
|
130
|
+
const luminance = 0.299 * r + 0.587 * g + 0.114 * b;
|
|
131
|
+
// You can adjust the threshold to your preference
|
|
132
|
+
const threshold = 128;
|
|
133
|
+
// Determine whether the color is light or dark
|
|
134
|
+
return luminance > threshold;
|
|
135
|
+
};
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
const colorful = new Colorful();
|
|
139
|
+
exports.default = colorful;
|
package/dist/lib/formatter.d.ts
CHANGED
|
@@ -10,14 +10,14 @@ declare class Formatter {
|
|
|
10
10
|
* // => 'Hello-World'
|
|
11
11
|
* ```
|
|
12
12
|
* */
|
|
13
|
-
toUpperFirst: (
|
|
13
|
+
toUpperFirst: (str: string | number) => string;
|
|
14
14
|
/**
|
|
15
15
|
* @remarks normalizes input to supported path and file name format.
|
|
16
16
|
* Changes camelCase strings to kebab-case, replaces spaces with dash and keeps underscores. *
|
|
17
17
|
* @param str - String needed to be modified
|
|
18
18
|
* @returns formatted string
|
|
19
19
|
*/
|
|
20
|
-
camelToKebab: (str: string) => string;
|
|
20
|
+
camelToKebab: (str: string | number) => string;
|
|
21
21
|
/**
|
|
22
22
|
* @remarks obfuscates email address
|
|
23
23
|
* @param email - email address to be obfuscated
|
|
@@ -39,7 +39,7 @@ declare class Formatter {
|
|
|
39
39
|
* console.log(newSentence) // 'This Is A Sentence'
|
|
40
40
|
* ```
|
|
41
41
|
*/
|
|
42
|
-
toUpperTitle: (sentence: string) => string;
|
|
42
|
+
toUpperTitle: (sentence: string | number) => string;
|
|
43
43
|
/**
|
|
44
44
|
* @remarks Generates a slug from a given string
|
|
45
45
|
* @param title - string to be converted to slug
|
|
@@ -49,7 +49,7 @@ declare class Formatter {
|
|
|
49
49
|
* formatter.slugify('Hello World') // => hello-world
|
|
50
50
|
* ```
|
|
51
51
|
* */
|
|
52
|
-
slugify: (title: string) => string;
|
|
52
|
+
slugify: (title: string | number) => string;
|
|
53
53
|
}
|
|
54
54
|
declare const formatter: Formatter;
|
|
55
55
|
export default formatter;
|
package/dist/lib/formatter.js
CHANGED
|
@@ -19,26 +19,15 @@ class Formatter {
|
|
|
19
19
|
* // => 'Hello-World'
|
|
20
20
|
* ```
|
|
21
21
|
* */
|
|
22
|
-
this.toUpperFirst = (
|
|
23
|
-
if (typeof
|
|
22
|
+
this.toUpperFirst = (str) => {
|
|
23
|
+
if (typeof str !== 'string')
|
|
24
24
|
throw new Error('Provide a valid string');
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
.replace(/\//g, '-')
|
|
32
|
-
.replace(/\./g, '');
|
|
33
|
-
}
|
|
34
|
-
else {
|
|
35
|
-
return _.toLowerCase()
|
|
36
|
-
.split(' ')
|
|
37
|
-
.map((val) => val.charAt(0).toUpperCase() + val.slice(1))
|
|
38
|
-
.join(' ')
|
|
39
|
-
.replace(/\//g, '-')
|
|
40
|
-
.replace(/\./g, '');
|
|
41
|
-
}
|
|
25
|
+
const formattedString = str
|
|
26
|
+
.toString()
|
|
27
|
+
.replace(/[^a-zA-Z0-9]+/g, '-')
|
|
28
|
+
.trim()
|
|
29
|
+
.toLowerCase();
|
|
30
|
+
return formattedString.charAt(0).toUpperCase() + formattedString.slice(1);
|
|
42
31
|
};
|
|
43
32
|
/**
|
|
44
33
|
* @remarks normalizes input to supported path and file name format.
|
|
@@ -47,6 +36,9 @@ class Formatter {
|
|
|
47
36
|
* @returns formatted string
|
|
48
37
|
*/
|
|
49
38
|
this.camelToKebab = (str) => {
|
|
39
|
+
if (typeof str !== 'string') {
|
|
40
|
+
throw new Error('Invalid input. Please provide a valid string.');
|
|
41
|
+
}
|
|
50
42
|
const STRING_DASHERIZE_REGEXP = /\s/g;
|
|
51
43
|
const STRING_DECAMELIZE_REGEXP = /([a-z\d])([A-Z])/g;
|
|
52
44
|
return str
|
|
@@ -96,14 +88,11 @@ class Formatter {
|
|
|
96
88
|
throw new Error('Provide a valid string');
|
|
97
89
|
if (!sentence)
|
|
98
90
|
throw new Error('Provide a valid string');
|
|
99
|
-
const sanitizedSentence = sentence.replace(/[^a-zA-
|
|
100
|
-
const words = sanitizedSentence.split(
|
|
101
|
-
const capitalizedWords =
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
const capitalizedWord = word.charAt(0).toUpperCase() + word.slice(1);
|
|
105
|
-
capitalizedWords.push(capitalizedWord);
|
|
106
|
-
}
|
|
91
|
+
const sanitizedSentence = sentence.replace(/[^a-zA-Z\s]+/g, ' '); // Exclude numbers from the character class
|
|
92
|
+
const words = sanitizedSentence.split(/\s+/);
|
|
93
|
+
const capitalizedWords = words.map((word) => {
|
|
94
|
+
return word.charAt(0).toUpperCase() + word.slice(1);
|
|
95
|
+
});
|
|
107
96
|
return capitalizedWords.join(' ');
|
|
108
97
|
};
|
|
109
98
|
/**
|
|
@@ -119,28 +108,8 @@ class Formatter {
|
|
|
119
108
|
if (typeof title !== 'string')
|
|
120
109
|
throw new Error('Provide a valid string');
|
|
121
110
|
return title
|
|
122
|
-
.replace(/
|
|
123
|
-
.replace(
|
|
124
|
-
.replace(/#/g, '-')
|
|
125
|
-
.replace(/\?/g, '-')
|
|
126
|
-
.replace(/,/g, '-')
|
|
127
|
-
.replace(/\//g, '-')
|
|
128
|
-
.replace(/\\/g, '-')
|
|
129
|
-
.replace(/\./g, '')
|
|
130
|
-
.replace(/'/g, '')
|
|
131
|
-
.replace(/"/g, '')
|
|
132
|
-
.replace(/\+/g, '-')
|
|
133
|
-
.replace(/\*/g, '-')
|
|
134
|
-
.replace(/\^/g, '-')
|
|
135
|
-
.replace(/@/g, '-')
|
|
136
|
-
.replace(/;/g, '-')
|
|
137
|
-
.replace(/:/g, '-')
|
|
138
|
-
.replace(/!/g, '-')
|
|
139
|
-
.replace(/&/g, '-')
|
|
140
|
-
.replace(/\$/g, '-')
|
|
141
|
-
.replace(/\(/g, '-')
|
|
142
|
-
.replace(/\)/g, '-')
|
|
143
|
-
.trim()
|
|
111
|
+
.replace(/[^\w\s]/g, '') // Remove non-alphanumeric characters
|
|
112
|
+
.replace(/\s+/g, '-') // Replace consecutive spaces with a single dash
|
|
144
113
|
.toLowerCase();
|
|
145
114
|
};
|
|
146
115
|
}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
declare class ImageFull {
|
|
2
|
+
/**
|
|
3
|
+
* @remarks
|
|
4
|
+
* Receives either a className or an id or an element and returns a promise that resolves when all images are loaded
|
|
5
|
+
* @param selector - either a className or an id or an element that contains images
|
|
6
|
+
* @example
|
|
7
|
+
* ```ts
|
|
8
|
+
* .image-container
|
|
9
|
+
* #image-container
|
|
10
|
+
* img //as an element
|
|
11
|
+
*
|
|
12
|
+
* Imagefull.preloadImages('.image-container').then((event) => {
|
|
13
|
+
* do something
|
|
14
|
+
* })
|
|
15
|
+
* ```
|
|
16
|
+
*
|
|
17
|
+
* @returns Promise<ImagesLoaded | undefined>
|
|
18
|
+
*
|
|
19
|
+
*/
|
|
20
|
+
preloadImages: (selector: string) => Promise<unknown>;
|
|
21
|
+
}
|
|
22
|
+
declare const imagefull: ImageFull;
|
|
23
|
+
export default imagefull;
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
const imagesloaded_1 = __importDefault(require("imagesloaded"));
|
|
7
|
+
/*
|
|
8
|
+
Author : Mustafa Halabi https://github.com/mustafahalabi
|
|
9
|
+
Date : 2024-01-29
|
|
10
|
+
Description : Loads images and returns a promise
|
|
11
|
+
Version : 1.0
|
|
12
|
+
*/
|
|
13
|
+
class ImageFull {
|
|
14
|
+
constructor() {
|
|
15
|
+
/**
|
|
16
|
+
* @remarks
|
|
17
|
+
* Receives either a className or an id or an element and returns a promise that resolves when all images are loaded
|
|
18
|
+
* @param selector - either a className or an id or an element that contains images
|
|
19
|
+
* @example
|
|
20
|
+
* ```ts
|
|
21
|
+
* .image-container
|
|
22
|
+
* #image-container
|
|
23
|
+
* img //as an element
|
|
24
|
+
*
|
|
25
|
+
* Imagefull.preloadImages('.image-container').then((event) => {
|
|
26
|
+
* do something
|
|
27
|
+
* })
|
|
28
|
+
* ```
|
|
29
|
+
*
|
|
30
|
+
* @returns Promise<ImagesLoaded | undefined>
|
|
31
|
+
*
|
|
32
|
+
*/
|
|
33
|
+
this.preloadImages = (selector) => {
|
|
34
|
+
return new Promise((resolve) => {
|
|
35
|
+
(0, imagesloaded_1.default)(document.querySelectorAll(selector), { background: true }, (event) => {
|
|
36
|
+
resolve(event);
|
|
37
|
+
});
|
|
38
|
+
});
|
|
39
|
+
};
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
const imagefull = new ImageFull();
|
|
43
|
+
exports.default = imagefull;
|
package/dist/lib/index.d.ts
CHANGED
package/dist/lib/index.js
CHANGED
|
@@ -3,10 +3,14 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
3
3
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
4
|
};
|
|
5
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
-
exports.Pdf = exports.Formatter = exports.Crypto = void 0;
|
|
6
|
+
exports.ImageFull = exports.Colorful = exports.Pdf = exports.Formatter = exports.Crypto = void 0;
|
|
7
7
|
var cypto_1 = require("./cypto");
|
|
8
8
|
Object.defineProperty(exports, "Crypto", { enumerable: true, get: function () { return __importDefault(cypto_1).default; } });
|
|
9
9
|
var formatter_1 = require("./formatter");
|
|
10
10
|
Object.defineProperty(exports, "Formatter", { enumerable: true, get: function () { return __importDefault(formatter_1).default; } });
|
|
11
11
|
var formatter_2 = require("./formatter");
|
|
12
12
|
Object.defineProperty(exports, "Pdf", { enumerable: true, get: function () { return __importDefault(formatter_2).default; } });
|
|
13
|
+
var colorful_1 = require("./colorful");
|
|
14
|
+
Object.defineProperty(exports, "Colorful", { enumerable: true, get: function () { return __importDefault(colorful_1).default; } });
|
|
15
|
+
var imagefull_1 = require("./imagefull");
|
|
16
|
+
Object.defineProperty(exports, "ImageFull", { enumerable: true, get: function () { return __importDefault(imagefull_1).default; } });
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
/// <reference types="jquery" />
|
|
2
|
+
|
|
3
|
+
declare namespace ImagesLoaded {
|
|
4
|
+
type ElementSelector = Element | NodeList | Element[] | string
|
|
5
|
+
|
|
6
|
+
/** interface for an image currently loading or completed */
|
|
7
|
+
interface LoadingImage {
|
|
8
|
+
img: HTMLImageElement
|
|
9
|
+
isLoaded: boolean
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
interface ImagesLoadedCallback {
|
|
13
|
+
(instance?: ImagesLoaded): void
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
interface ImagesLoadedListener {
|
|
17
|
+
(instance: ImagesLoaded, image?: LoadingImage): void
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
interface ImagesLoaded {
|
|
21
|
+
new (elem: ElementSelector, callback: ImagesLoadedCallback): ImagesLoaded
|
|
22
|
+
|
|
23
|
+
images: LoadingImage[]
|
|
24
|
+
|
|
25
|
+
// event listeners
|
|
26
|
+
on(event: string, listener: ImagesLoadedListener): void
|
|
27
|
+
off(event: string, listener: ImagesLoadedListener): void
|
|
28
|
+
once(event: string, listener: ImagesLoadedListener): void
|
|
29
|
+
progressedCount: number | undefined
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
interface ImagesLoadedOptions {
|
|
33
|
+
background: true | string
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
interface ImagesLoadedConstructor {
|
|
37
|
+
/**
|
|
38
|
+
* Creates a new ImagesLoaded object with the provided callback
|
|
39
|
+
* @param elem Element, NodeList, Element array, or selector string for images to watch
|
|
40
|
+
* @param options object that can tell imagesloaded to watch background images as well
|
|
41
|
+
* @param callback function triggered after all images have been loaded
|
|
42
|
+
*/
|
|
43
|
+
(
|
|
44
|
+
elem: ElementSelector,
|
|
45
|
+
options: ImagesLoadedOptions,
|
|
46
|
+
callback?: ImagesLoadedCallback
|
|
47
|
+
): ImagesLoaded
|
|
48
|
+
(elem: ElementSelector, callback?: ImagesLoadedCallback): ImagesLoaded
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
declare var imagesLoaded: ImagesLoaded.ImagesLoadedConstructor
|
|
53
|
+
|
|
54
|
+
declare module 'imagesloaded' {
|
|
55
|
+
export = imagesLoaded
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
interface JQuery {
|
|
59
|
+
imagesLoaded(
|
|
60
|
+
callback?: ImagesLoaded.ImagesLoadedCallback
|
|
61
|
+
): JQueryDeferred<ImagesLoaded.ImagesLoaded>
|
|
62
|
+
imagesLoaded(
|
|
63
|
+
options: ImagesLoaded.ImagesLoadedOptions,
|
|
64
|
+
callback?: ImagesLoaded.ImagesLoadedCallback
|
|
65
|
+
): JQueryDeferred<ImagesLoaded.ImagesLoaded>
|
|
66
|
+
}
|
package/jest.config.js
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
/** @type {import('ts-jest').JestConfigWithTsJest} */
|
|
2
|
+
module.exports = {
|
|
3
|
+
preset: 'ts-jest',
|
|
4
|
+
testEnvironment: 'node',
|
|
5
|
+
coverageDirectory: 'coverage',
|
|
6
|
+
coverageProvider: 'v8',
|
|
7
|
+
collectCoverageFrom: ['src/**/*.ts', '!test/**/*.ts'],
|
|
8
|
+
transform: {
|
|
9
|
+
'^.+\\.(ts)$': 'ts-jest',
|
|
10
|
+
},
|
|
11
|
+
transformIgnorePatterns: [],
|
|
12
|
+
testMatch: ['<rootDir>/src/**/__tests__/**/*.test.ts'],
|
|
13
|
+
}
|
package/package.json
CHANGED
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@mhmdhammoud/meritt-utils",
|
|
3
|
-
"version": "1.0
|
|
3
|
+
"version": "1.2.0",
|
|
4
4
|
"description": "",
|
|
5
5
|
"main": "./dist/index.js",
|
|
6
6
|
"private": false,
|
|
7
|
+
"typings": "./dist/index.d.ts",
|
|
7
8
|
"repository": {
|
|
8
9
|
"type": "git",
|
|
9
10
|
"url": "git@github.com:Mhmdhammoud/meritt-utils.git"
|
|
@@ -19,21 +20,26 @@
|
|
|
19
20
|
"types": "tsc --noemit",
|
|
20
21
|
"prepare": "husky install",
|
|
21
22
|
"lint": "eslint src --fix",
|
|
22
|
-
"prepublish": "npm run build"
|
|
23
|
+
"prepublish": "npm run build",
|
|
24
|
+
"test": "jest",
|
|
25
|
+
"test:coverage": "jest --coverage"
|
|
23
26
|
},
|
|
24
27
|
"devDependencies": {
|
|
25
|
-
"@types/jest": "^27.5.
|
|
28
|
+
"@types/jest": "^27.5.2",
|
|
26
29
|
"@typescript-eslint/eslint-plugin": "^5.17.0",
|
|
27
30
|
"@typescript-eslint/parser": "^5.17.0",
|
|
28
31
|
"eslint": "^8.12.0",
|
|
29
32
|
"eslint-plugin-tsdoc": "^0.2.14",
|
|
30
33
|
"husky": "^8.0.0",
|
|
34
|
+
"jest": "^29.7.0",
|
|
35
|
+
"ts-jest": "^29.1.1",
|
|
31
36
|
"ts-node-dev": "^1.1.8",
|
|
32
37
|
"typescript": "^4.6.3"
|
|
33
38
|
},
|
|
34
39
|
"author": "Mhmdhammoud",
|
|
35
40
|
"license": "ISC",
|
|
36
41
|
"dependencies": {
|
|
37
|
-
"axios": "^1.4.0"
|
|
42
|
+
"axios": "^1.4.0",
|
|
43
|
+
"imagesloaded": "^5.0.0"
|
|
38
44
|
}
|
|
39
45
|
}
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
import {Colorful} from '../lib'
|
|
2
|
+
|
|
3
|
+
describe('Colorful Class', () => {
|
|
4
|
+
describe('rgbToHex method', () => {
|
|
5
|
+
it('should convert RGB to HEX', () => {
|
|
6
|
+
const rgbColor = 'rgb(255, 255, 255)'
|
|
7
|
+
const hexColor = Colorful.rgbToHex(rgbColor)
|
|
8
|
+
expect(hexColor).toBe('#ffffff')
|
|
9
|
+
})
|
|
10
|
+
|
|
11
|
+
it('should convert RGBA to HEX', () => {
|
|
12
|
+
const rgbaColor = 'rgba(255, 0, 0, 0.5)'
|
|
13
|
+
const hexColor = Colorful.rgbToHex(rgbaColor)
|
|
14
|
+
expect(hexColor).toBe('#ff0000')
|
|
15
|
+
})
|
|
16
|
+
|
|
17
|
+
it('should throw an error for invalid color format', () => {
|
|
18
|
+
const invalidColor = 'invalidColor'
|
|
19
|
+
expect(() => Colorful.rgbToHex(invalidColor)).toThrowError(
|
|
20
|
+
`Invalid color format '${invalidColor}'. Please provide a valid RGB or RGBA color.`
|
|
21
|
+
)
|
|
22
|
+
})
|
|
23
|
+
})
|
|
24
|
+
|
|
25
|
+
describe('hexToRgb method', () => {
|
|
26
|
+
it('should convert HEX to RGB', () => {
|
|
27
|
+
const hexColor = '#ffffff'
|
|
28
|
+
const rgbColor = Colorful.hexToRgb(hexColor)
|
|
29
|
+
expect(rgbColor).toBe('rgb(255, 255, 255)')
|
|
30
|
+
})
|
|
31
|
+
})
|
|
32
|
+
|
|
33
|
+
describe('isLightColor method', () => {
|
|
34
|
+
it('should return true for a light color (HEX format)', () => {
|
|
35
|
+
const lightHexColor = '#f0f0f0'
|
|
36
|
+
const isLight = Colorful.isLightColor(lightHexColor)
|
|
37
|
+
expect(isLight).toBe(true)
|
|
38
|
+
})
|
|
39
|
+
|
|
40
|
+
it('should return false for a dark color (HEX format)', () => {
|
|
41
|
+
const darkHexColor = '#333333'
|
|
42
|
+
const isLight = Colorful.isLightColor(darkHexColor)
|
|
43
|
+
expect(isLight).toBe(false)
|
|
44
|
+
})
|
|
45
|
+
|
|
46
|
+
it('should return true for a light color (RGBA format)', () => {
|
|
47
|
+
const lightRgbaColor = 'rgba(255, 255, 255, 0.5)'
|
|
48
|
+
const isLight = Colorful.isLightColor(lightRgbaColor)
|
|
49
|
+
expect(isLight).toBe(true)
|
|
50
|
+
})
|
|
51
|
+
|
|
52
|
+
it('should return false for a dark color (RGBA format)', () => {
|
|
53
|
+
const darkRgbaColor = 'rgba(0, 0, 0, 0.8)'
|
|
54
|
+
const isLight = Colorful.isLightColor(darkRgbaColor)
|
|
55
|
+
expect(isLight).toBe(false)
|
|
56
|
+
})
|
|
57
|
+
|
|
58
|
+
it('should return true for a light color (RGB format)', () => {
|
|
59
|
+
const lightRgbColor = 'rgb(200, 220, 255)'
|
|
60
|
+
const isLight = Colorful.isLightColor(lightRgbColor)
|
|
61
|
+
expect(isLight).toBe(true)
|
|
62
|
+
})
|
|
63
|
+
|
|
64
|
+
it('should return false for a dark color (RGB format)', () => {
|
|
65
|
+
const darkRgbColor = 'rgb(10, 20, 30)'
|
|
66
|
+
const isLight = Colorful.isLightColor(darkRgbColor)
|
|
67
|
+
expect(isLight).toBe(false)
|
|
68
|
+
})
|
|
69
|
+
|
|
70
|
+
it('should throw an error for an invalid color format', () => {
|
|
71
|
+
const invalidColor = 'invalid-color'
|
|
72
|
+
expect(() => Colorful.isLightColor(invalidColor)).toThrowError(
|
|
73
|
+
"Invalid color format 'invalid-color'. Please provide a valid HEX, RGB, or RGBA color."
|
|
74
|
+
)
|
|
75
|
+
})
|
|
76
|
+
})
|
|
77
|
+
})
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
import {Formatter} from '../lib'
|
|
2
|
+
|
|
3
|
+
describe('Formatter', () => {
|
|
4
|
+
describe('toUpperFirst method', () => {
|
|
5
|
+
it('should format string with - and upper first letter', () => {
|
|
6
|
+
const formattedString = Formatter.toUpperFirst('hello world')
|
|
7
|
+
expect(formattedString).toBe('Hello-world')
|
|
8
|
+
})
|
|
9
|
+
|
|
10
|
+
it('should format string with spaces and upper first letter of every word', () => {
|
|
11
|
+
const formattedString = Formatter.toUpperFirst('hello_world+test')
|
|
12
|
+
expect(formattedString).toBe('Hello-world-test')
|
|
13
|
+
})
|
|
14
|
+
|
|
15
|
+
it('should throw an error for non-string input', () => {
|
|
16
|
+
expect(() => Formatter.toUpperFirst(123)).toThrowError(
|
|
17
|
+
'Provide a valid string'
|
|
18
|
+
)
|
|
19
|
+
})
|
|
20
|
+
})
|
|
21
|
+
|
|
22
|
+
describe('camelToKebab method', () => {
|
|
23
|
+
it('should convert camelCase to kebab-case', () => {
|
|
24
|
+
const kebabString = Formatter.camelToKebab('camelCaseString')
|
|
25
|
+
expect(kebabString).toBe('camel-case-string')
|
|
26
|
+
})
|
|
27
|
+
|
|
28
|
+
it('should handle spaces and underscores', () => {
|
|
29
|
+
const kebabString = Formatter.camelToKebab('hello World_with Spaces')
|
|
30
|
+
expect(kebabString).toBe('hello-world_with-spaces')
|
|
31
|
+
})
|
|
32
|
+
|
|
33
|
+
it('should throw an error for non-string input', () => {
|
|
34
|
+
expect(() => Formatter.camelToKebab(123)).toThrowError(
|
|
35
|
+
'Invalid input. Please provide a valid string.'
|
|
36
|
+
)
|
|
37
|
+
})
|
|
38
|
+
})
|
|
39
|
+
|
|
40
|
+
describe('obfuscate method', () => {
|
|
41
|
+
it('should obfuscate email address', () => {
|
|
42
|
+
const obfuscatedEmail = Formatter.obfuscate('johndoe@email.com')
|
|
43
|
+
expect(obfuscatedEmail).toMatch(/^jo.*@.*$/)
|
|
44
|
+
})
|
|
45
|
+
|
|
46
|
+
it('should throw an error for non-string input', () => {
|
|
47
|
+
//@ts-ignore
|
|
48
|
+
expect(() => Formatter.obfuscate(123)).toThrowError(
|
|
49
|
+
'Provide a valid string'
|
|
50
|
+
)
|
|
51
|
+
})
|
|
52
|
+
|
|
53
|
+
it('should throw an error for an invalid email address', () => {
|
|
54
|
+
expect(() => Formatter.obfuscate('invalid-email')).toThrowError(
|
|
55
|
+
'Provide a valid email address'
|
|
56
|
+
)
|
|
57
|
+
})
|
|
58
|
+
})
|
|
59
|
+
|
|
60
|
+
describe('toUpperTitle method', () => {
|
|
61
|
+
it('should capitalize first letter of each word and remove non-alphanumeric characters', () => {
|
|
62
|
+
const upperTitle = Formatter.toUpperTitle('this is34354345a---sentence')
|
|
63
|
+
expect(upperTitle).toBe('This Is A Sentence')
|
|
64
|
+
})
|
|
65
|
+
|
|
66
|
+
it('should throw an error for non-string input', () => {
|
|
67
|
+
expect(() => Formatter.toUpperTitle(123)).toThrowError(
|
|
68
|
+
'Provide a valid string'
|
|
69
|
+
)
|
|
70
|
+
})
|
|
71
|
+
|
|
72
|
+
it('should throw an error for empty string', () => {
|
|
73
|
+
expect(() => Formatter.toUpperTitle('')).toThrowError(
|
|
74
|
+
'Provide a valid string'
|
|
75
|
+
)
|
|
76
|
+
})
|
|
77
|
+
})
|
|
78
|
+
|
|
79
|
+
describe('slugify method', () => {
|
|
80
|
+
it('should generate a slug from the given string', () => {
|
|
81
|
+
const slug = Formatter.slugify('Hello World')
|
|
82
|
+
expect(slug).toBe('hello-world')
|
|
83
|
+
})
|
|
84
|
+
|
|
85
|
+
it('should handle special characters and spaces', () => {
|
|
86
|
+
const slug = Formatter.slugify('Special Characters % #$ & Space')
|
|
87
|
+
expect(slug).toBe('special-characters-space')
|
|
88
|
+
})
|
|
89
|
+
|
|
90
|
+
it('should throw an error for non-string input', () => {
|
|
91
|
+
expect(() => Formatter.slugify(123)).toThrowError(
|
|
92
|
+
'Provide a valid string'
|
|
93
|
+
)
|
|
94
|
+
})
|
|
95
|
+
})
|
|
96
|
+
})
|
package/src/index.ts
CHANGED
|
@@ -1,2 +1 @@
|
|
|
1
|
-
export {Crypto} from './lib'
|
|
2
|
-
export {Formatter} from './lib'
|
|
1
|
+
export {Crypto, Formatter, Colorful} from './lib'
|
|
@@ -0,0 +1,167 @@
|
|
|
1
|
+
/*
|
|
2
|
+
Author : Mhmd Hammoud https://github.com/mhmdhammoud
|
|
3
|
+
Date : 2024-01-20
|
|
4
|
+
Description : Color adapter
|
|
5
|
+
Version : 1.0
|
|
6
|
+
*/
|
|
7
|
+
class Colorful {
|
|
8
|
+
/**
|
|
9
|
+
* @remarks
|
|
10
|
+
* Receives a color in RGB format and returns it in HEX format
|
|
11
|
+
* @param color - Color in RGB format
|
|
12
|
+
* @example
|
|
13
|
+
* ```ts
|
|
14
|
+
* const color = 'rgb(255, 255, 255)';
|
|
15
|
+
* const hexColor = rgbToHex(color);
|
|
16
|
+
* console.log(hexColor) // #ffffff
|
|
17
|
+
* ```
|
|
18
|
+
*
|
|
19
|
+
* @returns Color in HEX format string
|
|
20
|
+
*/
|
|
21
|
+
rgbToHex = (color: string): string => {
|
|
22
|
+
const componentToHex = (c: any) => {
|
|
23
|
+
const hex = c.toString(16)
|
|
24
|
+
return hex.length == 1 ? '0' + hex : hex
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
const rgbaMatch = color.match(
|
|
28
|
+
/rgba\((\d{1,3}),\s*(\d{1,3}),\s*(\d{1,3}),\s*((\d+\.)?\d+)\)/
|
|
29
|
+
)
|
|
30
|
+
const rgbMatch = color.match(/rgb\((\d{1,3}),\s*(\d{1,3}),\s*(\d{1,3})\)/)
|
|
31
|
+
|
|
32
|
+
let redColor, greenColor, blueColor
|
|
33
|
+
|
|
34
|
+
if (rgbaMatch) {
|
|
35
|
+
redColor = parseInt(rgbaMatch[1], 10)
|
|
36
|
+
greenColor = parseInt(rgbaMatch[2], 10)
|
|
37
|
+
blueColor = parseInt(rgbaMatch[3], 10)
|
|
38
|
+
} else if (rgbMatch) {
|
|
39
|
+
redColor = parseInt(rgbMatch[1], 10)
|
|
40
|
+
greenColor = parseInt(rgbMatch[2], 10)
|
|
41
|
+
blueColor = parseInt(rgbMatch[3], 10)
|
|
42
|
+
} else {
|
|
43
|
+
// Both patterns failed to match
|
|
44
|
+
throw new Error(
|
|
45
|
+
`Invalid color format '${color}'. Please provide a valid RGB or RGBA color.`
|
|
46
|
+
)
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
const hexColor =
|
|
50
|
+
'#' +
|
|
51
|
+
componentToHex(redColor) +
|
|
52
|
+
componentToHex(greenColor) +
|
|
53
|
+
componentToHex(blueColor)
|
|
54
|
+
|
|
55
|
+
return hexColor
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
/**
|
|
59
|
+
* @remarks
|
|
60
|
+
* Receives a color in HEX format and returns it in RGB format
|
|
61
|
+
* @param hexColor - Color in HEX format
|
|
62
|
+
* @example
|
|
63
|
+
* ```ts
|
|
64
|
+
* const hexColor = '#ffffff';
|
|
65
|
+
* const rgbColor = hexToRgb(hexColor);
|
|
66
|
+
* console.log(rgbColor) // 'rgb(255, 255, 255)'
|
|
67
|
+
* ```
|
|
68
|
+
*
|
|
69
|
+
* @returns Color in RGB format string
|
|
70
|
+
*/
|
|
71
|
+
hexToRgb = (hexColor: string): string => {
|
|
72
|
+
const hex = hexColor.replace(/^#/, '')
|
|
73
|
+
const bigint = parseInt(hex, 16)
|
|
74
|
+
const red = (bigint >> 16) & 255
|
|
75
|
+
const green = (bigint >> 8) & 255
|
|
76
|
+
const blue = bigint & 255
|
|
77
|
+
return `rgb(${red}, ${green}, ${blue})`
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
/**
|
|
81
|
+
* @remarks
|
|
82
|
+
* Checks whether a given HEX color is light or dark
|
|
83
|
+
* @param hexColor - Color in HEX format
|
|
84
|
+
* @example
|
|
85
|
+
* ```ts
|
|
86
|
+
* const hexColor = '#ffffff';
|
|
87
|
+
* const isLight = isLightColor(hexColor);
|
|
88
|
+
* console.log(isLight); // true or false
|
|
89
|
+
* ```
|
|
90
|
+
*
|
|
91
|
+
* @returns True if the color is light, false if it is dark
|
|
92
|
+
*/
|
|
93
|
+
isLightColor = (color: string): boolean => {
|
|
94
|
+
// Remove spaces
|
|
95
|
+
const trimmedColor = color.replace(/\s/g, '')
|
|
96
|
+
|
|
97
|
+
// Check if the color is in RGBA format
|
|
98
|
+
const rgbaMatch = trimmedColor.match(
|
|
99
|
+
/^rgba\((\d+),(\d+),(\d+),(\d+(\.\d+)?)\)$/
|
|
100
|
+
)
|
|
101
|
+
|
|
102
|
+
if (rgbaMatch) {
|
|
103
|
+
// Extract RGB components from RGBA
|
|
104
|
+
const r = parseInt(rgbaMatch[1], 10)
|
|
105
|
+
const g = parseInt(rgbaMatch[2], 10)
|
|
106
|
+
const b = parseInt(rgbaMatch[3], 10)
|
|
107
|
+
|
|
108
|
+
// Calculate the relative luminance without considering alpha
|
|
109
|
+
const luminance = 0.299 * r + 0.587 * g + 0.114 * b
|
|
110
|
+
|
|
111
|
+
// You can adjust the threshold to your preference
|
|
112
|
+
const threshold = 128
|
|
113
|
+
|
|
114
|
+
// Determine whether the color is light or dark
|
|
115
|
+
return luminance > threshold
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
// Check if the color is in RGB format
|
|
119
|
+
const rgbMatch = trimmedColor.match(/^rgb\((\d+),(\d+),(\d+)\)$/)
|
|
120
|
+
|
|
121
|
+
if (rgbMatch) {
|
|
122
|
+
// Extract RGB components from RGB
|
|
123
|
+
const r = parseInt(rgbMatch[1], 10)
|
|
124
|
+
const g = parseInt(rgbMatch[2], 10)
|
|
125
|
+
const b = parseInt(rgbMatch[3], 10)
|
|
126
|
+
|
|
127
|
+
// Calculate the relative luminance of the color
|
|
128
|
+
// This formula gives more weight to green as the human eye is more sensitive to it
|
|
129
|
+
const luminance = 0.299 * r + 0.587 * g + 0.114 * b
|
|
130
|
+
|
|
131
|
+
// You can adjust the threshold to your preference
|
|
132
|
+
const threshold = 128
|
|
133
|
+
|
|
134
|
+
// Determine whether the color is light or dark
|
|
135
|
+
return luminance > threshold
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
// Remove the '#' character if present
|
|
139
|
+
const hexColor = trimmedColor.replace('#', '')
|
|
140
|
+
|
|
141
|
+
// Check if the hex color has a valid format
|
|
142
|
+
if (!/^[0-9A-Fa-f]{6}$/.test(hexColor)) {
|
|
143
|
+
throw new Error(
|
|
144
|
+
`Invalid color format '${color}'. Please provide a valid HEX, RGB, or RGBA color.`
|
|
145
|
+
)
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
// Convert the hex color to RGB components
|
|
149
|
+
const r = parseInt(hexColor.slice(0, 2), 16)
|
|
150
|
+
const g = parseInt(hexColor.slice(2, 4), 16)
|
|
151
|
+
const b = parseInt(hexColor.slice(4, 6), 16)
|
|
152
|
+
|
|
153
|
+
// Calculate the relative luminance of the color
|
|
154
|
+
// This formula gives more weight to green as the human eye is more sensitive to it
|
|
155
|
+
const luminance = 0.299 * r + 0.587 * g + 0.114 * b
|
|
156
|
+
|
|
157
|
+
// You can adjust the threshold to your preference
|
|
158
|
+
const threshold = 128
|
|
159
|
+
|
|
160
|
+
// Determine whether the color is light or dark
|
|
161
|
+
return luminance > threshold
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
const colorful = new Colorful()
|
|
166
|
+
|
|
167
|
+
export default colorful
|
package/src/lib/formatter.ts
CHANGED
|
@@ -16,24 +16,16 @@ class Formatter {
|
|
|
16
16
|
* // => 'Hello-World'
|
|
17
17
|
* ```
|
|
18
18
|
* */
|
|
19
|
-
toUpperFirst = (
|
|
20
|
-
if (typeof
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
} else {
|
|
30
|
-
return _.toLowerCase()
|
|
31
|
-
.split(' ')
|
|
32
|
-
.map((val: string) => val.charAt(0).toUpperCase() + val.slice(1))
|
|
33
|
-
.join(' ')
|
|
34
|
-
.replace(/\//g, '-')
|
|
35
|
-
.replace(/\./g, '')
|
|
36
|
-
}
|
|
19
|
+
toUpperFirst = (str: string | number): string => {
|
|
20
|
+
if (typeof str !== 'string') throw new Error('Provide a valid string')
|
|
21
|
+
|
|
22
|
+
const formattedString = str
|
|
23
|
+
.toString()
|
|
24
|
+
.replace(/[^a-zA-Z0-9]+/g, '-')
|
|
25
|
+
.trim()
|
|
26
|
+
.toLowerCase()
|
|
27
|
+
|
|
28
|
+
return formattedString.charAt(0).toUpperCase() + formattedString.slice(1)
|
|
37
29
|
}
|
|
38
30
|
|
|
39
31
|
/**
|
|
@@ -42,9 +34,14 @@ class Formatter {
|
|
|
42
34
|
* @param str - String needed to be modified
|
|
43
35
|
* @returns formatted string
|
|
44
36
|
*/
|
|
45
|
-
camelToKebab = (str: string) => {
|
|
37
|
+
camelToKebab = (str: string | number): string => {
|
|
38
|
+
if (typeof str !== 'string') {
|
|
39
|
+
throw new Error('Invalid input. Please provide a valid string.')
|
|
40
|
+
}
|
|
41
|
+
|
|
46
42
|
const STRING_DASHERIZE_REGEXP = /\s/g
|
|
47
43
|
const STRING_DECAMELIZE_REGEXP = /([a-z\d])([A-Z])/g
|
|
44
|
+
|
|
48
45
|
return str
|
|
49
46
|
.replace(STRING_DECAMELIZE_REGEXP, '$1-$2')
|
|
50
47
|
.toLowerCase()
|
|
@@ -92,19 +89,15 @@ class Formatter {
|
|
|
92
89
|
* console.log(newSentence) // 'This Is A Sentence'
|
|
93
90
|
* ```
|
|
94
91
|
*/
|
|
95
|
-
toUpperTitle = (sentence: string): string => {
|
|
92
|
+
toUpperTitle = (sentence: string | number): string => {
|
|
96
93
|
if (typeof sentence !== 'string') throw new Error('Provide a valid string')
|
|
97
94
|
if (!sentence) throw new Error('Provide a valid string')
|
|
98
95
|
|
|
99
|
-
const sanitizedSentence = sentence.replace(/[^a-zA-
|
|
100
|
-
const words = sanitizedSentence.split(
|
|
101
|
-
const capitalizedWords =
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
const word = words[i]
|
|
105
|
-
const capitalizedWord = word.charAt(0).toUpperCase() + word.slice(1)
|
|
106
|
-
capitalizedWords.push(capitalizedWord)
|
|
107
|
-
}
|
|
96
|
+
const sanitizedSentence = sentence.replace(/[^a-zA-Z\s]+/g, ' ') // Exclude numbers from the character class
|
|
97
|
+
const words = sanitizedSentence.split(/\s+/)
|
|
98
|
+
const capitalizedWords = words.map((word) => {
|
|
99
|
+
return word.charAt(0).toUpperCase() + word.slice(1)
|
|
100
|
+
})
|
|
108
101
|
|
|
109
102
|
return capitalizedWords.join(' ')
|
|
110
103
|
}
|
|
@@ -118,31 +111,12 @@ class Formatter {
|
|
|
118
111
|
* formatter.slugify('Hello World') // => hello-world
|
|
119
112
|
* ```
|
|
120
113
|
* */
|
|
121
|
-
slugify = (title: string) => {
|
|
114
|
+
slugify = (title: string | number): string => {
|
|
122
115
|
if (typeof title !== 'string') throw new Error('Provide a valid string')
|
|
116
|
+
|
|
123
117
|
return title
|
|
124
|
-
.replace(/
|
|
125
|
-
.replace(
|
|
126
|
-
.replace(/#/g, '-')
|
|
127
|
-
.replace(/\?/g, '-')
|
|
128
|
-
.replace(/,/g, '-')
|
|
129
|
-
.replace(/\//g, '-')
|
|
130
|
-
.replace(/\\/g, '-')
|
|
131
|
-
.replace(/\./g, '')
|
|
132
|
-
.replace(/'/g, '')
|
|
133
|
-
.replace(/"/g, '')
|
|
134
|
-
.replace(/\+/g, '-')
|
|
135
|
-
.replace(/\*/g, '-')
|
|
136
|
-
.replace(/\^/g, '-')
|
|
137
|
-
.replace(/@/g, '-')
|
|
138
|
-
.replace(/;/g, '-')
|
|
139
|
-
.replace(/:/g, '-')
|
|
140
|
-
.replace(/!/g, '-')
|
|
141
|
-
.replace(/&/g, '-')
|
|
142
|
-
.replace(/\$/g, '-')
|
|
143
|
-
.replace(/\(/g, '-')
|
|
144
|
-
.replace(/\)/g, '-')
|
|
145
|
-
.trim()
|
|
118
|
+
.replace(/[^\w\s]/g, '') // Remove non-alphanumeric characters
|
|
119
|
+
.replace(/\s+/g, '-') // Replace consecutive spaces with a single dash
|
|
146
120
|
.toLowerCase()
|
|
147
121
|
}
|
|
148
122
|
}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import imagesLoaded from 'imagesloaded'
|
|
2
|
+
/*
|
|
3
|
+
Author : Mustafa Halabi https://github.com/mustafahalabi
|
|
4
|
+
Date : 2024-01-29
|
|
5
|
+
Description : Loads images and returns a promise
|
|
6
|
+
Version : 1.0
|
|
7
|
+
*/
|
|
8
|
+
class ImageFull {
|
|
9
|
+
/**
|
|
10
|
+
* @remarks
|
|
11
|
+
* Receives either a className or an id or an element and returns a promise that resolves when all images are loaded
|
|
12
|
+
* @param selector - either a className or an id or an element that contains images
|
|
13
|
+
* @example
|
|
14
|
+
* ```ts
|
|
15
|
+
* .image-container
|
|
16
|
+
* #image-container
|
|
17
|
+
* img //as an element
|
|
18
|
+
*
|
|
19
|
+
* Imagefull.preloadImages('.image-container').then((event) => {
|
|
20
|
+
* do something
|
|
21
|
+
* })
|
|
22
|
+
* ```
|
|
23
|
+
*
|
|
24
|
+
* @returns Promise<ImagesLoaded | undefined>
|
|
25
|
+
*
|
|
26
|
+
*/
|
|
27
|
+
preloadImages = (selector: string) => {
|
|
28
|
+
return new Promise((resolve) => {
|
|
29
|
+
imagesLoaded(
|
|
30
|
+
document.querySelectorAll(selector),
|
|
31
|
+
{background: true},
|
|
32
|
+
(event) => {
|
|
33
|
+
resolve(event)
|
|
34
|
+
}
|
|
35
|
+
)
|
|
36
|
+
})
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
const imagefull = new ImageFull()
|
|
41
|
+
export default imagefull
|