@oscarpalmer/atoms 0.39.0 → 0.40.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.
@@ -0,0 +1,185 @@
1
+ // src/js/number.ts
2
+ function clamp(value, min, max, loop) {
3
+ if (value < min) {
4
+ return loop === true ? max : min;
5
+ }
6
+ return value > max ? loop === true ? min : max : value;
7
+ }
8
+
9
+ // src/js/colour.ts
10
+ var createHex = function(original) {
11
+ let value = original.slice();
12
+ const instance = Object.create({
13
+ toHsl() {
14
+ return hexToRgb(value).toHsl();
15
+ },
16
+ toRgb() {
17
+ return hexToRgb(value);
18
+ },
19
+ toString() {
20
+ return value;
21
+ }
22
+ });
23
+ Object.defineProperty(instance, "value", {
24
+ get() {
25
+ return value.slice();
26
+ },
27
+ set(hex) {
28
+ if (anyPattern.test(hex)) {
29
+ value = `#${getHex(hex)}`;
30
+ }
31
+ }
32
+ });
33
+ return instance;
34
+ };
35
+ var createHsl = function(original) {
36
+ const value = { ...original };
37
+ const instance = Object.create({
38
+ toHex() {
39
+ return hslToRgb(value).toHex();
40
+ },
41
+ toRgb() {
42
+ return hslToRgb(value);
43
+ },
44
+ toString() {
45
+ return `hsl(${value.hue}, ${value.saturation}%, ${value.lightness}%)`;
46
+ }
47
+ });
48
+ Object.defineProperties(instance, {
49
+ hue: createProperty(value, "hue", 0, 360),
50
+ lightness: createProperty(value, "lightness", 0, 100),
51
+ saturation: createProperty(value, "saturation", 0, 100),
52
+ value: { value }
53
+ });
54
+ return instance;
55
+ };
56
+ var createProperty = function(store, key, min, max) {
57
+ return {
58
+ get() {
59
+ return store[key];
60
+ },
61
+ set(value) {
62
+ store[key] = clamp(value, min, max);
63
+ }
64
+ };
65
+ };
66
+ var createRgb = function(original) {
67
+ const value = { ...original };
68
+ const instance = Object.create({
69
+ toHex() {
70
+ return rgbToHex(value);
71
+ },
72
+ toHsl() {
73
+ return rgbToHsl(value);
74
+ },
75
+ toString() {
76
+ return `rgb(${value.red}, ${value.green}, ${value.blue})`;
77
+ }
78
+ });
79
+ Object.defineProperties(instance, {
80
+ blue: createProperty(value, "blue", 0, 255),
81
+ green: createProperty(value, "green", 0, 255),
82
+ red: createProperty(value, "red", 0, 255),
83
+ value: { value }
84
+ });
85
+ return instance;
86
+ };
87
+ function getForegroundColour(value) {
88
+ const values = [value.blue / 255, value.green / 255, value.red / 255];
89
+ for (let colour of values) {
90
+ if (colour <= 0.03928) {
91
+ colour /= 12.92;
92
+ } else {
93
+ colour = ((colour + 0.055) / 1.055) ** 2.4;
94
+ }
95
+ }
96
+ const luminance = 0.2126 * values[2] + 0.7152 * values[1] + 0.0722 * values[0];
97
+ return luminance > 0.625 ? "black" : "white";
98
+ }
99
+ var getHex = function(value) {
100
+ const normalised = value.replace(/^#/, "");
101
+ return normalised.length === 3 ? normalised.split("").map((character) => character.repeat(2)).join("") : normalised;
102
+ };
103
+ function hexToRgb(value) {
104
+ const hex = anyPattern.test(value) ? getHex(value) : "";
105
+ const pairs = groupedPattern.exec(hex) ?? [];
106
+ const rgb = [];
107
+ const { length } = pairs;
108
+ for (let index = 1;index < length; index += 1) {
109
+ rgb.push(Number.parseInt(pairs[index], 16));
110
+ }
111
+ return createRgb({ blue: rgb[2] ?? 0, green: rgb[1] ?? 0, red: rgb[0] ?? 0 });
112
+ }
113
+ function hslToRgb(value) {
114
+ let hue = value.hue % 360;
115
+ if (hue < 0) {
116
+ hue += 360;
117
+ }
118
+ const saturation = value.saturation / 100;
119
+ const lightness = value.lightness / 100;
120
+ function get(value2) {
121
+ const part = (value2 + hue / 30) % 12;
122
+ const mod = saturation * Math.min(lightness, 1 - lightness);
123
+ return lightness - mod * Math.max(-1, Math.min(part - 3, 9 - part, 1));
124
+ }
125
+ return createRgb({
126
+ blue: clamp(Math.round(get(4) * 255), 0, 255),
127
+ green: clamp(Math.round(get(8) * 255), 0, 255),
128
+ red: clamp(Math.round(get(0) * 255), 0, 255)
129
+ });
130
+ }
131
+ function rgbToHex(value) {
132
+ return createHex(`#${[value.red, value.green, value.blue].map((colour) => {
133
+ const hex = colour.toString(16);
134
+ return hex.length === 1 ? `0${hex}` : hex;
135
+ }).join("")}`);
136
+ }
137
+ function rgbToHsl(rgb) {
138
+ const blue = rgb.blue / 255;
139
+ const green = rgb.green / 255;
140
+ const red = rgb.red / 255;
141
+ const max = Math.max(blue, green, red);
142
+ const min = Math.min(blue, green, red);
143
+ const delta = max - min;
144
+ const lightness = (min + max) / 2;
145
+ let hue = 0;
146
+ let saturation = 0;
147
+ if (delta !== 0) {
148
+ saturation = lightness === 0 || lightness === 1 ? 0 : (max - lightness) / Math.min(lightness, 1 - lightness);
149
+ switch (max) {
150
+ case blue:
151
+ hue = (red - green) / delta + 4;
152
+ break;
153
+ case green:
154
+ hue = (blue - red) / delta + 2;
155
+ break;
156
+ case red:
157
+ hue = (green - blue) / delta + (green < blue ? 6 : 0);
158
+ break;
159
+ default:
160
+ break;
161
+ }
162
+ hue *= 60;
163
+ }
164
+ if (saturation < 0) {
165
+ hue += 180;
166
+ saturation = Math.abs(saturation);
167
+ }
168
+ if (hue >= 360) {
169
+ hue -= 360;
170
+ }
171
+ return createHsl({
172
+ hue: +hue.toFixed(2),
173
+ lightness: +(lightness * 100).toFixed(2),
174
+ saturation: +(saturation * 100).toFixed(2)
175
+ });
176
+ }
177
+ var anyPattern = /^#*([a-f0-9]{3}){1,2}$/i;
178
+ var groupedPattern = /^#*([a-f0-9]{2})([a-f0-9]{2})([a-f0-9]{2})$/i;
179
+ export {
180
+ rgbToHsl,
181
+ rgbToHex,
182
+ hslToRgb,
183
+ hexToRgb,
184
+ getForegroundColour
185
+ };
@@ -0,0 +1,178 @@
1
+ // src/js/colour.ts
2
+ import {clamp} from "./number";
3
+ var createHex = function(original) {
4
+ let value = original.slice();
5
+ const instance = Object.create({
6
+ toHsl() {
7
+ return hexToRgb(value).toHsl();
8
+ },
9
+ toRgb() {
10
+ return hexToRgb(value);
11
+ },
12
+ toString() {
13
+ return value;
14
+ }
15
+ });
16
+ Object.defineProperty(instance, "value", {
17
+ get() {
18
+ return value.slice();
19
+ },
20
+ set(hex) {
21
+ if (anyPattern.test(hex)) {
22
+ value = `#${getHex(hex)}`;
23
+ }
24
+ }
25
+ });
26
+ return instance;
27
+ };
28
+ var createHsl = function(original) {
29
+ const value = { ...original };
30
+ const instance = Object.create({
31
+ toHex() {
32
+ return hslToRgb(value).toHex();
33
+ },
34
+ toRgb() {
35
+ return hslToRgb(value);
36
+ },
37
+ toString() {
38
+ return `hsl(${value.hue}, ${value.saturation}%, ${value.lightness}%)`;
39
+ }
40
+ });
41
+ Object.defineProperties(instance, {
42
+ hue: createProperty(value, "hue", 0, 360),
43
+ lightness: createProperty(value, "lightness", 0, 100),
44
+ saturation: createProperty(value, "saturation", 0, 100),
45
+ value: { value }
46
+ });
47
+ return instance;
48
+ };
49
+ var createProperty = function(store, key, min, max) {
50
+ return {
51
+ get() {
52
+ return store[key];
53
+ },
54
+ set(value) {
55
+ store[key] = clamp(value, min, max);
56
+ }
57
+ };
58
+ };
59
+ var createRgb = function(original) {
60
+ const value = { ...original };
61
+ const instance = Object.create({
62
+ toHex() {
63
+ return rgbToHex(value);
64
+ },
65
+ toHsl() {
66
+ return rgbToHsl(value);
67
+ },
68
+ toString() {
69
+ return `rgb(${value.red}, ${value.green}, ${value.blue})`;
70
+ }
71
+ });
72
+ Object.defineProperties(instance, {
73
+ blue: createProperty(value, "blue", 0, 255),
74
+ green: createProperty(value, "green", 0, 255),
75
+ red: createProperty(value, "red", 0, 255),
76
+ value: { value }
77
+ });
78
+ return instance;
79
+ };
80
+ function getForegroundColour(value) {
81
+ const values = [value.blue / 255, value.green / 255, value.red / 255];
82
+ for (let colour of values) {
83
+ if (colour <= 0.03928) {
84
+ colour /= 12.92;
85
+ } else {
86
+ colour = ((colour + 0.055) / 1.055) ** 2.4;
87
+ }
88
+ }
89
+ const luminance = 0.2126 * values[2] + 0.7152 * values[1] + 0.0722 * values[0];
90
+ return luminance > 0.625 ? "black" : "white";
91
+ }
92
+ var getHex = function(value) {
93
+ const normalised = value.replace(/^#/, "");
94
+ return normalised.length === 3 ? normalised.split("").map((character) => character.repeat(2)).join("") : normalised;
95
+ };
96
+ function hexToRgb(value) {
97
+ const hex = anyPattern.test(value) ? getHex(value) : "";
98
+ const pairs = groupedPattern.exec(hex) ?? [];
99
+ const rgb = [];
100
+ const { length } = pairs;
101
+ for (let index = 1;index < length; index += 1) {
102
+ rgb.push(Number.parseInt(pairs[index], 16));
103
+ }
104
+ return createRgb({ blue: rgb[2] ?? 0, green: rgb[1] ?? 0, red: rgb[0] ?? 0 });
105
+ }
106
+ function hslToRgb(value) {
107
+ let hue = value.hue % 360;
108
+ if (hue < 0) {
109
+ hue += 360;
110
+ }
111
+ const saturation = value.saturation / 100;
112
+ const lightness = value.lightness / 100;
113
+ function get(value2) {
114
+ const part = (value2 + hue / 30) % 12;
115
+ const mod = saturation * Math.min(lightness, 1 - lightness);
116
+ return lightness - mod * Math.max(-1, Math.min(part - 3, 9 - part, 1));
117
+ }
118
+ return createRgb({
119
+ blue: clamp(Math.round(get(4) * 255), 0, 255),
120
+ green: clamp(Math.round(get(8) * 255), 0, 255),
121
+ red: clamp(Math.round(get(0) * 255), 0, 255)
122
+ });
123
+ }
124
+ function rgbToHex(value) {
125
+ return createHex(`#${[value.red, value.green, value.blue].map((colour) => {
126
+ const hex = colour.toString(16);
127
+ return hex.length === 1 ? `0${hex}` : hex;
128
+ }).join("")}`);
129
+ }
130
+ function rgbToHsl(rgb) {
131
+ const blue = rgb.blue / 255;
132
+ const green = rgb.green / 255;
133
+ const red = rgb.red / 255;
134
+ const max = Math.max(blue, green, red);
135
+ const min = Math.min(blue, green, red);
136
+ const delta = max - min;
137
+ const lightness = (min + max) / 2;
138
+ let hue = 0;
139
+ let saturation = 0;
140
+ if (delta !== 0) {
141
+ saturation = lightness === 0 || lightness === 1 ? 0 : (max - lightness) / Math.min(lightness, 1 - lightness);
142
+ switch (max) {
143
+ case blue:
144
+ hue = (red - green) / delta + 4;
145
+ break;
146
+ case green:
147
+ hue = (blue - red) / delta + 2;
148
+ break;
149
+ case red:
150
+ hue = (green - blue) / delta + (green < blue ? 6 : 0);
151
+ break;
152
+ default:
153
+ break;
154
+ }
155
+ hue *= 60;
156
+ }
157
+ if (saturation < 0) {
158
+ hue += 180;
159
+ saturation = Math.abs(saturation);
160
+ }
161
+ if (hue >= 360) {
162
+ hue -= 360;
163
+ }
164
+ return createHsl({
165
+ hue: +hue.toFixed(2),
166
+ lightness: +(lightness * 100).toFixed(2),
167
+ saturation: +(saturation * 100).toFixed(2)
168
+ });
169
+ }
170
+ var anyPattern = /^#*([a-f0-9]{3}){1,2}$/i;
171
+ var groupedPattern = /^#*([a-f0-9]{2})([a-f0-9]{2})([a-f0-9]{2})$/i;
172
+ export {
173
+ rgbToHsl,
174
+ rgbToHex,
175
+ hslToRgb,
176
+ hexToRgb,
177
+ getForegroundColour
178
+ };
package/dist/js/index.js CHANGED
@@ -132,6 +132,214 @@ function splice(array, start, amountOrValues, values) {
132
132
  function unique(array, key) {
133
133
  return _findValues("unique", array, undefined, key);
134
134
  }
135
+ // src/js/number.ts
136
+ function between(value, min, max) {
137
+ return value >= min && value <= max;
138
+ }
139
+ function clamp(value, min, max, loop) {
140
+ if (value < min) {
141
+ return loop === true ? max : min;
142
+ }
143
+ return value > max ? loop === true ? min : max : value;
144
+ }
145
+ function getNumber(value) {
146
+ if (typeof value === "number") {
147
+ return value;
148
+ }
149
+ if (typeof value === "symbol") {
150
+ return Number.NaN;
151
+ }
152
+ let parsed = value?.valueOf?.() ?? value;
153
+ if (typeof parsed === "object") {
154
+ parsed = parsed?.toString() ?? parsed;
155
+ }
156
+ if (typeof parsed !== "string") {
157
+ return parsed == null ? Number.NaN : typeof parsed === "number" ? parsed : +parsed;
158
+ }
159
+ if (/^\s*0+\s*$/.test(parsed)) {
160
+ return 0;
161
+ }
162
+ const trimmed = parsed.trim();
163
+ if (trimmed.length === 0) {
164
+ return Number.NaN;
165
+ }
166
+ const isBinary = /^0b[01]+$/i.test(trimmed);
167
+ if (isBinary || /^0o[0-7]+$/i.test(trimmed)) {
168
+ return Number.parseInt(trimmed.slice(2), isBinary ? 2 : 8);
169
+ }
170
+ return +(/^0x[0-9a-f]+$/i.test(trimmed) ? trimmed : trimmed.replace(/_/g, ""));
171
+ }
172
+
173
+ // src/js/colour.ts
174
+ var createHex = function(original) {
175
+ let value = original.slice();
176
+ const instance = Object.create({
177
+ toHsl() {
178
+ return hexToRgb(value).toHsl();
179
+ },
180
+ toRgb() {
181
+ return hexToRgb(value);
182
+ },
183
+ toString() {
184
+ return value;
185
+ }
186
+ });
187
+ Object.defineProperty(instance, "value", {
188
+ get() {
189
+ return value.slice();
190
+ },
191
+ set(hex) {
192
+ if (anyPattern.test(hex)) {
193
+ value = `#${getHex(hex)}`;
194
+ }
195
+ }
196
+ });
197
+ return instance;
198
+ };
199
+ var createHsl = function(original) {
200
+ const value = { ...original };
201
+ const instance = Object.create({
202
+ toHex() {
203
+ return hslToRgb(value).toHex();
204
+ },
205
+ toRgb() {
206
+ return hslToRgb(value);
207
+ },
208
+ toString() {
209
+ return `hsl(${value.hue}, ${value.saturation}%, ${value.lightness}%)`;
210
+ }
211
+ });
212
+ Object.defineProperties(instance, {
213
+ hue: createProperty(value, "hue", 0, 360),
214
+ lightness: createProperty(value, "lightness", 0, 100),
215
+ saturation: createProperty(value, "saturation", 0, 100),
216
+ value: { value }
217
+ });
218
+ return instance;
219
+ };
220
+ var createProperty = function(store, key, min, max) {
221
+ return {
222
+ get() {
223
+ return store[key];
224
+ },
225
+ set(value) {
226
+ store[key] = clamp(value, min, max);
227
+ }
228
+ };
229
+ };
230
+ var createRgb = function(original) {
231
+ const value = { ...original };
232
+ const instance = Object.create({
233
+ toHex() {
234
+ return rgbToHex(value);
235
+ },
236
+ toHsl() {
237
+ return rgbToHsl(value);
238
+ },
239
+ toString() {
240
+ return `rgb(${value.red}, ${value.green}, ${value.blue})`;
241
+ }
242
+ });
243
+ Object.defineProperties(instance, {
244
+ blue: createProperty(value, "blue", 0, 255),
245
+ green: createProperty(value, "green", 0, 255),
246
+ red: createProperty(value, "red", 0, 255),
247
+ value: { value }
248
+ });
249
+ return instance;
250
+ };
251
+ function getForegroundColour(value) {
252
+ const values = [value.blue / 255, value.green / 255, value.red / 255];
253
+ for (let colour of values) {
254
+ if (colour <= 0.03928) {
255
+ colour /= 12.92;
256
+ } else {
257
+ colour = ((colour + 0.055) / 1.055) ** 2.4;
258
+ }
259
+ }
260
+ const luminance = 0.2126 * values[2] + 0.7152 * values[1] + 0.0722 * values[0];
261
+ return luminance > 0.625 ? "black" : "white";
262
+ }
263
+ var getHex = function(value) {
264
+ const normalised = value.replace(/^#/, "");
265
+ return normalised.length === 3 ? normalised.split("").map((character) => character.repeat(2)).join("") : normalised;
266
+ };
267
+ function hexToRgb(value) {
268
+ const hex = anyPattern.test(value) ? getHex(value) : "";
269
+ const pairs = groupedPattern.exec(hex) ?? [];
270
+ const rgb = [];
271
+ const { length } = pairs;
272
+ for (let index = 1;index < length; index += 1) {
273
+ rgb.push(Number.parseInt(pairs[index], 16));
274
+ }
275
+ return createRgb({ blue: rgb[2] ?? 0, green: rgb[1] ?? 0, red: rgb[0] ?? 0 });
276
+ }
277
+ function hslToRgb(value) {
278
+ let hue = value.hue % 360;
279
+ if (hue < 0) {
280
+ hue += 360;
281
+ }
282
+ const saturation = value.saturation / 100;
283
+ const lightness = value.lightness / 100;
284
+ function get(value2) {
285
+ const part = (value2 + hue / 30) % 12;
286
+ const mod = saturation * Math.min(lightness, 1 - lightness);
287
+ return lightness - mod * Math.max(-1, Math.min(part - 3, 9 - part, 1));
288
+ }
289
+ return createRgb({
290
+ blue: clamp(Math.round(get(4) * 255), 0, 255),
291
+ green: clamp(Math.round(get(8) * 255), 0, 255),
292
+ red: clamp(Math.round(get(0) * 255), 0, 255)
293
+ });
294
+ }
295
+ function rgbToHex(value) {
296
+ return createHex(`#${[value.red, value.green, value.blue].map((colour) => {
297
+ const hex = colour.toString(16);
298
+ return hex.length === 1 ? `0${hex}` : hex;
299
+ }).join("")}`);
300
+ }
301
+ function rgbToHsl(rgb) {
302
+ const blue = rgb.blue / 255;
303
+ const green = rgb.green / 255;
304
+ const red = rgb.red / 255;
305
+ const max = Math.max(blue, green, red);
306
+ const min = Math.min(blue, green, red);
307
+ const delta = max - min;
308
+ const lightness = (min + max) / 2;
309
+ let hue = 0;
310
+ let saturation = 0;
311
+ if (delta !== 0) {
312
+ saturation = lightness === 0 || lightness === 1 ? 0 : (max - lightness) / Math.min(lightness, 1 - lightness);
313
+ switch (max) {
314
+ case blue:
315
+ hue = (red - green) / delta + 4;
316
+ break;
317
+ case green:
318
+ hue = (blue - red) / delta + 2;
319
+ break;
320
+ case red:
321
+ hue = (green - blue) / delta + (green < blue ? 6 : 0);
322
+ break;
323
+ default:
324
+ break;
325
+ }
326
+ hue *= 60;
327
+ }
328
+ if (saturation < 0) {
329
+ hue += 180;
330
+ saturation = Math.abs(saturation);
331
+ }
332
+ if (hue >= 360) {
333
+ hue -= 360;
334
+ }
335
+ return createHsl({
336
+ hue: +hue.toFixed(2),
337
+ lightness: +(lightness * 100).toFixed(2),
338
+ saturation: +(saturation * 100).toFixed(2)
339
+ });
340
+ }
341
+ var anyPattern = /^#*([a-f0-9]{3}){1,2}$/i;
342
+ var groupedPattern = /^#*([a-f0-9]{2})([a-f0-9]{2})([a-f0-9]{2})$/i;
135
343
  // src/js/element/index.ts
136
344
  var _findElements = function(selector, context, single) {
137
345
  const callback = single ? document.querySelector : document.querySelectorAll;
@@ -426,43 +634,6 @@ function isPlainObject(value) {
426
634
  function isPrimitive(value) {
427
635
  return value == null || /^(bigint|boolean|number|string|symbol)$/.test(typeof value);
428
636
  }
429
- // src/js/number.ts
430
- function between(value, min, max) {
431
- return value >= min && value <= max;
432
- }
433
- function clamp(value, min, max, loop) {
434
- if (value < min) {
435
- return loop === true ? max : min;
436
- }
437
- return value > max ? loop === true ? min : max : value;
438
- }
439
- function getNumber(value) {
440
- if (typeof value === "number") {
441
- return value;
442
- }
443
- if (typeof value === "symbol") {
444
- return Number.NaN;
445
- }
446
- let parsed = value?.valueOf?.() ?? value;
447
- if (typeof parsed === "object") {
448
- parsed = parsed?.toString() ?? parsed;
449
- }
450
- if (typeof parsed !== "string") {
451
- return parsed == null ? Number.NaN : typeof parsed === "number" ? parsed : +parsed;
452
- }
453
- if (/^\s*0+\s*$/.test(parsed)) {
454
- return 0;
455
- }
456
- const trimmed = parsed.trim();
457
- if (trimmed.length === 0) {
458
- return Number.NaN;
459
- }
460
- const isBinary = /^0b[01]+$/i.test(trimmed);
461
- if (isBinary || /^0o[0-7]+$/i.test(trimmed)) {
462
- return Number.parseInt(trimmed.slice(2), isBinary ? 2 : 8);
463
- }
464
- return +(/^0x[0-9a-f]+$/i.test(trimmed) ? trimmed : trimmed.replace(/_/g, ""));
465
- }
466
637
  // src/js/queue.ts
467
638
  function queue(callback) {
468
639
  _atomic_queued.add(callback);
@@ -880,6 +1051,8 @@ export {
880
1051
  titleCase,
881
1052
  splice,
882
1053
  setValue,
1054
+ rgbToHsl,
1055
+ rgbToHex,
883
1056
  repeat,
884
1057
  queue,
885
1058
  push,
@@ -901,6 +1074,8 @@ export {
901
1074
  isArrayOrPlainObject,
902
1075
  insert,
903
1076
  indexOf,
1077
+ hslToRgb,
1078
+ hexToRgb,
904
1079
  groupBy,
905
1080
  getValue,
906
1081
  getTextDirection,
@@ -911,10 +1086,10 @@ export {
911
1086
  getRandomFloat,
912
1087
  getRandomDate,
913
1088
  getRandomColour,
914
- getRandomColour as getRandomColor,
915
1089
  getRandomBoolean,
916
1090
  getPosition,
917
1091
  getNumber,
1092
+ getForegroundColour,
918
1093
  getFocusableElements,
919
1094
  getElementUnderPointer,
920
1095
  findParentElement,
package/dist/js/index.mjs CHANGED
@@ -1,5 +1,6 @@
1
1
  // src/js/index.ts
2
2
  export * from "./array";
3
+ export * from "./colour";
3
4
  export * from "./element/index";
4
5
  export * from "./element/focusable";
5
6
  export * from "./event";
package/dist/js/random.js CHANGED
@@ -26,6 +26,5 @@ export {
26
26
  getRandomFloat,
27
27
  getRandomDate,
28
28
  getRandomColour,
29
- getRandomColour as getRandomColor,
30
29
  getRandomBoolean
31
30
  };
@@ -26,6 +26,5 @@ export {
26
26
  getRandomFloat,
27
27
  getRandomDate,
28
28
  getRandomColour,
29
- getRandomColour as getRandomColor,
30
29
  getRandomBoolean
31
30
  };
package/package.json CHANGED
@@ -130,5 +130,5 @@
130
130
  },
131
131
  "type": "module",
132
132
  "types": "./types/index.d.ts",
133
- "version": "0.39.0"
133
+ "version": "0.40.0"
134
134
  }
@@ -0,0 +1,338 @@
1
+ import {clamp} from './number';
2
+
3
+ type Colour<Model> = {
4
+ /**
5
+ * The current value of the colour
6
+ */
7
+ readonly value: Model;
8
+ /**
9
+ * Convert the colour to a hex-colour
10
+ */
11
+ toHex(): HexColour;
12
+ /**
13
+ * Get the colour as a _CSS-formatted_ string
14
+ */
15
+ toString(): string;
16
+ };
17
+
18
+ type HexColour = {
19
+ /**
20
+ * The current value of the colour
21
+ */
22
+ value: string;
23
+ /**
24
+ * Convert the colour to an RGB-colour
25
+ */
26
+ toHsl(): HSLColour;
27
+ /**
28
+ * Convert the colour to an HSL-colour
29
+ */
30
+ toRgb(): RGBColour;
31
+ /**
32
+ * Get the colour as a string
33
+ */
34
+ toString(): string;
35
+ };
36
+
37
+ export type HSLColour = {
38
+ /**
39
+ * The current hue
40
+ */
41
+ hue: number;
42
+ /**
43
+ * The current lightness
44
+ */
45
+ lightness: number;
46
+ /**
47
+ * The current saturation
48
+ */
49
+ saturation: number;
50
+ /**
51
+ * Convert the colour to an RGB-colour
52
+ */
53
+ toRgb(): RGBColour;
54
+ } & Colour<HSLColourValue>;
55
+
56
+ export type HSLColourValue = {
57
+ hue: number;
58
+ lightness: number;
59
+ saturation: number;
60
+ };
61
+
62
+ type Property<Value> = {
63
+ get(): Value;
64
+ set(value: Value): void;
65
+ };
66
+
67
+ export type RGBColour = {
68
+ /**
69
+ * The current blue value
70
+ */
71
+ blue: number;
72
+ /**
73
+ * The current green value
74
+ */
75
+ green: number;
76
+ /**
77
+ * The current red value
78
+ */
79
+ red: number;
80
+ /**
81
+ * Convert the colour to an HSL-colour
82
+ */
83
+ toHsl(): HSLColour;
84
+ } & Colour<RGBColourValue>;
85
+
86
+ export type RGBColourValue = {
87
+ blue: number;
88
+ green: number;
89
+ red: number;
90
+ };
91
+
92
+ const anyPattern = /^#*([a-f0-9]{3}){1,2}$/i;
93
+ const groupedPattern = /^#*([a-f0-9]{2})([a-f0-9]{2})([a-f0-9]{2})$/i;
94
+
95
+ function createHex(original: string): HexColour {
96
+ let value = original.slice();
97
+
98
+ const instance = Object.create({
99
+ toHsl() {
100
+ return hexToRgb(value).toHsl();
101
+ },
102
+ toRgb() {
103
+ return hexToRgb(value);
104
+ },
105
+ toString() {
106
+ return value;
107
+ },
108
+ });
109
+
110
+ Object.defineProperty(instance, 'value', {
111
+ get() {
112
+ return value.slice();
113
+ },
114
+ set(hex: string) {
115
+ if (anyPattern.test(hex)) {
116
+ value = `#${getHex(hex)}`;
117
+ }
118
+ },
119
+ });
120
+
121
+ return instance;
122
+ }
123
+
124
+ function createHsl(original: HSLColourValue): HSLColour {
125
+ const value = {...original};
126
+
127
+ const instance = Object.create({
128
+ toHex() {
129
+ return hslToRgb(value).toHex();
130
+ },
131
+ toRgb() {
132
+ return hslToRgb(value);
133
+ },
134
+ toString() {
135
+ return `hsl(${value.hue}, ${value.saturation}%, ${value.lightness}%)`;
136
+ },
137
+ });
138
+
139
+ Object.defineProperties(instance, {
140
+ hue: createProperty(value, 'hue', 0, 360),
141
+ lightness: createProperty(value, 'lightness', 0, 100),
142
+ saturation: createProperty(value, 'saturation', 0, 100),
143
+ value: {value},
144
+ });
145
+
146
+ return instance;
147
+ }
148
+
149
+ function createProperty(
150
+ store: Record<string, number>,
151
+ key: string,
152
+ min: number,
153
+ max: number,
154
+ ): Property<number> {
155
+ return {
156
+ get() {
157
+ return store[key];
158
+ },
159
+ set(value) {
160
+ store[key] = clamp(value, min, max);
161
+ },
162
+ };
163
+ }
164
+
165
+ function createRgb(original: RGBColourValue): RGBColour {
166
+ const value = {...original};
167
+
168
+ const instance = Object.create({
169
+ toHex() {
170
+ return rgbToHex(value);
171
+ },
172
+ toHsl() {
173
+ return rgbToHsl(value);
174
+ },
175
+ toString() {
176
+ return `rgb(${value.red}, ${value.green}, ${value.blue})`;
177
+ },
178
+ });
179
+
180
+ Object.defineProperties(instance, {
181
+ blue: createProperty(value, 'blue', 0, 255),
182
+ green: createProperty(value, 'green', 0, 255),
183
+ red: createProperty(value, 'red', 0, 255),
184
+ value: {value},
185
+ });
186
+
187
+ return instance;
188
+ }
189
+
190
+ /**
191
+ * Get a foreground colour _(usually text)_ based on a background colour's luminance
192
+ */
193
+ export function getForegroundColour(value: RGBColourValue): string {
194
+ const values = [value.blue / 255, value.green / 255, value.red / 255];
195
+
196
+ for (let colour of values) {
197
+ if (colour <= 0.03928) {
198
+ colour /= 12.92;
199
+ } else {
200
+ colour = ((colour + 0.055) / 1.055) ** 2.4;
201
+ }
202
+ }
203
+
204
+ const luminance =
205
+ 0.2126 * values[2] + 0.7152 * values[1] + 0.0722 * values[0];
206
+
207
+ // Rudimentary and ureliable?; implement APCA for more reliable results?
208
+ return luminance > 0.625 ? 'black' : 'white';
209
+ }
210
+
211
+ function getHex(value: string): string {
212
+ const normalised = value.replace(/^#/, '');
213
+
214
+ return normalised.length === 3
215
+ ? normalised
216
+ .split('')
217
+ .map(character => character.repeat(2))
218
+ .join('')
219
+ : normalised;
220
+ }
221
+
222
+ /**
223
+ * Convert a hex-colour to an RGB-colour
224
+ */
225
+ export function hexToRgb(value: string): RGBColour {
226
+ const hex = anyPattern.test(value) ? getHex(value) : '';
227
+ const pairs = groupedPattern.exec(hex) ?? [];
228
+ const rgb = [];
229
+
230
+ const {length} = pairs;
231
+
232
+ for (let index = 1; index < length; index += 1) {
233
+ rgb.push(Number.parseInt(pairs[index], 16));
234
+ }
235
+
236
+ return createRgb({blue: rgb[2] ?? 0, green: rgb[1] ?? 0, red: rgb[0] ?? 0});
237
+ }
238
+
239
+ /**
240
+ * - Convert an HSL-colour to an RGB-colour
241
+ * - Thanks, https://github.com/color-js/color.js/blob/main/src/spaces/hsl.js#L61
242
+ */
243
+ export function hslToRgb(value: HSLColourValue): RGBColour {
244
+ let hue = value.hue % 360;
245
+
246
+ if (hue < 0) {
247
+ hue += 360;
248
+ }
249
+
250
+ const saturation = value.saturation / 100;
251
+ const lightness = value.lightness / 100;
252
+
253
+ function get(value: number) {
254
+ const part = (value + hue / 30) % 12;
255
+ const mod = saturation * Math.min(lightness, 1 - lightness);
256
+
257
+ return lightness - mod * Math.max(-1, Math.min(part - 3, 9 - part, 1));
258
+ }
259
+
260
+ return createRgb({
261
+ blue: clamp(Math.round(get(4) * 255), 0, 255),
262
+ green: clamp(Math.round(get(8) * 255), 0, 255),
263
+ red: clamp(Math.round(get(0) * 255), 0, 255),
264
+ });
265
+ }
266
+
267
+ /**
268
+ * Convert an RGB-colour to a hex-colour
269
+ */
270
+ export function rgbToHex(value: RGBColourValue): HexColour {
271
+ return createHex(
272
+ `#${[value.red, value.green, value.blue]
273
+ .map(colour => {
274
+ const hex = colour.toString(16);
275
+
276
+ return hex.length === 1 ? `0${hex}` : hex;
277
+ })
278
+ .join('')}`,
279
+ );
280
+ }
281
+
282
+ /**
283
+ * - Convert an RGB-colour to an HSL-colour
284
+ * - Thanks, https://github.com/color-js/color.js/blob/main/src/spaces/hsl.js#L26
285
+ */
286
+ export function rgbToHsl(rgb: RGBColourValue): HSLColour {
287
+ const blue = rgb.blue / 255;
288
+ const green = rgb.green / 255;
289
+ const red = rgb.red / 255;
290
+
291
+ const max = Math.max(blue, green, red);
292
+ const min = Math.min(blue, green, red);
293
+
294
+ const delta = max - min;
295
+ const lightness = (min + max) / 2;
296
+
297
+ let hue = 0;
298
+ let saturation = 0;
299
+
300
+ if (delta !== 0) {
301
+ saturation =
302
+ lightness === 0 || lightness === 1
303
+ ? 0
304
+ : (max - lightness) / Math.min(lightness, 1 - lightness);
305
+
306
+ switch (max) {
307
+ case blue:
308
+ hue = (red - green) / delta + 4;
309
+ break;
310
+ case green:
311
+ hue = (blue - red) / delta + 2;
312
+ break;
313
+ case red:
314
+ hue = (green - blue) / delta + (green < blue ? 6 : 0);
315
+ break;
316
+ default:
317
+ break;
318
+ }
319
+
320
+ hue *= 60;
321
+ }
322
+
323
+ if (saturation < 0) {
324
+ hue += 180;
325
+ saturation = Math.abs(saturation);
326
+ }
327
+
328
+ if (hue >= 360) {
329
+ hue -= 360;
330
+ }
331
+
332
+ return createHsl({
333
+ hue: +hue.toFixed(2),
334
+ lightness: +(lightness * 100).toFixed(2),
335
+ saturation: +(saturation * 100).toFixed(2),
336
+ });
337
+ }
338
+
package/src/js/index.ts CHANGED
@@ -1,4 +1,5 @@
1
1
  export * from './array';
2
+ export * from './colour';
2
3
  export * from './element/index';
3
4
  export * from './element/focusable';
4
5
  export * from './event';
package/src/js/random.ts CHANGED
@@ -44,5 +44,3 @@ export function getRandomInteger(min?: number, max?: number): number {
44
44
  export function getRandomHex(): string {
45
45
  return '0123456789ABCDEF'[getRandomInteger(0, 16)];
46
46
  }
47
-
48
- export {getRandomColour as getRandomColor};
@@ -0,0 +1,101 @@
1
+ type Colour<Model> = {
2
+ /**
3
+ * The current value of the colour
4
+ */
5
+ readonly value: Model;
6
+ /**
7
+ * Convert the colour to a hex-colour
8
+ */
9
+ toHex(): HexColour;
10
+ /**
11
+ * Get the colour as a _CSS-formatted_ string
12
+ */
13
+ toString(): string;
14
+ };
15
+ type HexColour = {
16
+ /**
17
+ * The current value of the colour
18
+ */
19
+ value: string;
20
+ /**
21
+ * Convert the colour to an RGB-colour
22
+ */
23
+ toHsl(): HSLColour;
24
+ /**
25
+ * Convert the colour to an HSL-colour
26
+ */
27
+ toRgb(): RGBColour;
28
+ /**
29
+ * Get the colour as a string
30
+ */
31
+ toString(): string;
32
+ };
33
+ export type HSLColour = {
34
+ /**
35
+ * The current hue
36
+ */
37
+ hue: number;
38
+ /**
39
+ * The current lightness
40
+ */
41
+ lightness: number;
42
+ /**
43
+ * The current saturation
44
+ */
45
+ saturation: number;
46
+ /**
47
+ * Convert the colour to an RGB-colour
48
+ */
49
+ toRgb(): RGBColour;
50
+ } & Colour<HSLColourValue>;
51
+ export type HSLColourValue = {
52
+ hue: number;
53
+ lightness: number;
54
+ saturation: number;
55
+ };
56
+ export type RGBColour = {
57
+ /**
58
+ * The current blue value
59
+ */
60
+ blue: number;
61
+ /**
62
+ * The current green value
63
+ */
64
+ green: number;
65
+ /**
66
+ * The current red value
67
+ */
68
+ red: number;
69
+ /**
70
+ * Convert the colour to an HSL-colour
71
+ */
72
+ toHsl(): HSLColour;
73
+ } & Colour<RGBColourValue>;
74
+ export type RGBColourValue = {
75
+ blue: number;
76
+ green: number;
77
+ red: number;
78
+ };
79
+ /**
80
+ * Get a foreground colour _(usually text)_ based on a background colour's luminance
81
+ */
82
+ export declare function getForegroundColour(value: RGBColourValue): string;
83
+ /**
84
+ * Convert a hex-colour to an RGB-colour
85
+ */
86
+ export declare function hexToRgb(value: string): RGBColour;
87
+ /**
88
+ * - Convert an HSL-colour to an RGB-colour
89
+ * - Thanks, https://github.com/color-js/color.js/blob/main/src/spaces/hsl.js#L61
90
+ */
91
+ export declare function hslToRgb(value: HSLColourValue): RGBColour;
92
+ /**
93
+ * Convert an RGB-colour to a hex-colour
94
+ */
95
+ export declare function rgbToHex(value: RGBColourValue): HexColour;
96
+ /**
97
+ * - Convert an RGB-colour to an HSL-colour
98
+ * - Thanks, https://github.com/color-js/color.js/blob/main/src/spaces/hsl.js#L26
99
+ */
100
+ export declare function rgbToHsl(rgb: RGBColourValue): HSLColour;
101
+ export {};
package/types/index.d.ts CHANGED
@@ -1,4 +1,5 @@
1
1
  export * from './array';
2
+ export * from './colour';
2
3
  export * from './element/index';
3
4
  export * from './element/focusable';
4
5
  export * from './event';
package/types/random.d.ts CHANGED
@@ -22,4 +22,3 @@ export declare function getRandomInteger(min?: number, max?: number): number;
22
22
  * Returns a random hexadecimal character
23
23
  */
24
24
  export declare function getRandomHex(): string;
25
- export { getRandomColour as getRandomColor };