@etsoo/shared 1.1.15 → 1.1.18

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/README.md CHANGED
@@ -26,7 +26,12 @@ Etsoo implmented Color
26
26
  |static getColors|Get HEX or RGB colors|
27
27
  |static getEColors|Get EColors|
28
28
  |static parse|Parse HTML color to EColor|
29
+ |clone|Clone color with adjustments|
30
+ |getContrastRatio|Get contrast ratio, a value between 0 and 1|
31
+ |getDeltaValue|Get Delta value (perceptible by human eyes)|
32
+ |getLuminance|Get luminance|
29
33
  |toHEXColor|To HEX color string|
34
+ |toLabValue|To Lab value|
30
35
  |toRGBColor|To RGB color string|
31
36
 
32
37
  ## Keyboard
@@ -1,4 +1,5 @@
1
1
  import { EColor } from '../src/types/EColor';
2
+ import { ColorUtils } from '../src/ColorUtils';
2
3
 
3
4
  test('Tests for parse', () => {
4
5
  // Arrange & act
@@ -13,5 +14,13 @@ test('Tests for parse', () => {
13
14
  });
14
15
 
15
16
  test('Tests for getColors', () => {
16
- expect(EColor.getColors(undefined, 128).length).toBe(8);
17
+ const colors = ColorUtils.getColors(undefined, 128);
18
+ expect(colors.length).toBe(8);
19
+ });
20
+
21
+ test('Tests for toRGBColor', () => {
22
+ const color = new EColor(0, 0, 0);
23
+ expect(color.toRGBColor()).toBe('RGB(0, 0, 0)');
24
+ expect(color.toRGBColor(0.1)).toBe('RGBA(0, 0, 0, 0.1)');
25
+ expect(color.alpha).toBeUndefined();
17
26
  });
@@ -0,0 +1,23 @@
1
+ import { EColor } from './types/EColor';
2
+ /**
3
+ * Color utils
4
+ */
5
+ export declare namespace ColorUtils {
6
+ /**
7
+ * Get HEX or RGB colors
8
+ * @param init Initial color
9
+ * @param factor Increase factor
10
+ * @param adjustOrder Adjust order to increase difference
11
+ * @param hex to HEX or not
12
+ * @returns Result
13
+ */
14
+ function getColors(init?: string, factor?: number, adjustOrder?: boolean, hex?: boolean): string[];
15
+ /**
16
+ * Get EColors
17
+ * @param init Initial color
18
+ * @param factor Increase factor
19
+ * @param adjustOrder Adjust order to increase difference
20
+ * @returns Result
21
+ */
22
+ function getEColors(init?: string, factor?: number, adjustOrder?: boolean): EColor[];
23
+ }
@@ -0,0 +1,80 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.ColorUtils = void 0;
4
+ const EColor_1 = require("./types/EColor");
5
+ /**
6
+ * Color utils
7
+ */
8
+ var ColorUtils;
9
+ (function (ColorUtils) {
10
+ /**
11
+ * Get HEX or RGB colors
12
+ * @param init Initial color
13
+ * @param factor Increase factor
14
+ * @param adjustOrder Adjust order to increase difference
15
+ * @param hex to HEX or not
16
+ * @returns Result
17
+ */
18
+ function getColors(init = '#000', factor = 51, adjustOrder = true, hex = true) {
19
+ return getEColors(init, factor, adjustOrder).map((c) => hex ? c.toHEXColor() : c.toRGBColor());
20
+ }
21
+ ColorUtils.getColors = getColors;
22
+ /**
23
+ * Get EColors
24
+ * @param init Initial color
25
+ * @param factor Increase factor
26
+ * @param adjustOrder Adjust order to increase difference
27
+ * @returns Result
28
+ */
29
+ function getEColors(init = '#000', factor = 51, adjustOrder = true) {
30
+ var _a;
31
+ // Init color
32
+ const initColor = (_a = EColor_1.EColor.parse(init)) !== null && _a !== void 0 ? _a : new EColor_1.EColor(0, 0, 0);
33
+ // Factors elements
34
+ // 51 = '00', '33', '66', '99', 'cc', 'ff'
35
+ const factors = [];
36
+ let f = 0;
37
+ while (f <= 255) {
38
+ factors.push(f);
39
+ f += factor;
40
+ }
41
+ // RGB loop
42
+ const colors = [initColor];
43
+ for (const r of factors) {
44
+ for (const g of factors) {
45
+ for (const b of factors) {
46
+ colors.push(initColor.clone(r, g, b));
47
+ }
48
+ }
49
+ }
50
+ // Non-nullable colors
51
+ const nColors = colors.filter((color) => color != null);
52
+ // Adjust order
53
+ if (adjustOrder) {
54
+ const firstColor = nColors.shift();
55
+ if (firstColor) {
56
+ let color = firstColor;
57
+ const newColors = [color];
58
+ while (nColors.length > 0) {
59
+ const result = nColors.reduce((p, c, index) => {
60
+ const delta = color.getDeltaValue(c);
61
+ if (delta != null && delta > p.delta) {
62
+ p.delta = delta;
63
+ p.color = c;
64
+ p.index = index;
65
+ }
66
+ return p;
67
+ }, { delta: 0, color, index: -1 });
68
+ if (result.delta > 0) {
69
+ color = result.color;
70
+ newColors.push(color);
71
+ nColors.splice(result.index, 1);
72
+ }
73
+ }
74
+ return newColors;
75
+ }
76
+ }
77
+ return nColors;
78
+ }
79
+ ColorUtils.getEColors = getEColors;
80
+ })(ColorUtils = exports.ColorUtils || (exports.ColorUtils = {}));
@@ -4,6 +4,7 @@ export * from './types/FormData';
4
4
  export * from './storage/IStorage';
5
5
  export * from './storage/WindowStorage';
6
6
  export * from './DataTypes';
7
+ export * from './ColorUtils';
7
8
  export * from './DateUtils';
8
9
  export * from './DomUtils';
9
10
  export * from './ExtendUtils';
package/lib/cjs/index.js CHANGED
@@ -20,6 +20,7 @@ __exportStar(require("./types/FormData"), exports);
20
20
  __exportStar(require("./storage/IStorage"), exports);
21
21
  __exportStar(require("./storage/WindowStorage"), exports);
22
22
  __exportStar(require("./DataTypes"), exports);
23
+ __exportStar(require("./ColorUtils"), exports);
23
24
  __exportStar(require("./DateUtils"), exports);
24
25
  __exportStar(require("./DomUtils"), exports);
25
26
  __exportStar(require("./ExtendUtils"), exports);
@@ -13,21 +13,6 @@ export declare class EColor {
13
13
  * @returns Adjusted value
14
14
  */
15
15
  static adjust(value: number, adjust?: number): number;
16
- /**
17
- * Get HEX or RGB colors
18
- * @param init Initial color
19
- * @param factor Increase factor
20
- * @param hex to HEX or not
21
- * @returns Result
22
- */
23
- static getColors(init?: string, factor?: number, hex?: boolean): string[];
24
- /**
25
- * Get EColors
26
- * @param init Initial color
27
- * @param factor Increase factor
28
- * @returns Result
29
- */
30
- static getEColors(init?: string, factor?: number): EColor[];
31
16
  /**
32
17
  * HEX string to integer value
33
18
  * @param hex HEX string
@@ -55,7 +40,7 @@ export declare class EColor {
55
40
  */
56
41
  constructor(r: number, g: number, b: number, alpha?: number | undefined);
57
42
  /**
58
- * Clone color with adjusts
43
+ * Clone color with adjustments
59
44
  * @param adjustR Adjust R value
60
45
  * @param adjustG Adjust G value
61
46
  * @param adjustB Adjust B value
@@ -63,14 +48,41 @@ export declare class EColor {
63
48
  */
64
49
  clone(adjustR?: number, adjustG?: number, adjustB?: number, alpha?: number): EColor | undefined;
65
50
  /**
66
- * To RGB color string
67
- * @param includeAlpha Include alpha or not
68
- * @returns RGB color string
51
+ * Get contrast ratio, a value between 0 and 1
52
+ * @param color Contrast color
53
+ */
54
+ getContrastRatio(color: EColor): number;
55
+ /**
56
+ * Get Delta value (perceptible by human eyes)
57
+ * <= 1, Not perceptible by human eyes
58
+ * 1 - 2, Perceptible through close observation
59
+ * 2 - 10, Perceptible at a glance
60
+ * 11 - 49, Colors are more similar than opposite
61
+ * 100+, Colors are exact opposite
62
+ * @param color Contrast color
63
+ * @returns Value
64
+ */
65
+ getDeltaValue(color: EColor): number;
66
+ /**
67
+ * Get luminance
68
+ * Darker one has higher luminance
69
+ * https://dev.to/alvaromontoro/building-your-own-color-contrast-checker-4j7o
69
70
  */
70
- toRGBColor(includeAlpha?: boolean): string;
71
+ getLuminance(): number;
71
72
  /**
72
73
  * To HEX color string
73
74
  * @returns HEX color string
74
75
  */
75
76
  toHEXColor(): string;
77
+ /**
78
+ * To Lab value
79
+ * @returns Lab value
80
+ */
81
+ toLabValue(): [number, number, number];
82
+ /**
83
+ * To RGB color string
84
+ * @param alpha Alpha value, false means ignore it
85
+ * @returns RGB color string
86
+ */
87
+ toRGBColor(alpha?: boolean | number): string;
76
88
  }
@@ -32,47 +32,6 @@ class EColor {
32
32
  return value % 255;
33
33
  return value;
34
34
  }
35
- /**
36
- * Get HEX or RGB colors
37
- * @param init Initial color
38
- * @param factor Increase factor
39
- * @param hex to HEX or not
40
- * @returns Result
41
- */
42
- static getColors(init = '#000', factor = 51, hex = true) {
43
- return EColor.getEColors(init, factor).map((c) => hex ? c.toHEXColor() : c.toRGBColor());
44
- }
45
- /**
46
- * Get EColors
47
- * @param init Initial color
48
- * @param factor Increase factor
49
- * @returns Result
50
- */
51
- static getEColors(init = '#000', factor = 51) {
52
- var _a;
53
- // Init color
54
- const initColor = (_a = EColor.parse(init)) !== null && _a !== void 0 ? _a : new EColor(0, 0, 0);
55
- // Factors elements
56
- // 51 = '00', '33', '66', '99', 'cc', 'ff'
57
- const factors = [];
58
- let f = 0;
59
- while (f <= 255) {
60
- factors.push(f);
61
- f += factor;
62
- }
63
- // RGB loop
64
- const colors = [initColor];
65
- for (const r of factors) {
66
- for (const g of factors) {
67
- for (const b of factors) {
68
- const newColor = initColor.clone(r, g, b);
69
- if (newColor)
70
- colors.push(newColor);
71
- }
72
- }
73
- }
74
- return colors;
75
- }
76
35
  /**
77
36
  * HEX string to integer value
78
37
  * @param hex HEX string
@@ -98,7 +57,7 @@ class EColor {
98
57
  // Null
99
58
  if (htmlColor == null)
100
59
  return undefined;
101
- htmlColor = htmlColor.toUpperCase();
60
+ htmlColor = htmlColor.trim().toUpperCase();
102
61
  // HEX color
103
62
  if (htmlColor.startsWith('#')) {
104
63
  htmlColor = htmlColor.substring(1);
@@ -124,7 +83,7 @@ class EColor {
124
83
  return undefined;
125
84
  }
126
85
  /**
127
- * Clone color with adjusts
86
+ * Clone color with adjustments
128
87
  * @param adjustR Adjust R value
129
88
  * @param adjustG Adjust G value
130
89
  * @param adjustB Adjust B value
@@ -142,17 +101,60 @@ class EColor {
142
101
  return new EColor(r, g, b, alpha);
143
102
  }
144
103
  /**
145
- * To RGB color string
146
- * @param includeAlpha Include alpha or not
147
- * @returns RGB color string
104
+ * Get contrast ratio, a value between 0 and 1
105
+ * @param color Contrast color
148
106
  */
149
- toRGBColor(includeAlpha) {
150
- var _a;
151
- // Default case
152
- includeAlpha !== null && includeAlpha !== void 0 ? includeAlpha : (includeAlpha = this.alpha != null);
153
- if (includeAlpha)
154
- return `RGBA(${this.r}, ${this.g}, ${this.b}, ${(_a = this.alpha) !== null && _a !== void 0 ? _a : 1})`;
155
- return `RGB(${this.r}, ${this.g}, ${this.b})`;
107
+ getContrastRatio(color) {
108
+ const lum1 = this.getLuminance();
109
+ const lum2 = color.getLuminance();
110
+ const brightest = Math.max(lum1, lum2);
111
+ const darkest = Math.min(lum1, lum2);
112
+ return (brightest + 0.05) / (darkest + 0.05);
113
+ }
114
+ /**
115
+ * Get Delta value (perceptible by human eyes)
116
+ * <= 1, Not perceptible by human eyes
117
+ * 1 - 2, Perceptible through close observation
118
+ * 2 - 10, Perceptible at a glance
119
+ * 11 - 49, Colors are more similar than opposite
120
+ * 100+, Colors are exact opposite
121
+ * @param color Contrast color
122
+ * @returns Value
123
+ */
124
+ getDeltaValue(color) {
125
+ const labA = this.toLabValue();
126
+ const labB = color.toLabValue();
127
+ const deltaL = labA[0] - labB[0];
128
+ const deltaA = labA[1] - labB[1];
129
+ const deltaB = labA[2] - labB[2];
130
+ const c1 = Math.sqrt(labA[1] * labA[1] + labA[2] * labA[2]);
131
+ const c2 = Math.sqrt(labB[1] * labB[1] + labB[2] * labB[2]);
132
+ const deltaC = c1 - c2;
133
+ let deltaH = deltaA * deltaA + deltaB * deltaB - deltaC * deltaC;
134
+ deltaH = deltaH < 0 ? 0 : Math.sqrt(deltaH);
135
+ const sc = 1.0 + 0.045 * c1;
136
+ const sh = 1.0 + 0.015 * c1;
137
+ const deltaLKlsl = deltaL / 1.0;
138
+ const deltaCkcsc = deltaC / sc;
139
+ const deltaHkhsh = deltaH / sh;
140
+ const i = deltaLKlsl * deltaLKlsl +
141
+ deltaCkcsc * deltaCkcsc +
142
+ deltaHkhsh * deltaHkhsh;
143
+ return i < 0 ? 0 : Math.sqrt(i);
144
+ }
145
+ /**
146
+ * Get luminance
147
+ * Darker one has higher luminance
148
+ * https://dev.to/alvaromontoro/building-your-own-color-contrast-checker-4j7o
149
+ */
150
+ getLuminance() {
151
+ const a = [this.r, this.g, this.b].map((v) => {
152
+ v /= 255;
153
+ return v <= 0.03928
154
+ ? v / 12.92
155
+ : Math.pow((v + 0.055) / 1.055, 2.4);
156
+ });
157
+ return a[0] * 0.2126 + a[1] * 0.7152 + a[2] * 0.0722;
156
158
  }
157
159
  /**
158
160
  * To HEX color string
@@ -161,5 +163,44 @@ class EColor {
161
163
  toHEXColor() {
162
164
  return `#${EColor.toHex(this.r)}${EColor.toHex(this.g)}${EColor.toHex(this.b)}`;
163
165
  }
166
+ /**
167
+ * To Lab value
168
+ * @returns Lab value
169
+ */
170
+ toLabValue() {
171
+ let r = this.r / 255, g = this.g / 255, b = this.b / 255, x, y, z;
172
+ r = r > 0.04045 ? Math.pow((r + 0.055) / 1.055, 2.4) : r / 12.92;
173
+ g = g > 0.04045 ? Math.pow((g + 0.055) / 1.055, 2.4) : g / 12.92;
174
+ b = b > 0.04045 ? Math.pow((b + 0.055) / 1.055, 2.4) : b / 12.92;
175
+ x = (r * 0.4124 + g * 0.3576 + b * 0.1805) / 0.95047;
176
+ y = (r * 0.2126 + g * 0.7152 + b * 0.0722) / 1.0;
177
+ z = (r * 0.0193 + g * 0.1192 + b * 0.9505) / 1.08883;
178
+ x = x > 0.008856 ? Math.pow(x, 1 / 3) : 7.787 * x + 16 / 116;
179
+ y = y > 0.008856 ? Math.pow(y, 1 / 3) : 7.787 * y + 16 / 116;
180
+ z = z > 0.008856 ? Math.pow(z, 1 / 3) : 7.787 * z + 16 / 116;
181
+ return [116 * y - 16, 500 * (x - y), 200 * (y - z)];
182
+ }
183
+ /**
184
+ * To RGB color string
185
+ * @param alpha Alpha value, false means ignore it
186
+ * @returns RGB color string
187
+ */
188
+ toRGBColor(alpha) {
189
+ // Decide
190
+ let includeAlpha, alphaValue = this.alpha;
191
+ if (typeof alpha === 'number') {
192
+ alphaValue = alpha;
193
+ includeAlpha = true;
194
+ }
195
+ else if (alpha == null) {
196
+ includeAlpha = this.alpha != null;
197
+ }
198
+ else {
199
+ includeAlpha = alpha;
200
+ }
201
+ if (includeAlpha)
202
+ return `RGBA(${this.r}, ${this.g}, ${this.b}, ${alphaValue !== null && alphaValue !== void 0 ? alphaValue : 1})`;
203
+ return `RGB(${this.r}, ${this.g}, ${this.b})`;
204
+ }
164
205
  }
165
206
  exports.EColor = EColor;
@@ -0,0 +1,23 @@
1
+ import { EColor } from './types/EColor';
2
+ /**
3
+ * Color utils
4
+ */
5
+ export declare namespace ColorUtils {
6
+ /**
7
+ * Get HEX or RGB colors
8
+ * @param init Initial color
9
+ * @param factor Increase factor
10
+ * @param adjustOrder Adjust order to increase difference
11
+ * @param hex to HEX or not
12
+ * @returns Result
13
+ */
14
+ function getColors(init?: string, factor?: number, adjustOrder?: boolean, hex?: boolean): string[];
15
+ /**
16
+ * Get EColors
17
+ * @param init Initial color
18
+ * @param factor Increase factor
19
+ * @param adjustOrder Adjust order to increase difference
20
+ * @returns Result
21
+ */
22
+ function getEColors(init?: string, factor?: number, adjustOrder?: boolean): EColor[];
23
+ }
@@ -0,0 +1,77 @@
1
+ import { EColor } from './types/EColor';
2
+ /**
3
+ * Color utils
4
+ */
5
+ export var ColorUtils;
6
+ (function (ColorUtils) {
7
+ /**
8
+ * Get HEX or RGB colors
9
+ * @param init Initial color
10
+ * @param factor Increase factor
11
+ * @param adjustOrder Adjust order to increase difference
12
+ * @param hex to HEX or not
13
+ * @returns Result
14
+ */
15
+ function getColors(init = '#000', factor = 51, adjustOrder = true, hex = true) {
16
+ return getEColors(init, factor, adjustOrder).map((c) => hex ? c.toHEXColor() : c.toRGBColor());
17
+ }
18
+ ColorUtils.getColors = getColors;
19
+ /**
20
+ * Get EColors
21
+ * @param init Initial color
22
+ * @param factor Increase factor
23
+ * @param adjustOrder Adjust order to increase difference
24
+ * @returns Result
25
+ */
26
+ function getEColors(init = '#000', factor = 51, adjustOrder = true) {
27
+ var _a;
28
+ // Init color
29
+ const initColor = (_a = EColor.parse(init)) !== null && _a !== void 0 ? _a : new EColor(0, 0, 0);
30
+ // Factors elements
31
+ // 51 = '00', '33', '66', '99', 'cc', 'ff'
32
+ const factors = [];
33
+ let f = 0;
34
+ while (f <= 255) {
35
+ factors.push(f);
36
+ f += factor;
37
+ }
38
+ // RGB loop
39
+ const colors = [initColor];
40
+ for (const r of factors) {
41
+ for (const g of factors) {
42
+ for (const b of factors) {
43
+ colors.push(initColor.clone(r, g, b));
44
+ }
45
+ }
46
+ }
47
+ // Non-nullable colors
48
+ const nColors = colors.filter((color) => color != null);
49
+ // Adjust order
50
+ if (adjustOrder) {
51
+ const firstColor = nColors.shift();
52
+ if (firstColor) {
53
+ let color = firstColor;
54
+ const newColors = [color];
55
+ while (nColors.length > 0) {
56
+ const result = nColors.reduce((p, c, index) => {
57
+ const delta = color.getDeltaValue(c);
58
+ if (delta != null && delta > p.delta) {
59
+ p.delta = delta;
60
+ p.color = c;
61
+ p.index = index;
62
+ }
63
+ return p;
64
+ }, { delta: 0, color, index: -1 });
65
+ if (result.delta > 0) {
66
+ color = result.color;
67
+ newColors.push(color);
68
+ nColors.splice(result.index, 1);
69
+ }
70
+ }
71
+ return newColors;
72
+ }
73
+ }
74
+ return nColors;
75
+ }
76
+ ColorUtils.getEColors = getEColors;
77
+ })(ColorUtils || (ColorUtils = {}));
@@ -4,6 +4,7 @@ export * from './types/FormData';
4
4
  export * from './storage/IStorage';
5
5
  export * from './storage/WindowStorage';
6
6
  export * from './DataTypes';
7
+ export * from './ColorUtils';
7
8
  export * from './DateUtils';
8
9
  export * from './DomUtils';
9
10
  export * from './ExtendUtils';
package/lib/mjs/index.js CHANGED
@@ -4,6 +4,7 @@ export * from './types/FormData';
4
4
  export * from './storage/IStorage';
5
5
  export * from './storage/WindowStorage';
6
6
  export * from './DataTypes';
7
+ export * from './ColorUtils';
7
8
  export * from './DateUtils';
8
9
  export * from './DomUtils';
9
10
  export * from './ExtendUtils';
@@ -13,21 +13,6 @@ export declare class EColor {
13
13
  * @returns Adjusted value
14
14
  */
15
15
  static adjust(value: number, adjust?: number): number;
16
- /**
17
- * Get HEX or RGB colors
18
- * @param init Initial color
19
- * @param factor Increase factor
20
- * @param hex to HEX or not
21
- * @returns Result
22
- */
23
- static getColors(init?: string, factor?: number, hex?: boolean): string[];
24
- /**
25
- * Get EColors
26
- * @param init Initial color
27
- * @param factor Increase factor
28
- * @returns Result
29
- */
30
- static getEColors(init?: string, factor?: number): EColor[];
31
16
  /**
32
17
  * HEX string to integer value
33
18
  * @param hex HEX string
@@ -55,7 +40,7 @@ export declare class EColor {
55
40
  */
56
41
  constructor(r: number, g: number, b: number, alpha?: number | undefined);
57
42
  /**
58
- * Clone color with adjusts
43
+ * Clone color with adjustments
59
44
  * @param adjustR Adjust R value
60
45
  * @param adjustG Adjust G value
61
46
  * @param adjustB Adjust B value
@@ -63,14 +48,41 @@ export declare class EColor {
63
48
  */
64
49
  clone(adjustR?: number, adjustG?: number, adjustB?: number, alpha?: number): EColor | undefined;
65
50
  /**
66
- * To RGB color string
67
- * @param includeAlpha Include alpha or not
68
- * @returns RGB color string
51
+ * Get contrast ratio, a value between 0 and 1
52
+ * @param color Contrast color
53
+ */
54
+ getContrastRatio(color: EColor): number;
55
+ /**
56
+ * Get Delta value (perceptible by human eyes)
57
+ * <= 1, Not perceptible by human eyes
58
+ * 1 - 2, Perceptible through close observation
59
+ * 2 - 10, Perceptible at a glance
60
+ * 11 - 49, Colors are more similar than opposite
61
+ * 100+, Colors are exact opposite
62
+ * @param color Contrast color
63
+ * @returns Value
64
+ */
65
+ getDeltaValue(color: EColor): number;
66
+ /**
67
+ * Get luminance
68
+ * Darker one has higher luminance
69
+ * https://dev.to/alvaromontoro/building-your-own-color-contrast-checker-4j7o
69
70
  */
70
- toRGBColor(includeAlpha?: boolean): string;
71
+ getLuminance(): number;
71
72
  /**
72
73
  * To HEX color string
73
74
  * @returns HEX color string
74
75
  */
75
76
  toHEXColor(): string;
77
+ /**
78
+ * To Lab value
79
+ * @returns Lab value
80
+ */
81
+ toLabValue(): [number, number, number];
82
+ /**
83
+ * To RGB color string
84
+ * @param alpha Alpha value, false means ignore it
85
+ * @returns RGB color string
86
+ */
87
+ toRGBColor(alpha?: boolean | number): string;
76
88
  }
@@ -29,47 +29,6 @@ export class EColor {
29
29
  return value % 255;
30
30
  return value;
31
31
  }
32
- /**
33
- * Get HEX or RGB colors
34
- * @param init Initial color
35
- * @param factor Increase factor
36
- * @param hex to HEX or not
37
- * @returns Result
38
- */
39
- static getColors(init = '#000', factor = 51, hex = true) {
40
- return EColor.getEColors(init, factor).map((c) => hex ? c.toHEXColor() : c.toRGBColor());
41
- }
42
- /**
43
- * Get EColors
44
- * @param init Initial color
45
- * @param factor Increase factor
46
- * @returns Result
47
- */
48
- static getEColors(init = '#000', factor = 51) {
49
- var _a;
50
- // Init color
51
- const initColor = (_a = EColor.parse(init)) !== null && _a !== void 0 ? _a : new EColor(0, 0, 0);
52
- // Factors elements
53
- // 51 = '00', '33', '66', '99', 'cc', 'ff'
54
- const factors = [];
55
- let f = 0;
56
- while (f <= 255) {
57
- factors.push(f);
58
- f += factor;
59
- }
60
- // RGB loop
61
- const colors = [initColor];
62
- for (const r of factors) {
63
- for (const g of factors) {
64
- for (const b of factors) {
65
- const newColor = initColor.clone(r, g, b);
66
- if (newColor)
67
- colors.push(newColor);
68
- }
69
- }
70
- }
71
- return colors;
72
- }
73
32
  /**
74
33
  * HEX string to integer value
75
34
  * @param hex HEX string
@@ -95,7 +54,7 @@ export class EColor {
95
54
  // Null
96
55
  if (htmlColor == null)
97
56
  return undefined;
98
- htmlColor = htmlColor.toUpperCase();
57
+ htmlColor = htmlColor.trim().toUpperCase();
99
58
  // HEX color
100
59
  if (htmlColor.startsWith('#')) {
101
60
  htmlColor = htmlColor.substring(1);
@@ -121,7 +80,7 @@ export class EColor {
121
80
  return undefined;
122
81
  }
123
82
  /**
124
- * Clone color with adjusts
83
+ * Clone color with adjustments
125
84
  * @param adjustR Adjust R value
126
85
  * @param adjustG Adjust G value
127
86
  * @param adjustB Adjust B value
@@ -139,17 +98,60 @@ export class EColor {
139
98
  return new EColor(r, g, b, alpha);
140
99
  }
141
100
  /**
142
- * To RGB color string
143
- * @param includeAlpha Include alpha or not
144
- * @returns RGB color string
101
+ * Get contrast ratio, a value between 0 and 1
102
+ * @param color Contrast color
145
103
  */
146
- toRGBColor(includeAlpha) {
147
- var _a;
148
- // Default case
149
- includeAlpha !== null && includeAlpha !== void 0 ? includeAlpha : (includeAlpha = this.alpha != null);
150
- if (includeAlpha)
151
- return `RGBA(${this.r}, ${this.g}, ${this.b}, ${(_a = this.alpha) !== null && _a !== void 0 ? _a : 1})`;
152
- return `RGB(${this.r}, ${this.g}, ${this.b})`;
104
+ getContrastRatio(color) {
105
+ const lum1 = this.getLuminance();
106
+ const lum2 = color.getLuminance();
107
+ const brightest = Math.max(lum1, lum2);
108
+ const darkest = Math.min(lum1, lum2);
109
+ return (brightest + 0.05) / (darkest + 0.05);
110
+ }
111
+ /**
112
+ * Get Delta value (perceptible by human eyes)
113
+ * <= 1, Not perceptible by human eyes
114
+ * 1 - 2, Perceptible through close observation
115
+ * 2 - 10, Perceptible at a glance
116
+ * 11 - 49, Colors are more similar than opposite
117
+ * 100+, Colors are exact opposite
118
+ * @param color Contrast color
119
+ * @returns Value
120
+ */
121
+ getDeltaValue(color) {
122
+ const labA = this.toLabValue();
123
+ const labB = color.toLabValue();
124
+ const deltaL = labA[0] - labB[0];
125
+ const deltaA = labA[1] - labB[1];
126
+ const deltaB = labA[2] - labB[2];
127
+ const c1 = Math.sqrt(labA[1] * labA[1] + labA[2] * labA[2]);
128
+ const c2 = Math.sqrt(labB[1] * labB[1] + labB[2] * labB[2]);
129
+ const deltaC = c1 - c2;
130
+ let deltaH = deltaA * deltaA + deltaB * deltaB - deltaC * deltaC;
131
+ deltaH = deltaH < 0 ? 0 : Math.sqrt(deltaH);
132
+ const sc = 1.0 + 0.045 * c1;
133
+ const sh = 1.0 + 0.015 * c1;
134
+ const deltaLKlsl = deltaL / 1.0;
135
+ const deltaCkcsc = deltaC / sc;
136
+ const deltaHkhsh = deltaH / sh;
137
+ const i = deltaLKlsl * deltaLKlsl +
138
+ deltaCkcsc * deltaCkcsc +
139
+ deltaHkhsh * deltaHkhsh;
140
+ return i < 0 ? 0 : Math.sqrt(i);
141
+ }
142
+ /**
143
+ * Get luminance
144
+ * Darker one has higher luminance
145
+ * https://dev.to/alvaromontoro/building-your-own-color-contrast-checker-4j7o
146
+ */
147
+ getLuminance() {
148
+ const a = [this.r, this.g, this.b].map((v) => {
149
+ v /= 255;
150
+ return v <= 0.03928
151
+ ? v / 12.92
152
+ : Math.pow((v + 0.055) / 1.055, 2.4);
153
+ });
154
+ return a[0] * 0.2126 + a[1] * 0.7152 + a[2] * 0.0722;
153
155
  }
154
156
  /**
155
157
  * To HEX color string
@@ -158,4 +160,43 @@ export class EColor {
158
160
  toHEXColor() {
159
161
  return `#${EColor.toHex(this.r)}${EColor.toHex(this.g)}${EColor.toHex(this.b)}`;
160
162
  }
163
+ /**
164
+ * To Lab value
165
+ * @returns Lab value
166
+ */
167
+ toLabValue() {
168
+ let r = this.r / 255, g = this.g / 255, b = this.b / 255, x, y, z;
169
+ r = r > 0.04045 ? Math.pow((r + 0.055) / 1.055, 2.4) : r / 12.92;
170
+ g = g > 0.04045 ? Math.pow((g + 0.055) / 1.055, 2.4) : g / 12.92;
171
+ b = b > 0.04045 ? Math.pow((b + 0.055) / 1.055, 2.4) : b / 12.92;
172
+ x = (r * 0.4124 + g * 0.3576 + b * 0.1805) / 0.95047;
173
+ y = (r * 0.2126 + g * 0.7152 + b * 0.0722) / 1.0;
174
+ z = (r * 0.0193 + g * 0.1192 + b * 0.9505) / 1.08883;
175
+ x = x > 0.008856 ? Math.pow(x, 1 / 3) : 7.787 * x + 16 / 116;
176
+ y = y > 0.008856 ? Math.pow(y, 1 / 3) : 7.787 * y + 16 / 116;
177
+ z = z > 0.008856 ? Math.pow(z, 1 / 3) : 7.787 * z + 16 / 116;
178
+ return [116 * y - 16, 500 * (x - y), 200 * (y - z)];
179
+ }
180
+ /**
181
+ * To RGB color string
182
+ * @param alpha Alpha value, false means ignore it
183
+ * @returns RGB color string
184
+ */
185
+ toRGBColor(alpha) {
186
+ // Decide
187
+ let includeAlpha, alphaValue = this.alpha;
188
+ if (typeof alpha === 'number') {
189
+ alphaValue = alpha;
190
+ includeAlpha = true;
191
+ }
192
+ else if (alpha == null) {
193
+ includeAlpha = this.alpha != null;
194
+ }
195
+ else {
196
+ includeAlpha = alpha;
197
+ }
198
+ if (includeAlpha)
199
+ return `RGBA(${this.r}, ${this.g}, ${this.b}, ${alphaValue !== null && alphaValue !== void 0 ? alphaValue : 1})`;
200
+ return `RGB(${this.r}, ${this.g}, ${this.b})`;
201
+ }
161
202
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@etsoo/shared",
3
- "version": "1.1.15",
3
+ "version": "1.1.18",
4
4
  "description": "TypeScript shared utilities and functions",
5
5
  "main": "lib/cjs/index.js",
6
6
  "module": "lib/mjs/index.js",
@@ -55,13 +55,13 @@
55
55
  "dependencies": {},
56
56
  "devDependencies": {
57
57
  "@types/jest": "^27.4.1",
58
- "@typescript-eslint/eslint-plugin": "^5.13.0",
59
- "@typescript-eslint/parser": "^5.13.0",
60
- "eslint": "^8.10.0",
58
+ "@typescript-eslint/eslint-plugin": "^5.16.0",
59
+ "@typescript-eslint/parser": "^5.16.0",
60
+ "eslint": "^8.11.0",
61
61
  "eslint-config-airbnb-base": "^15.0.0",
62
62
  "eslint-plugin-import": "^2.25.4",
63
63
  "jest": "^27.5.1",
64
- "ts-jest": "^27.1.3",
65
- "typescript": "^4.6.2"
64
+ "ts-jest": "^27.1.4",
65
+ "typescript": "^4.6.3"
66
66
  }
67
67
  }
@@ -0,0 +1,99 @@
1
+ import { EColor } from './types/EColor';
2
+
3
+ /**
4
+ * Color utils
5
+ */
6
+ export namespace ColorUtils {
7
+ /**
8
+ * Get HEX or RGB colors
9
+ * @param init Initial color
10
+ * @param factor Increase factor
11
+ * @param adjustOrder Adjust order to increase difference
12
+ * @param hex to HEX or not
13
+ * @returns Result
14
+ */
15
+ export function getColors(
16
+ init: string = '#000',
17
+ factor: number = 51,
18
+ adjustOrder: boolean = true,
19
+ hex: boolean = true
20
+ ) {
21
+ return getEColors(init, factor, adjustOrder).map((c) =>
22
+ hex ? c.toHEXColor() : c.toRGBColor()
23
+ );
24
+ }
25
+
26
+ /**
27
+ * Get EColors
28
+ * @param init Initial color
29
+ * @param factor Increase factor
30
+ * @param adjustOrder Adjust order to increase difference
31
+ * @returns Result
32
+ */
33
+ export function getEColors(
34
+ init: string = '#000',
35
+ factor: number = 51,
36
+ adjustOrder: boolean = true
37
+ ): EColor[] {
38
+ // Init color
39
+ const initColor = EColor.parse(init) ?? new EColor(0, 0, 0);
40
+
41
+ // Factors elements
42
+ // 51 = '00', '33', '66', '99', 'cc', 'ff'
43
+ const factors: number[] = [];
44
+ let f = 0;
45
+ while (f <= 255) {
46
+ factors.push(f);
47
+ f += factor;
48
+ }
49
+
50
+ // RGB loop
51
+ const colors: (EColor | undefined)[] = [initColor];
52
+ for (const r of factors) {
53
+ for (const g of factors) {
54
+ for (const b of factors) {
55
+ colors.push(initColor.clone(r, g, b));
56
+ }
57
+ }
58
+ }
59
+
60
+ // Non-nullable colors
61
+ const nColors = colors.filter(
62
+ (color): color is EColor => color != null
63
+ );
64
+
65
+ // Adjust order
66
+ if (adjustOrder) {
67
+ const firstColor = nColors.shift();
68
+ if (firstColor) {
69
+ let color = firstColor;
70
+ const newColors: EColor[] = [color];
71
+
72
+ while (nColors.length > 0) {
73
+ const result = nColors.reduce(
74
+ (p, c, index) => {
75
+ const delta = color.getDeltaValue(c);
76
+ if (delta != null && delta > p.delta) {
77
+ p.delta = delta;
78
+ p.color = c;
79
+ p.index = index;
80
+ }
81
+ return p;
82
+ },
83
+ { delta: 0, color, index: -1 }
84
+ );
85
+
86
+ if (result.delta > 0) {
87
+ color = result.color;
88
+ newColors.push(color);
89
+ nColors.splice(result.index, 1);
90
+ }
91
+ }
92
+
93
+ return newColors;
94
+ }
95
+ }
96
+
97
+ return nColors;
98
+ }
99
+ }
package/src/index.ts CHANGED
@@ -6,6 +6,7 @@ export * from './storage/IStorage';
6
6
  export * from './storage/WindowStorage';
7
7
 
8
8
  export * from './DataTypes';
9
+ export * from './ColorUtils';
9
10
  export * from './DateUtils';
10
11
  export * from './DomUtils';
11
12
  export * from './ExtendUtils';
@@ -15,53 +15,6 @@ export class EColor {
15
15
  return value;
16
16
  }
17
17
 
18
- /**
19
- * Get HEX or RGB colors
20
- * @param init Initial color
21
- * @param factor Increase factor
22
- * @param hex to HEX or not
23
- * @returns Result
24
- */
25
- static getColors(init = '#000', factor: number = 51, hex: boolean = true) {
26
- return EColor.getEColors(init, factor).map((c) =>
27
- hex ? c.toHEXColor() : c.toRGBColor()
28
- );
29
- }
30
-
31
- /**
32
- * Get EColors
33
- * @param init Initial color
34
- * @param factor Increase factor
35
- * @returns Result
36
- */
37
- static getEColors(init = '#000', factor: number = 51): EColor[] {
38
- // Init color
39
- const initColor = EColor.parse(init) ?? new EColor(0, 0, 0);
40
-
41
- // Factors elements
42
- // 51 = '00', '33', '66', '99', 'cc', 'ff'
43
- const factors: number[] = [];
44
- let f = 0;
45
- while (f <= 255) {
46
- factors.push(f);
47
- f += factor;
48
- }
49
-
50
- // RGB loop
51
- const colors: EColor[] = [initColor];
52
-
53
- for (const r of factors) {
54
- for (const g of factors) {
55
- for (const b of factors) {
56
- const newColor = initColor.clone(r, g, b);
57
- if (newColor) colors.push(newColor);
58
- }
59
- }
60
- }
61
-
62
- return colors;
63
- }
64
-
65
18
  /**
66
19
  * HEX string to integer value
67
20
  * @param hex HEX string
@@ -88,7 +41,7 @@ export class EColor {
88
41
  static parse(htmlColor?: string | null): EColor | undefined {
89
42
  // Null
90
43
  if (htmlColor == null) return undefined;
91
- htmlColor = htmlColor.toUpperCase();
44
+ htmlColor = htmlColor.trim().toUpperCase();
92
45
 
93
46
  // HEX color
94
47
  if (htmlColor.startsWith('#')) {
@@ -143,7 +96,7 @@ export class EColor {
143
96
  ) {}
144
97
 
145
98
  /**
146
- * Clone color with adjusts
99
+ * Clone color with adjustments
147
100
  * @param adjustR Adjust R value
148
101
  * @param adjustG Adjust G value
149
102
  * @param adjustB Adjust B value
@@ -169,18 +122,63 @@ export class EColor {
169
122
  }
170
123
 
171
124
  /**
172
- * To RGB color string
173
- * @param includeAlpha Include alpha or not
174
- * @returns RGB color string
125
+ * Get contrast ratio, a value between 0 and 1
126
+ * @param color Contrast color
175
127
  */
176
- toRGBColor(includeAlpha?: boolean) {
177
- // Default case
178
- includeAlpha ??= this.alpha != null;
128
+ getContrastRatio(color: EColor) {
129
+ const lum1 = this.getLuminance();
130
+ const lum2 = color.getLuminance();
131
+ const brightest = Math.max(lum1, lum2);
132
+ const darkest = Math.min(lum1, lum2);
133
+ return (brightest + 0.05) / (darkest + 0.05);
134
+ }
179
135
 
180
- if (includeAlpha)
181
- return `RGBA(${this.r}, ${this.g}, ${this.b}, ${this.alpha ?? 1})`;
136
+ /**
137
+ * Get Delta value (perceptible by human eyes)
138
+ * <= 1, Not perceptible by human eyes
139
+ * 1 - 2, Perceptible through close observation
140
+ * 2 - 10, Perceptible at a glance
141
+ * 11 - 49, Colors are more similar than opposite
142
+ * 100+, Colors are exact opposite
143
+ * @param color Contrast color
144
+ * @returns Value
145
+ */
146
+ getDeltaValue(color: EColor) {
147
+ const labA = this.toLabValue();
148
+ const labB = color.toLabValue();
149
+ const deltaL = labA[0] - labB[0];
150
+ const deltaA = labA[1] - labB[1];
151
+ const deltaB = labA[2] - labB[2];
152
+ const c1 = Math.sqrt(labA[1] * labA[1] + labA[2] * labA[2]);
153
+ const c2 = Math.sqrt(labB[1] * labB[1] + labB[2] * labB[2]);
154
+ const deltaC = c1 - c2;
155
+ let deltaH = deltaA * deltaA + deltaB * deltaB - deltaC * deltaC;
156
+ deltaH = deltaH < 0 ? 0 : Math.sqrt(deltaH);
157
+ const sc = 1.0 + 0.045 * c1;
158
+ const sh = 1.0 + 0.015 * c1;
159
+ const deltaLKlsl = deltaL / 1.0;
160
+ const deltaCkcsc = deltaC / sc;
161
+ const deltaHkhsh = deltaH / sh;
162
+ const i =
163
+ deltaLKlsl * deltaLKlsl +
164
+ deltaCkcsc * deltaCkcsc +
165
+ deltaHkhsh * deltaHkhsh;
166
+ return i < 0 ? 0 : Math.sqrt(i);
167
+ }
182
168
 
183
- return `RGB(${this.r}, ${this.g}, ${this.b})`;
169
+ /**
170
+ * Get luminance
171
+ * Darker one has higher luminance
172
+ * https://dev.to/alvaromontoro/building-your-own-color-contrast-checker-4j7o
173
+ */
174
+ getLuminance() {
175
+ const a = [this.r, this.g, this.b].map((v) => {
176
+ v /= 255;
177
+ return v <= 0.03928
178
+ ? v / 12.92
179
+ : Math.pow((v + 0.055) / 1.055, 2.4);
180
+ });
181
+ return a[0] * 0.2126 + a[1] * 0.7152 + a[2] * 0.0722;
184
182
  }
185
183
 
186
184
  /**
@@ -192,4 +190,52 @@ export class EColor {
192
190
  this.b
193
191
  )}`;
194
192
  }
193
+
194
+ /**
195
+ * To Lab value
196
+ * @returns Lab value
197
+ */
198
+ toLabValue(): [number, number, number] {
199
+ let r = this.r / 255,
200
+ g = this.g / 255,
201
+ b = this.b / 255,
202
+ x,
203
+ y,
204
+ z;
205
+ r = r > 0.04045 ? Math.pow((r + 0.055) / 1.055, 2.4) : r / 12.92;
206
+ g = g > 0.04045 ? Math.pow((g + 0.055) / 1.055, 2.4) : g / 12.92;
207
+ b = b > 0.04045 ? Math.pow((b + 0.055) / 1.055, 2.4) : b / 12.92;
208
+ x = (r * 0.4124 + g * 0.3576 + b * 0.1805) / 0.95047;
209
+ y = (r * 0.2126 + g * 0.7152 + b * 0.0722) / 1.0;
210
+ z = (r * 0.0193 + g * 0.1192 + b * 0.9505) / 1.08883;
211
+ x = x > 0.008856 ? Math.pow(x, 1 / 3) : 7.787 * x + 16 / 116;
212
+ y = y > 0.008856 ? Math.pow(y, 1 / 3) : 7.787 * y + 16 / 116;
213
+ z = z > 0.008856 ? Math.pow(z, 1 / 3) : 7.787 * z + 16 / 116;
214
+ return [116 * y - 16, 500 * (x - y), 200 * (y - z)];
215
+ }
216
+
217
+ /**
218
+ * To RGB color string
219
+ * @param alpha Alpha value, false means ignore it
220
+ * @returns RGB color string
221
+ */
222
+ toRGBColor(alpha?: boolean | number) {
223
+ // Decide
224
+ let includeAlpha: boolean,
225
+ alphaValue: number | undefined = this.alpha;
226
+
227
+ if (typeof alpha === 'number') {
228
+ alphaValue = alpha;
229
+ includeAlpha = true;
230
+ } else if (alpha == null) {
231
+ includeAlpha = this.alpha != null;
232
+ } else {
233
+ includeAlpha = alpha;
234
+ }
235
+
236
+ if (includeAlpha)
237
+ return `RGBA(${this.r}, ${this.g}, ${this.b}, ${alphaValue ?? 1})`;
238
+
239
+ return `RGB(${this.r}, ${this.g}, ${this.b})`;
240
+ }
195
241
  }