@nativerent/js-utils 1.0.0 → 1.0.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +3 -1
- package/dist/index.d.ts +139 -5
- package/dist/index.js +357 -1
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +319 -2
- package/dist/index.mjs.map +1 -1
- package/jest.config.mjs +5 -0
- package/package.json +6 -2
- package/src/index.ts +563 -12
- package/src/types.d.ts +7 -1
- package/tests/tests.ts +521 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@nativerent/js-utils",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.2",
|
|
4
4
|
"description": "",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"module": "dist/index.mjs",
|
|
@@ -12,11 +12,15 @@
|
|
|
12
12
|
"author": "NativeRent",
|
|
13
13
|
"license": "GPL-3.0-or-later",
|
|
14
14
|
"devDependencies": {
|
|
15
|
+
"@types/jest": "^29.5.11",
|
|
15
16
|
"eslint": "^8.56.0",
|
|
17
|
+
"jest": "^29.7.0",
|
|
16
18
|
"prettier": "^3.1.1",
|
|
17
19
|
"rollup": "^4.9.4",
|
|
18
20
|
"rollup-plugin-dts": "^6.1.0",
|
|
19
21
|
"rollup-plugin-esbuild": "^6.1.0",
|
|
20
|
-
"
|
|
22
|
+
"ts-jest": "^29.1.1",
|
|
23
|
+
"typescript": "^5.3.3",
|
|
24
|
+
"jest-environment-jsdom": "^29.7.0"
|
|
21
25
|
}
|
|
22
26
|
}
|
package/src/index.ts
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
|
-
import { Primitive } from "./types";
|
|
1
|
+
import { FlattenableObject, Primitive, SimpleObject } from "./types";
|
|
2
2
|
|
|
3
3
|
export function debounce(fn: Function, delay: number): () => void {
|
|
4
4
|
let timeout: ReturnType<typeof setTimeout>;
|
|
5
5
|
|
|
6
|
-
return function (
|
|
6
|
+
return function (...args: any[]) {
|
|
7
7
|
clearTimeout(timeout);
|
|
8
|
-
timeout = setTimeout(() => fn.apply(
|
|
8
|
+
timeout = setTimeout(() => fn.apply(null, args), delay);
|
|
9
9
|
};
|
|
10
10
|
}
|
|
11
11
|
|
|
@@ -38,6 +38,13 @@ export function isString(str: any): str is string {
|
|
|
38
38
|
return typeof str === "string";
|
|
39
39
|
}
|
|
40
40
|
|
|
41
|
+
/**
|
|
42
|
+
* Check if the given argument is a string which is not empty
|
|
43
|
+
*/
|
|
44
|
+
export function isNotEmptyString(str: any) {
|
|
45
|
+
return typeof str === "string" && str.length;
|
|
46
|
+
}
|
|
47
|
+
|
|
41
48
|
export function isHTMLElement(el: any): el is HTMLElement {
|
|
42
49
|
return el instanceof HTMLElement || el instanceof SVGElement;
|
|
43
50
|
}
|
|
@@ -117,23 +124,75 @@ export function getObjectKeys(object: object): string[] {
|
|
|
117
124
|
}
|
|
118
125
|
|
|
119
126
|
/**
|
|
120
|
-
*
|
|
121
|
-
*
|
|
122
|
-
* @param object {[key: string]: SimpleObject | { [key: string]: SimpleObject }}
|
|
127
|
+
* Convert an object to a query string
|
|
128
|
+
* Works with primitive objects
|
|
123
129
|
*/
|
|
124
|
-
export function objectToQueryString(object:
|
|
130
|
+
export function objectToQueryString(object: {
|
|
131
|
+
[key: string | number]:
|
|
132
|
+
| Primitive
|
|
133
|
+
| Array<Primitive | null | undefined>
|
|
134
|
+
| SimpleObject
|
|
135
|
+
| { [key: string | number]: SimpleObject }
|
|
136
|
+
| undefined
|
|
137
|
+
| null;
|
|
138
|
+
}): string {
|
|
125
139
|
return Object.entries(object)
|
|
126
140
|
.map(([k, v]) => {
|
|
127
141
|
if (Array.isArray(v)) {
|
|
128
|
-
return v
|
|
142
|
+
return v
|
|
143
|
+
.map((item) => `${k}[]=${encodeURIComponent(item || "")}`)
|
|
144
|
+
.join("&");
|
|
129
145
|
} else if (isObject(v)) {
|
|
130
|
-
|
|
146
|
+
return `${k}=${encodeURIComponent(JSON.stringify(v))}`;
|
|
131
147
|
}
|
|
132
148
|
return `${k}=${encodeURIComponent(v || "")}`;
|
|
133
149
|
})
|
|
134
150
|
.join("&");
|
|
135
151
|
}
|
|
136
152
|
|
|
153
|
+
export function countObjectInnerLength(object: {
|
|
154
|
+
[key: string | number]: any;
|
|
155
|
+
}) {
|
|
156
|
+
const obj = Object.values(object);
|
|
157
|
+
|
|
158
|
+
return obj.length === 1
|
|
159
|
+
? obj[0].length
|
|
160
|
+
: obj.reduce((acc, cur) => {
|
|
161
|
+
if (Array.isArray(acc)) {
|
|
162
|
+
acc = acc.length;
|
|
163
|
+
}
|
|
164
|
+
if (cur) {
|
|
165
|
+
acc += cur.length;
|
|
166
|
+
}
|
|
167
|
+
return acc;
|
|
168
|
+
});
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
/**
|
|
172
|
+
* Check if the element is in viewport
|
|
173
|
+
*/
|
|
174
|
+
export function isElementVisible(element: HTMLElement, minVisiblePercent = 50) {
|
|
175
|
+
const elementCoords = element.getBoundingClientRect();
|
|
176
|
+
const elementHeight = elementCoords.height;
|
|
177
|
+
|
|
178
|
+
if (!elementHeight) {
|
|
179
|
+
return false;
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
const elementTop = elementCoords.top;
|
|
183
|
+
const elementBottom = elementCoords.bottom;
|
|
184
|
+
const viewPortHeight = window.innerHeight;
|
|
185
|
+
|
|
186
|
+
const elementVisibleHeight =
|
|
187
|
+
elementTop > 0
|
|
188
|
+
? // the element is in or below viewport
|
|
189
|
+
viewPortHeight - elementTop
|
|
190
|
+
: // the element is still in or above viewport
|
|
191
|
+
elementBottom;
|
|
192
|
+
|
|
193
|
+
return (elementVisibleHeight / elementHeight) * 100 >= minVisiblePercent;
|
|
194
|
+
}
|
|
195
|
+
|
|
137
196
|
export function decodeSafeURL(url: string): string {
|
|
138
197
|
try {
|
|
139
198
|
return decodeURI(url);
|
|
@@ -142,7 +201,8 @@ export function decodeSafeURL(url: string): string {
|
|
|
142
201
|
}
|
|
143
202
|
}
|
|
144
203
|
|
|
145
|
-
export function getSafeURL(url
|
|
204
|
+
export function getSafeURL(url?: string): string {
|
|
205
|
+
url = url ? url : location.href;
|
|
146
206
|
return encodeURI(decodeSafeURL(url));
|
|
147
207
|
}
|
|
148
208
|
|
|
@@ -183,6 +243,13 @@ export function toBinaryStr(str: string) {
|
|
|
183
243
|
return String.fromCharCode(...charCodes);
|
|
184
244
|
}
|
|
185
245
|
|
|
246
|
+
/**
|
|
247
|
+
* Find a DOM element where an ad unit should be rendered to
|
|
248
|
+
*/
|
|
249
|
+
export function getHtmlElement(id: string) {
|
|
250
|
+
return document.getElementById(id);
|
|
251
|
+
}
|
|
252
|
+
|
|
186
253
|
/**
|
|
187
254
|
* Convert an HTML string into a list of nodes
|
|
188
255
|
*/
|
|
@@ -216,7 +283,7 @@ export function createHtmlElement(
|
|
|
216
283
|
*/
|
|
217
284
|
export function insertHtmlElements(
|
|
218
285
|
nodes: NodeListOf<Node>,
|
|
219
|
-
appendTo
|
|
286
|
+
appendTo?: Element,
|
|
220
287
|
) {
|
|
221
288
|
appendTo = appendTo || document.body;
|
|
222
289
|
|
|
@@ -242,7 +309,11 @@ export function insertHtmlElements(
|
|
|
242
309
|
flatHtmlAttributes(node.attributes),
|
|
243
310
|
);
|
|
244
311
|
} else {
|
|
245
|
-
newNode = createScriptElement(
|
|
312
|
+
newNode = createScriptElement(
|
|
313
|
+
node.innerHTML,
|
|
314
|
+
true,
|
|
315
|
+
flatHtmlAttributes(node.attributes),
|
|
316
|
+
);
|
|
246
317
|
}
|
|
247
318
|
break;
|
|
248
319
|
|
|
@@ -340,6 +411,18 @@ export function createStyleElement(css: string | null) {
|
|
|
340
411
|
return element;
|
|
341
412
|
}
|
|
342
413
|
|
|
414
|
+
/**
|
|
415
|
+
* Create a <link> element
|
|
416
|
+
*/
|
|
417
|
+
export function createLinkElement(href: string) {
|
|
418
|
+
const element = createHtmlElement("link") as HTMLLinkElement;
|
|
419
|
+
|
|
420
|
+
element.rel = "stylesheet";
|
|
421
|
+
element.href = href;
|
|
422
|
+
|
|
423
|
+
return element;
|
|
424
|
+
}
|
|
425
|
+
|
|
343
426
|
/**
|
|
344
427
|
* Create svg elements
|
|
345
428
|
*/
|
|
@@ -360,3 +443,471 @@ export function createSvgElement(
|
|
|
360
443
|
|
|
361
444
|
return element;
|
|
362
445
|
}
|
|
446
|
+
|
|
447
|
+
/**
|
|
448
|
+
* Create an image element to use as a tracking pixel
|
|
449
|
+
*/
|
|
450
|
+
export function createPixelElement(src: string) {
|
|
451
|
+
const image = document.createElement("img");
|
|
452
|
+
|
|
453
|
+
image.src = src;
|
|
454
|
+
image.width = 1;
|
|
455
|
+
image.height = 1;
|
|
456
|
+
image.style.position = "absolute";
|
|
457
|
+
image.style.left = "-99999px";
|
|
458
|
+
|
|
459
|
+
return image;
|
|
460
|
+
}
|
|
461
|
+
|
|
462
|
+
/**
|
|
463
|
+
* Append a new script element to the DOM head
|
|
464
|
+
*/
|
|
465
|
+
export function insertJs(js: string, inline = false) {
|
|
466
|
+
const element = createScriptElement(js, inline);
|
|
467
|
+
|
|
468
|
+
document.head.appendChild(element);
|
|
469
|
+
}
|
|
470
|
+
|
|
471
|
+
/**
|
|
472
|
+
* Append a new style element to the DOM head
|
|
473
|
+
*/
|
|
474
|
+
export function insertCss(css: string, className?: string, external = false) {
|
|
475
|
+
const element = external ? createLinkElement(css) : createStyleElement(css);
|
|
476
|
+
|
|
477
|
+
if (className) {
|
|
478
|
+
element.className = className;
|
|
479
|
+
}
|
|
480
|
+
|
|
481
|
+
document.head.appendChild(element);
|
|
482
|
+
}
|
|
483
|
+
|
|
484
|
+
/**
|
|
485
|
+
* Get screen sizes
|
|
486
|
+
*/
|
|
487
|
+
export function getScreenSize(): { width: number; height: number } {
|
|
488
|
+
let width, height;
|
|
489
|
+
|
|
490
|
+
if (window.screen) {
|
|
491
|
+
width = screen.width;
|
|
492
|
+
height = screen.height;
|
|
493
|
+
} else {
|
|
494
|
+
width = window.innerWidth;
|
|
495
|
+
height = window.innerHeight;
|
|
496
|
+
}
|
|
497
|
+
|
|
498
|
+
return {
|
|
499
|
+
width: width,
|
|
500
|
+
height: height,
|
|
501
|
+
};
|
|
502
|
+
}
|
|
503
|
+
|
|
504
|
+
/**
|
|
505
|
+
* Get a random integer between min and max
|
|
506
|
+
*/
|
|
507
|
+
export function getRandomInt(min = 0, max = 4294967295) {
|
|
508
|
+
min = Math.ceil(min);
|
|
509
|
+
max = Math.floor(max);
|
|
510
|
+
|
|
511
|
+
return Math.floor(Math.random() * (max - min)) + min;
|
|
512
|
+
}
|
|
513
|
+
|
|
514
|
+
/**
|
|
515
|
+
* Implementation of Java's String.hashCode() method
|
|
516
|
+
*/
|
|
517
|
+
export function hashCode(str: string) {
|
|
518
|
+
let hash = 0,
|
|
519
|
+
chr;
|
|
520
|
+
|
|
521
|
+
for (let i = 0; i < str.length; i++) {
|
|
522
|
+
chr = str.charCodeAt(i);
|
|
523
|
+
hash = (hash << 5) - hash + chr;
|
|
524
|
+
hash |= 0; // convert to 32bit integer
|
|
525
|
+
}
|
|
526
|
+
|
|
527
|
+
return hash >>> 0;
|
|
528
|
+
}
|
|
529
|
+
|
|
530
|
+
/**
|
|
531
|
+
* Executes a callback as soon as DOM is interactive
|
|
532
|
+
*/
|
|
533
|
+
export function onDOMReady(callback: Function, ...args: any[]) {
|
|
534
|
+
if (
|
|
535
|
+
document.readyState === "interactive" ||
|
|
536
|
+
document.readyState === "complete"
|
|
537
|
+
) {
|
|
538
|
+
callback(...args);
|
|
539
|
+
} else {
|
|
540
|
+
document.addEventListener("DOMContentLoaded", () => callback(...args));
|
|
541
|
+
}
|
|
542
|
+
}
|
|
543
|
+
|
|
544
|
+
/**
|
|
545
|
+
* Calculate the percentage of two values
|
|
546
|
+
*
|
|
547
|
+
* @param a
|
|
548
|
+
* @param b
|
|
549
|
+
* @return number
|
|
550
|
+
*/
|
|
551
|
+
export function toPercent(a: number, b: number): number {
|
|
552
|
+
return b > 0 ? (a / b) * 100 : 0;
|
|
553
|
+
}
|
|
554
|
+
|
|
555
|
+
/**
|
|
556
|
+
* Make a camelCase string
|
|
557
|
+
*
|
|
558
|
+
* @param string
|
|
559
|
+
* @return string
|
|
560
|
+
*/
|
|
561
|
+
export function toCamelCase(string: string): string {
|
|
562
|
+
return string
|
|
563
|
+
.split(new RegExp(/[-_.]/))
|
|
564
|
+
.reduce(
|
|
565
|
+
(a: string, b: string) => a + b.charAt(0).toUpperCase() + b.slice(1),
|
|
566
|
+
);
|
|
567
|
+
}
|
|
568
|
+
|
|
569
|
+
/**
|
|
570
|
+
* Make a snake_case string
|
|
571
|
+
*
|
|
572
|
+
* @param string
|
|
573
|
+
* @return string
|
|
574
|
+
*/
|
|
575
|
+
export function toSnakeCase(string: string): string {
|
|
576
|
+
return toCamelCase(string)
|
|
577
|
+
.replace(/(^[A-Z])?([A-Z])/gm, "$1_$2")
|
|
578
|
+
.toLowerCase();
|
|
579
|
+
}
|
|
580
|
+
|
|
581
|
+
/**
|
|
582
|
+
* Transform the first letter of the given string to the upper case
|
|
583
|
+
*
|
|
584
|
+
* @param string
|
|
585
|
+
* @return string
|
|
586
|
+
*/
|
|
587
|
+
export function capitalize(string: string): string {
|
|
588
|
+
return string.charAt(0).toUpperCase() + string.slice(1);
|
|
589
|
+
}
|
|
590
|
+
|
|
591
|
+
/**
|
|
592
|
+
* Registry independent string sorting
|
|
593
|
+
*
|
|
594
|
+
* @param first
|
|
595
|
+
* @param second
|
|
596
|
+
*/
|
|
597
|
+
export function sortByAlphabet(first: string, second: string): number {
|
|
598
|
+
return first.toLowerCase() > second.toLowerCase() ? 1 : -1;
|
|
599
|
+
}
|
|
600
|
+
|
|
601
|
+
/**
|
|
602
|
+
* @param length
|
|
603
|
+
* @return string of max length 100
|
|
604
|
+
*/
|
|
605
|
+
export function getRandomStr(length?: number): string {
|
|
606
|
+
length = length ? (length > 100 ? 100 : length) : 10;
|
|
607
|
+
|
|
608
|
+
let str = Math.random().toString(36).substring(2);
|
|
609
|
+
|
|
610
|
+
while (str.length < length) {
|
|
611
|
+
str += Math.random().toString(36).substring(2);
|
|
612
|
+
}
|
|
613
|
+
|
|
614
|
+
const result = str.slice(-length);
|
|
615
|
+
return isNaN(Number(result)) ? result : getRandomStr(length);
|
|
616
|
+
}
|
|
617
|
+
|
|
618
|
+
export function getRandomItem(items: any[]): any {
|
|
619
|
+
return items[Math.floor(Math.random() * items.length)];
|
|
620
|
+
}
|
|
621
|
+
|
|
622
|
+
/**
|
|
623
|
+
* Summarize elements of the given array
|
|
624
|
+
*
|
|
625
|
+
* @param values
|
|
626
|
+
* @return number | string - a summary of all the numbers in the array or an empty string if
|
|
627
|
+
* there was only NaNs
|
|
628
|
+
*/
|
|
629
|
+
export function sumArray(values: Array<any>): number | string {
|
|
630
|
+
if (values.every((value: any) => isNaN(value))) {
|
|
631
|
+
return "";
|
|
632
|
+
} else {
|
|
633
|
+
return values.reduce((sum: any, curr: any) => {
|
|
634
|
+
const value = Number(curr);
|
|
635
|
+
|
|
636
|
+
if (!isNaN(value)) {
|
|
637
|
+
return sum + value;
|
|
638
|
+
} else {
|
|
639
|
+
return sum;
|
|
640
|
+
}
|
|
641
|
+
}, 0);
|
|
642
|
+
}
|
|
643
|
+
}
|
|
644
|
+
|
|
645
|
+
/**
|
|
646
|
+
* Returns an array of the object keys which values are not present in filter
|
|
647
|
+
*
|
|
648
|
+
* @param obj
|
|
649
|
+
* @param values
|
|
650
|
+
*/
|
|
651
|
+
export function filterObjectKeysByValues(
|
|
652
|
+
obj: { [key: string]: any },
|
|
653
|
+
values: Array<string | number | boolean | null | undefined>,
|
|
654
|
+
): string[] {
|
|
655
|
+
values = Array.isArray(values) ? values : Array(values);
|
|
656
|
+
|
|
657
|
+
const keys = [];
|
|
658
|
+
|
|
659
|
+
for (const key in obj) {
|
|
660
|
+
if (!values.includes(obj[key])) {
|
|
661
|
+
keys.push(key);
|
|
662
|
+
}
|
|
663
|
+
}
|
|
664
|
+
|
|
665
|
+
return keys;
|
|
666
|
+
}
|
|
667
|
+
|
|
668
|
+
export function getFromObject(object: object, key: string): any {
|
|
669
|
+
return key.split(".").reduce((a: any, b: any) => {
|
|
670
|
+
return a[b];
|
|
671
|
+
}, object);
|
|
672
|
+
}
|
|
673
|
+
|
|
674
|
+
export function fireBlurEvent(
|
|
675
|
+
element: string | Element | null,
|
|
676
|
+
delay: number = 0,
|
|
677
|
+
): void {
|
|
678
|
+
fireEvent("blur", element, delay);
|
|
679
|
+
}
|
|
680
|
+
|
|
681
|
+
export function fireInputEvent(
|
|
682
|
+
element: string | Element | null,
|
|
683
|
+
delay: number = 0,
|
|
684
|
+
): void {
|
|
685
|
+
fireEvent("input", element, delay);
|
|
686
|
+
}
|
|
687
|
+
|
|
688
|
+
export function fireEvent(
|
|
689
|
+
eventName: string,
|
|
690
|
+
element: string | Element | null,
|
|
691
|
+
delay: number = 0,
|
|
692
|
+
): void {
|
|
693
|
+
setTimeout(() => {
|
|
694
|
+
if (isString(element)) {
|
|
695
|
+
element = document.querySelector(element);
|
|
696
|
+
}
|
|
697
|
+
|
|
698
|
+
if (!isNullOrUndef(element)) {
|
|
699
|
+
element.dispatchEvent(
|
|
700
|
+
new Event(eventName, {
|
|
701
|
+
bubbles: true,
|
|
702
|
+
}),
|
|
703
|
+
);
|
|
704
|
+
}
|
|
705
|
+
}, delay);
|
|
706
|
+
}
|
|
707
|
+
|
|
708
|
+
export function hasObjectChanged(
|
|
709
|
+
object: { [key: string]: any },
|
|
710
|
+
values: any[],
|
|
711
|
+
): boolean {
|
|
712
|
+
let hasChanged = false;
|
|
713
|
+
|
|
714
|
+
for (const key in object) {
|
|
715
|
+
let oldVal = object[key];
|
|
716
|
+
let [, newVal] = values.find(([newKey]) => key === newKey);
|
|
717
|
+
|
|
718
|
+
if (isObject(oldVal)) {
|
|
719
|
+
if (!isObject(newVal)) {
|
|
720
|
+
hasChanged = true;
|
|
721
|
+
} else {
|
|
722
|
+
hasChanged = hasObjectChanged(oldVal, Object.entries(newVal));
|
|
723
|
+
}
|
|
724
|
+
} else if (Array.isArray(newVal) || Array.isArray(oldVal)) {
|
|
725
|
+
newVal = Array.isArray(newVal) ? newVal : [];
|
|
726
|
+
oldVal = Array.isArray(oldVal) ? oldVal : [];
|
|
727
|
+
|
|
728
|
+
hasChanged = arrayDiff(newVal, oldVal).length > 0;
|
|
729
|
+
} else {
|
|
730
|
+
hasChanged = areDiff(newVal, oldVal);
|
|
731
|
+
}
|
|
732
|
+
|
|
733
|
+
if (hasChanged) {
|
|
734
|
+
break;
|
|
735
|
+
}
|
|
736
|
+
}
|
|
737
|
+
|
|
738
|
+
return hasChanged;
|
|
739
|
+
}
|
|
740
|
+
|
|
741
|
+
export function roundBigNum(number: number): number {
|
|
742
|
+
const digitsNum = Math.trunc(number).toString().length;
|
|
743
|
+
|
|
744
|
+
let roundTo = 0;
|
|
745
|
+
switch (true) {
|
|
746
|
+
case digitsNum > 2 && digitsNum < 5:
|
|
747
|
+
roundTo = 100;
|
|
748
|
+
break;
|
|
749
|
+
case digitsNum >= 5:
|
|
750
|
+
roundTo = 10000;
|
|
751
|
+
break;
|
|
752
|
+
}
|
|
753
|
+
|
|
754
|
+
return roundTo ? Math.round(number / roundTo) * roundTo : number;
|
|
755
|
+
}
|
|
756
|
+
|
|
757
|
+
export function extractNumbers(value: any): string {
|
|
758
|
+
if (!isStr(value) && !isNum(value)) {
|
|
759
|
+
return "";
|
|
760
|
+
}
|
|
761
|
+
return value.toString().replace(/[^0-9]/g, "");
|
|
762
|
+
}
|
|
763
|
+
|
|
764
|
+
export function removeSpaces(value: any): string {
|
|
765
|
+
if (!isStr(value) && !isNum(value)) {
|
|
766
|
+
return "";
|
|
767
|
+
}
|
|
768
|
+
return value.toString().replace(/\s/g, "");
|
|
769
|
+
}
|
|
770
|
+
|
|
771
|
+
export function roundUp(num: number, precision: number): number {
|
|
772
|
+
const multiplier = Number("1".padEnd(precision + 1, "0"));
|
|
773
|
+
return Math.ceil(num * multiplier) / multiplier;
|
|
774
|
+
}
|
|
775
|
+
|
|
776
|
+
export function roundDown(num: number, precision: number): number {
|
|
777
|
+
const multiplier = Number("1".padEnd(precision + 1, "0"));
|
|
778
|
+
return Math.floor(num * multiplier) / multiplier;
|
|
779
|
+
}
|
|
780
|
+
|
|
781
|
+
export function areDiff(
|
|
782
|
+
val1?: Primitive | null,
|
|
783
|
+
val2?: Primitive | null,
|
|
784
|
+
strict?: boolean,
|
|
785
|
+
): boolean {
|
|
786
|
+
if (strict) {
|
|
787
|
+
return val1 !== val2;
|
|
788
|
+
}
|
|
789
|
+
|
|
790
|
+
// at least one value is not empty
|
|
791
|
+
if (Boolean(val1) || Boolean(val2)) {
|
|
792
|
+
return val1 != val2;
|
|
793
|
+
}
|
|
794
|
+
|
|
795
|
+
// both empty, but not equally empty!
|
|
796
|
+
// each one may be one of: empty string, null or undefined
|
|
797
|
+
return false;
|
|
798
|
+
}
|
|
799
|
+
|
|
800
|
+
export function flattenObject(obj: FlattenableObject, prefix = "") {
|
|
801
|
+
return Object.keys(obj).reduce((acc, k) => {
|
|
802
|
+
const pre = prefix.length ? prefix + "." : "";
|
|
803
|
+
if (isObject(obj[k]) || Array.isArray(obj[k])) {
|
|
804
|
+
Object.assign(acc, flattenObject(obj[k], pre + k));
|
|
805
|
+
} else {
|
|
806
|
+
acc[pre + k] = obj[k];
|
|
807
|
+
}
|
|
808
|
+
return acc;
|
|
809
|
+
}, {} as FlattenableObject);
|
|
810
|
+
}
|
|
811
|
+
|
|
812
|
+
export function flattenObjectAsArray(obj: FlattenableObject) {
|
|
813
|
+
const result: FlattenableObject = {};
|
|
814
|
+
const flat = flattenObject(obj);
|
|
815
|
+
Object.keys(flat).forEach((key) => {
|
|
816
|
+
const newKey = key.replaceAll(/\.([^.]+)/g, "[$1]");
|
|
817
|
+
result[newKey] = flat[key];
|
|
818
|
+
});
|
|
819
|
+
|
|
820
|
+
return result;
|
|
821
|
+
}
|
|
822
|
+
|
|
823
|
+
export function parseObjectPathStr(pathStr: string): string[] {
|
|
824
|
+
const pathParts = pathStr.replace(/\[/g, ".").replace(/\]/g, "").split(".");
|
|
825
|
+
|
|
826
|
+
if (pathParts.length > 1 && pathParts[1].includes("[")) {
|
|
827
|
+
return [pathParts[0], ...parseObjectPathStr(pathParts[1])];
|
|
828
|
+
}
|
|
829
|
+
|
|
830
|
+
return pathParts;
|
|
831
|
+
}
|
|
832
|
+
|
|
833
|
+
export function formatNumber(
|
|
834
|
+
num: number | string,
|
|
835
|
+
fractionDigits?: number,
|
|
836
|
+
): string {
|
|
837
|
+
fractionDigits = isNullOrUndef(fractionDigits) ? 2 : fractionDigits;
|
|
838
|
+
return new Intl.NumberFormat("ru-RU", {
|
|
839
|
+
style: "decimal",
|
|
840
|
+
minimumFractionDigits: fractionDigits,
|
|
841
|
+
maximumFractionDigits: fractionDigits,
|
|
842
|
+
})
|
|
843
|
+
.format(Number(num))
|
|
844
|
+
.replace(",", ".")
|
|
845
|
+
.replace(/\u00A0/g, " "); // charCode 160, White-space
|
|
846
|
+
}
|
|
847
|
+
|
|
848
|
+
export function formatNumberWithSign(
|
|
849
|
+
num: number | string,
|
|
850
|
+
fractionDigits?: number,
|
|
851
|
+
): string {
|
|
852
|
+
return formatWithSign(num, formatNumber(num, fractionDigits));
|
|
853
|
+
}
|
|
854
|
+
|
|
855
|
+
export function formatPercent(num: number | string): string {
|
|
856
|
+
return formatNumber(num) + "%";
|
|
857
|
+
}
|
|
858
|
+
|
|
859
|
+
export function formatWithSign(
|
|
860
|
+
num: number | string,
|
|
861
|
+
numString?: string,
|
|
862
|
+
): string {
|
|
863
|
+
numString = numString ? numString : num.toString();
|
|
864
|
+
return (Number(num) < 0 ? "" : "+") + numString;
|
|
865
|
+
}
|
|
866
|
+
|
|
867
|
+
export function autoSizeText(
|
|
868
|
+
el: HTMLElement,
|
|
869
|
+
height: number,
|
|
870
|
+
minFontSize: number,
|
|
871
|
+
maxFontSize: number = 50,
|
|
872
|
+
) {
|
|
873
|
+
let attempts = 30;
|
|
874
|
+
|
|
875
|
+
const resizeText = () => {
|
|
876
|
+
if (getTextHeight() === 0) {
|
|
877
|
+
return;
|
|
878
|
+
}
|
|
879
|
+
while (attempts && getTextHeight() > height) {
|
|
880
|
+
attempts--;
|
|
881
|
+
reduceText();
|
|
882
|
+
}
|
|
883
|
+
while (attempts && getTextHeight() < height) {
|
|
884
|
+
attempts--;
|
|
885
|
+
enlargeText();
|
|
886
|
+
}
|
|
887
|
+
};
|
|
888
|
+
|
|
889
|
+
const reduceText = () => {
|
|
890
|
+
const fontSize = getNumericStyleProp("fontSize", el);
|
|
891
|
+
const newFontSize = fontSize - 1;
|
|
892
|
+
if (fontSize > 1 && newFontSize >= minFontSize) {
|
|
893
|
+
el.style.fontSize = `${newFontSize}px`;
|
|
894
|
+
}
|
|
895
|
+
};
|
|
896
|
+
|
|
897
|
+
const enlargeText = () => {
|
|
898
|
+
const fontSize = getNumericStyleProp("fontSize", el);
|
|
899
|
+
const newFontSize = fontSize + 1;
|
|
900
|
+
if (newFontSize <= maxFontSize) {
|
|
901
|
+
el.style.fontSize = `${newFontSize}px`;
|
|
902
|
+
}
|
|
903
|
+
};
|
|
904
|
+
|
|
905
|
+
const getTextHeight = () =>
|
|
906
|
+
Math.floor(
|
|
907
|
+
el.scrollHeight -
|
|
908
|
+
getNumericStyleProp("paddingTop", el) -
|
|
909
|
+
getNumericStyleProp("paddingBottom", el),
|
|
910
|
+
);
|
|
911
|
+
|
|
912
|
+
resizeText();
|
|
913
|
+
}
|
package/src/types.d.ts
CHANGED
|
@@ -1 +1,7 @@
|
|
|
1
|
-
export type
|
|
1
|
+
export type FlattenableObject = { [key: string]: any };
|
|
2
|
+
|
|
3
|
+
export type Primitive = boolean | number | string;
|
|
4
|
+
|
|
5
|
+
export type SimpleObject = {
|
|
6
|
+
[key: string | number]: Primitive | Primitive[] | null | undefined;
|
|
7
|
+
};
|