Initial commit

This commit is contained in:
Igor Martyniuk
2026-06-25 15:53:49 +03:00
commit 1bb0b7e68a
1517 changed files with 218469 additions and 0 deletions
+31
View File
@@ -0,0 +1,31 @@
# [←](../../Readme.md) Огляд Array утиліт
### # arrayDedup\<T>(arr: T[]): T[]
Видаляє дублікати з масиву об'єктів або складних значень. Порівняння відбувається через детермінований stringify (ключі об'єктів сортуються).
```ts
arrayDedup([{ a: 1 }, { a: 1 }, { a: 2 }]); // → [{ a: 1 }, { a: 2 }]
arrayDedup([1, 'x', 1, null, null]); // → [1, 'x', null]
```
---
### # arrayDedupPrimitives(array: Array\<number | string | null | undefined>): Array
Видаляє дублікати з масиву примітивних значень через `Set`. Швидший варіант для простих типів.
```ts
arrayDedupPrimitives([1, 2, 2, 3]); // → [1, 2, 3]
arrayDedupPrimitives(['a', 'b', 'a']); // → ['a', 'b']
```
---
### # without\<T>(excludeValues: Iterable\<T>, values: T[]): T[]
Повертає масив без вказаних значень.
```ts
without([2, 4], [1, 2, 3, 4, 5]); // → [1, 3, 5]
```
+36
View File
@@ -0,0 +1,36 @@
type Value = string | number | boolean | null | undefined | Value[] | { [key: string]: Value };
export function arrayDedup<T extends Value>(arr: T[]): T[] {
const seen = new Map<string, T>();
for (const item of arr) {
const key = stableStringify(item);
if (!seen.has(key)) {
seen.set(key, item);
}
}
return Array.from(seen.values());
}
/**
* Deterministic stringify:
* - sorts object keys
* - preserves arrays order
* - distinguishes undefined/null
*/
function stableStringify(value: Value): string {
if (value === undefined) return 'undefined';
if (value === null) return 'null';
if (Array.isArray(value)) {
return `[${value.map(stableStringify).join(',')}]`;
}
if (typeof value === 'object') {
const keys = Object.keys(value).sort();
return `{${keys.map((k) => `${k}:${stableStringify(value[k])}`).join(',')}}`;
}
return `${typeof value}:${String(value)}`;
}
@@ -0,0 +1,3 @@
export function arrayDedupPrimitives(array: Array<number | string | null | undefined>) {
return Array.from(new Set(array));
}
+3
View File
@@ -0,0 +1,3 @@
export * from './arrayDedupPrimitives';
export * from './arrayDedup';
export * from './without';
+22
View File
@@ -0,0 +1,22 @@
/**
* Return an array of values from values excluding the values present in excludeValues .
*
* @param excludeValues - An array of values to exclude from the result.
* @param values - An array of values from which to exclude the excludeValues .
* @returns An array of values from values excluding the values present in excludeValues .
*
* @example
* const exclude = [2, 4, 6];
* const values = [1, 2, 3, 4, 5, 6];
* const result = without(exclude, values);
* // result: [1, 3, 5]
*/
export function without<T>(excludeValues: Iterable<T>, values: T[]): T[] {
const excludeSet = new Set<T>(excludeValues);
const result: T[] = [];
values.forEach((value) => {
if (excludeSet.has(value)) return;
result.push(value);
});
return result;
}
+49
View File
@@ -0,0 +1,49 @@
# [←](../../Readme.md) Огляд Конвертерів
### # base64toFile(base64: string): File | null
Converts a base64 data URL (e.g. data:image/png;base64,...) into a browser File object
### # capitalizeEveryWord(str: string): string
Перетворює першу літеру кожного слова в рядку на велику
`"hello world" → "Hello World"`
### # capitalize(str: string, lowerRest = false): string
Перетворює першу літеру першого слова в рядку на велику.
Якщо `lowerRest = true`, решта рядка пишеться малими літерами.
В іншому випадку зберігається початковий регістр.
`capitalize("hELLO", true) → "Hello"`
### # numToBool(value: string | number): boolean
Перетворює числові значення на логічні:
`0` чи `"0"` → `false`
`1` чи `"1"` → `true`
Будь-яке інше ТРУШНЕ(`true`) значення → `true`
Будь-яке інше ПОЗЕРСЬКЕ(`falsy`) значення → `false`
### # snakeToCamel(snake: string): string
Перетворює `snake_case` на `camelCase`
`"hello_world"` → `"helloWorld"`
### # boolToStr(value: boolean): 'true' | 'false'
`true` -> `"true"`
`false` -> `"false"`
### # strBoolToBool(value: string): boolean
`"true"`-> `true`
`"false"`-> `false`
### # mask(cc: number | string, num?: number, symbol?: string): string
Маскує всі символи, крім останніх `num` (за замовчуванням `4`). Символ маски за замовчуванням `*`.
`mask(1234567890)` → `'******7890'`
`mask(1234567890123456, 4, 'X')` → `'XXXXXXXXXXXX3456'`
@@ -0,0 +1,41 @@
export function base64toFile(base64: string): File | null {
if (typeof base64 !== 'string') return null;
try {
if (!base64.startsWith('data:')) {
throw new Error("Invalid data URL: must start with 'data:'.");
}
const parts = base64.split(',');
if (parts.length !== 2) {
throw new Error('Invalid data URL format: missing comma separator.');
}
const header = parts[0];
const base64Data = parts[1];
const mimeMatch = header.match(/data:(.*?);base64/);
if (!mimeMatch) {
throw new Error('Invalid data URL: MIME type not found or not base64 encoded.');
}
const mime = mimeMatch[1];
let bstr: string;
try {
bstr = atob(base64Data);
} catch (e) {
throw new Error('Base64 decoding failed: data is not valid base64.');
}
const u8arr = new Uint8Array(bstr.length);
for (let i = 0; i < bstr.length; i++) {
u8arr[i] = bstr.charCodeAt(i);
}
return new File([u8arr], 'mileage-photo', { type: mime });
} catch (err) {
console.error(err);
return null;
}
}
+3
View File
@@ -0,0 +1,3 @@
export const boolToStr = (value: boolean): 'true' | 'false' => {
return value === true ? 'true' : 'false';
};
+3
View File
@@ -0,0 +1,3 @@
export function capitalize(str = '', lowerRest = false): string {
return str.slice(0, 1).toUpperCase() + (lowerRest ? str.slice(1).toLowerCase() : str.slice(1));
}
+3
View File
@@ -0,0 +1,3 @@
export function capitalizeEveryWord(str = ''): string {
return str.replace(/\b[a-z]/g, (char) => char.toUpperCase());
}
+8
View File
@@ -0,0 +1,8 @@
export * from './capitalize';
export * from './capitalizeEveryWord';
export * from './numToBool';
export * from './strToBool';
export * from './boolToStr';
export * from './base64ToFile';
export * from './snakeToCamel';
export * from './mask';
+14
View File
@@ -0,0 +1,14 @@
/**
* Replaces all but the last num of characters with the specified mask character
* @param cc - The number or string to mask.
* @param num - The number of digits to keep at the end. Default is 4.
* @param symbol - The symbol to use for masking. Default is "*".
* @returns The masked string.
* @example
* // Example usage:
* mask(1234567890123456, 4, "X"); // Output: "XXXXXXXXXXXX3456"
* mask(1234567890); // '******7890'
* mask(1234567890, 3); // '*******890'
* mask(1234567890, -4, "$"); // '$$$$567890'
*/
export const mask = (cc: number | string, num = 4, symbol = '*'): string => String(cc).slice(-num).padStart(String(cc).length, symbol);
+11
View File
@@ -0,0 +1,11 @@
export const numToBool = (value: string | number): boolean => {
if (value === 0 || value === '0') {
return false;
}
if (value === 1 || value === '1') {
return true;
}
return Boolean(value);
};
@@ -0,0 +1,5 @@
export function snakeToCamel(snake: string): string {
if (typeof snake !== 'string') return '';
return snake.replace(/_([a-z])/g, (_, c) => c.toUpperCase());
}
+11
View File
@@ -0,0 +1,11 @@
export const strBoolToBool = (value: string): boolean => {
if (value === 'true') {
return true;
}
if (value === 'false') {
return false;
}
return false;
};
+35
View File
@@ -0,0 +1,35 @@
# [←](../../Readme.md) Огляд Детекторів
### # detectUserDeviceType(widthFallback?: UserDeviceTypeWidthFallback): UserDeviceType
Визначає тип пристрою користувача: `'mobile'` | `'tablet'` | `'desktop'`.
Пріоритет визначення:
1. Modern API (`navigator.userAgentData`) — Chrome, Edge, Android
2. User-Agent — iOS (`iPhone`, `iPod`), iPadOS, Android
3. Fallback за шириною екрану (якщо передано `widthFallback`)
```ts
detectUserDeviceType(); // → 'desktop' | 'tablet' | 'mobile'
detectUserDeviceType({ mobile: 480, tablet: 1024 }); // fallback
```
### # detectUserDateInputMode(): UserDateInputModeSegment[]
Повертає порядок сегментів дати (`day`, `month`, `year`) відповідно до локалі браузера (`navigator.language`).
```ts
detectUserDateInputMode(); // → ['month', 'day', 'year'] (en-US)
detectUserDateInputMode(); // → ['day', 'month', 'year'] (uk-UA)
```
### # detectUserTimeMode(): TimeMode
Визначає формат часу користувача: `'12h'` або `'24h'` на основі локалі браузера.
```ts
detectUserTimeMode(); // → '12h' (en-US)
detectUserTimeMode(); // → '24h' (uk-UA)
```
@@ -0,0 +1,10 @@
import { UserDateInputModeSegment } from '@crux-kit/types';
export const detectUserDateInputMode = (): UserDateInputModeSegment[] => {
const format = new Intl.DateTimeFormat(navigator.language)
.formatToParts(new Date())
.map((p) => p.type)
.filter((t) => ['day', 'month', 'year'].includes(t));
return format as UserDateInputModeSegment[];
};
@@ -0,0 +1,38 @@
import { UserDeviceType } from '@crux-kit/types';
export type UserDeviceTypeWidthFallback = {
mobile: number;
tablet: number;
};
export function detectUserDeviceType(widthFallback?: UserDeviceTypeWidthFallback): UserDeviceType {
const ua = navigator.userAgent || '';
// --- Modern API first (Chrome, Edge, Android, etc.)
const uaData = (navigator as any).userAgentData;
if (uaData && uaData.mobile !== undefined) {
if (uaData.mobile) return 'mobile';
}
// --- Apple devices (iOS & iPadOS)
const isIOS = /iPhone|iPod/.test(ua);
const isIPad = /iPad/.test(ua) || (/Macintosh/.test(ua) && 'ontouchend' in document); // iPadOS masquerading as Mac
if (isIOS) return 'mobile';
if (isIPad) return 'tablet';
// --- Android
if (/android/i.test(ua)) {
return /mobile/i.test(ua) ? 'mobile' : 'tablet';
}
// --- Fallback: screen size
if (widthFallback) {
const width = window.innerWidth;
if (width <= widthFallback?.mobile) return 'mobile';
if (width <= widthFallback?.tablet) return 'tablet';
return 'desktop';
}
return 'desktop';
}
@@ -0,0 +1,9 @@
import { TimeMode } from '@crux-kit/types';
export const detectUserTimeMode = (): TimeMode => {
return new Intl.DateTimeFormat(undefined, {
hour: 'numeric',
}).resolvedOptions().hour12
? '12h'
: '24h';
};
+3
View File
@@ -0,0 +1,3 @@
export * from './detectUserDeviceType';
export * from './detectUserDateInputMode';
export * from './detectUserTimeMode';
+11
View File
@@ -0,0 +1,11 @@
# [←](../../Readme.md) Огляд DOM утиліт
### # getHeight(element: HTMLElement): number
Повертає повну висоту елемента в пікселях, включаючи `margin-top` та `margin-bottom`.
Якщо елемент не передано — повертає `0`.
```ts
getHeight(document.querySelector('.my-element')); // → 124 (height + margins)
```
+13
View File
@@ -0,0 +1,13 @@
export function getHeight(element: HTMLElement): number {
if (!element) return 0;
const styles = window.getComputedStyle(element);
const height = element.getBoundingClientRect().height;
const marginTop = parseFloat(styles.marginTop);
const marginBottom = parseFloat(styles.marginBottom);
const totalHeight = height + marginTop + marginBottom;
return totalHeight;
}
+1
View File
@@ -0,0 +1 @@
export * from './getHeight';
+32
View File
@@ -0,0 +1,32 @@
# [←](../../Readme.md) Огляд Форматерів
### # bytesToHumanReadableFormat(bytes: number, units?: string[]): string
Перетворює кількість байт на читабельний рядок з одиницями виміру.
За замовчуванням використовує поріг `1024` та одиниці: `Bytes`, `KB`, `MB`, `GB`, `TB`, `PB`, `EB`, `ZB`, `YB`.
```ts
bytesToHumanReadableFormat(500); // → '500 Bytes'
bytesToHumanReadableFormat(1536); // → '1.5 KB'
bytesToHumanReadableFormat(1048576); // → '1.0 MB'
```
Можна передати власні одиниці:
```ts
bytesToHumanReadableFormat(2048, ['Б', 'КБ', 'МБ']); // → '2.0 КБ'
```
### # numberToCompactFormat(num: number, digits?: number): string
Скорочує велике число до компактного формату з суфіксом (`K`, `M`, `G`, `T`, `P`, `E`).
`digits` — кількість знаків після коми (за замовчуванням `1`).
```ts
numberToCompactFormat(999); // → '999'
numberToCompactFormat(1500); // → '1.5K'
numberToCompactFormat(1500, 2); // → '1.50K'
numberToCompactFormat(2500000); // → '2.5M'
```
+12
View File
@@ -0,0 +1,12 @@
const thresh = 1024;
export function bytesToHumanReadableFormat(bytes: number, units: string[] = ['Bytes', 'KB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB']): string {
let bytesCopy = bytes;
if (bytesCopy < thresh) return `${bytesCopy} ${units[0]}`;
let u = -1;
do {
bytesCopy /= thresh;
++u;
} while (bytesCopy >= thresh && u + 1 < units.length - 1);
return `${bytesCopy.toFixed(1)} ${units[u + 1]}`;
}
+2
View File
@@ -0,0 +1,2 @@
export * from './bytesToHumanReadableFormat';
export * from './numberToCompactFormat';
+29
View File
@@ -0,0 +1,29 @@
/**
* Converts a number to a compact format with symbols.
* @param num - The number to convert.
* @param digits - The number of decimal places to round to. Default is 1.
* @returns The number in compact format with symbols.
* @example
* // Example usage:
* const compactNumber = numberToCompactFormat(1500, 2);
* console.log(compactNumber); // Output: "1.50K"
*/
export const numberToCompactFormat = (num: number, digits = 1): string => {
const lookup = [
{ value: 1, symbol: '' },
{ value: 1e3, symbol: 'K' },
{ value: 1e6, symbol: 'M' },
{ value: 1e9, symbol: 'G' },
{ value: 1e12, symbol: 'T' },
{ value: 1e15, symbol: 'P' },
{ value: 1e18, symbol: 'E' },
];
const rx = /\.0+$|(?<temp1>\.[0-9]*[1-9])0+$/;
const item = lookup.reverse().find((entry) => {
return num >= entry.value;
});
return item ? (num / item.value).toFixed(digits).replace(rx, '$1') + item.symbol : '0';
};
+41
View File
@@ -0,0 +1,41 @@
# [←](../../Readme.md) Огляд Хелперів
### # downloadFileByLink(href: string, name?: string, target?: string): void
Програмно завантажує файл за URL, створюючи тимчасовий `<a>` елемент і клікаючи по ньому.
```ts
downloadFileByLink('https://example.com/file.pdf', 'report'); // завантажує як 'report'
```
---
### # sleep(ms: number): Promise\<void>
Затримує виконання на задану кількість мілісекунд.
```ts
await sleep(1000); // чекає 1 секунду
```
---
### # throttle(func, limit: number): Function
Обмежує частоту виклику функції — не частіше ніж раз на `limit` мс.
```ts
const throttled = throttle(onScroll, 200);
window.addEventListener('scroll', throttled);
```
---
### # debounce\<T>(func: T, wait: number): Function
Відкладає виклик функції до закінчення `wait` мс після останнього виклику.
```ts
const debounced = debounce(onSearch, 300);
input.addEventListener('input', debounced);
```
+22
View File
@@ -0,0 +1,22 @@
/// <reference lib="dom" />
/**
* Downloads a file by providing a link.
* @param href - The URL of the file to download.
* @param name - The name of the downloaded file. Default is "file".
* @param target - The target window or frame for the downloaded file. Default is "_blank".
* @example
* // Example usage:
* downloadFileByLink("https://example.com/file.pdf", "myFile", "_self");
*/
export const downloadFileByLink = (href: string, name = 'file', target = '_blank'): void => {
const link = document.createElement('a');
link.href = href;
link.download = name;
link.target = target;
link.style.visibility = 'hidden';
link.setAttribute('target', '_blank');
document.body.appendChild(link);
link.click();
document.body.removeChild(link);
};
+3
View File
@@ -0,0 +1,3 @@
export * from './downloadFileByLink';
export * from './throttle-debounce';
export * from './sleep';
+4
View File
@@ -0,0 +1,4 @@
export const sleep = (ms: number): Promise<void> =>
new Promise((resolve) => {
setTimeout(resolve, ms);
});
@@ -0,0 +1,38 @@
export const throttle = (func: (...args: unknown[]) => void, limit: number): ((...args: unknown[]) => void) => {
let lastFunc: ReturnType<typeof setTimeout> | null = null;
let lastRan: number | null = null;
return function (this: unknown, ...args: unknown[]) {
if (lastRan === null) {
func.apply(this, args);
lastRan = Date.now();
} else {
if (lastFunc !== null) {
clearTimeout(lastFunc);
}
lastFunc = setTimeout(
() => {
if (Date.now() - (lastRan as number) >= limit) {
func.apply(this, args);
lastRan = Date.now();
}
},
limit - (Date.now() - (lastRan as number)),
);
}
};
};
export function debounce<T extends (...args: unknown[]) => unknown>(func: T, wait: number): (...args: Parameters<T>) => void {
let timeout: NodeJS.Timeout | null = null;
return function (...args: Parameters<T>): void {
if (timeout) {
clearTimeout(timeout);
}
timeout = setTimeout(() => {
func(...args);
}, wait);
};
}
+11
View File
@@ -0,0 +1,11 @@
export * from './helpers';
export * from './random';
export * from './converters';
export * from './formaters';
export * from './detectors';
export * from './tailwind';
export * from './dom';
export * from './array';
export * from './object';
export * from './is.ts';
+80
View File
@@ -0,0 +1,80 @@
export const isUndef = (o: unknown): o is undefined | void => typeof o === 'undefined' || o === undefined;
export const isNull = (o: unknown): o is null => o === null;
export const isBool = (o: unknown): o is boolean => typeof o === 'boolean';
export const isNil = (o: unknown): o is null | undefined | void => isNull(o) || isUndef(o);
export function isStr<T extends string = string>(o: unknown): o is T {
return typeof o === 'string';
}
export const isNum = (o: unknown, isStringAllowed = false): o is number => {
const isNumType = typeof o === 'number';
if (!isStringAllowed) return isNumType;
return isNumType || (typeof o === 'string' && !isNaN(Number(o)) && o.trim() !== '');
};
export const isNumStr = (o: unknown): o is number | string => isNum(o, true);
export const isEven = (num: number): boolean => num % 2 === 0;
export const isOdd = (num: number): boolean => num % 2 !== 0;
type Func = (...args: any[]) => any;
export function isFunc<T extends Func = Func>(o: unknown): o is T {
return !(isNull(o) || isUndef(o)) && typeof o === 'function';
}
export function isArr<T extends unknown[] = unknown[]>(value: unknown): value is T {
return Array.isArray(value) as boolean;
}
export function isPureObj<T extends Record<string, any> = Record<string, any>>(o: unknown): o is T {
return !(isNull(o) || isUndef(o)) && typeof o === 'object' && !isArr(o) && !(o instanceof Map) && !(o instanceof Set);
}
/**
* Check if two objects are deeply equal.
*
* @param objA - The first object to compare.
* @param objB - The second object to compare.
* @returns A boolean indicating whether the objects are deeply equal.
*
* @example
* const obj1 = { a: 1, b: { c: 2 } };
* const obj2 = { a: 1, b: { c: 2 } };
* const isEqual = isDeepEqual(obj1, obj2);
* // isEqual: true
*/
export function isDeepEqual(objA: any, objB: any): boolean {
if (Object.is(objA, objB)) return true;
if (typeof objA !== 'object' || objA === null || typeof objB !== 'object' || objB === null) return false;
if (objA.constructor !== objB.constructor) return false;
const keysA = Object.keys(objA);
const keysB = Object.keys(objB);
if (keysA.length !== keysB.length) return false;
const bHasOwnProperty = global.hasOwnProperty.bind(objB);
while (keysA.length > 0) {
const key: any = keysA.pop();
if (!bHasOwnProperty(key)) return false;
const a: unknown = objA[key];
const b: unknown = objB[key];
if (!Object.is(a, b)) {
if (typeof a !== 'object' || typeof b !== 'object' || a === null || b === null) return false;
if (!isDeepEqual(a, b)) return false;
}
}
return true;
}
+29
View File
@@ -0,0 +1,29 @@
# [←](../../Readme.md) Огляд Object утиліт
### # keys\<T extends object>(object: T): (keyof T)[]
Типізована обгортка над `Object.keys` — повертає масив ключів зі збереженням типу.
```ts
keys({ a: 1, b: 2 }); // → ['a', 'b']
```
---
### # pick(names: string[], obj: Record\<string, unknown>): object
Повертає новий об'єкт лише з вказаними ключами.
```ts
pick(['a', 'c'], { a: 1, b: 2, c: 3 }); // → { a: 1, c: 3 }
```
---
### # omit(excludeObjectKeys: string[], obj: Record\<string, unknown>): object
Повертає новий об'єкт без вказаних ключів.
```ts
omit(['b', 'c'], { a: 1, b: 2, c: 3 }); // → { a: 1 }
```
+3
View File
@@ -0,0 +1,3 @@
export * from './keys';
export * from './pick';
export * from './omit';
+14
View File
@@ -0,0 +1,14 @@
/**
* Return an array of keys from an object.
*
* @param object - The object from which to extract the keys.
* @returns An array of keys from the object.
*
* @example
* const obj = { a: 1, b: 2, c: 3 };
* const result = keys(obj);
* // result: ['a', 'b', 'c']
*/
export function keys<T extends object>(object: T): (keyof T)[] {
return Object.keys(object) as (keyof T)[];
}
+30
View File
@@ -0,0 +1,30 @@
/**
* Omit specified keys from an object.
*
* @param excludeObjectKeys - An array of keys to exclude from the object.
* @param obj - The object from which to omit the keys.
* @returns The new object with the specified keys omitted.
*
* @example
* const input = { a: 1, b: 2, c: 3 };
* const excludedKeys = ['b', 'c'];
* const output = omit(excludedKeys, input);
* // output: { a: 1 }
*/
export function omit(excludeObjectKeys: string[], obj: Record<string, unknown>): object {
const result: Record<string, unknown> = {};
const index: Record<string, unknown> = {};
let idx = 0;
const length = excludeObjectKeys.length;
while (idx < length) {
index[excludeObjectKeys[idx]] = 1;
idx += 1;
}
for (const prop in obj) {
if (!Object.prototype.hasOwnProperty.call(index, prop)) result[prop] = obj[prop];
}
return result;
}
+24
View File
@@ -0,0 +1,24 @@
/**
* Pick specified keys from an object.
*
* @param names - An array of keys to pick from the object.
* @param obj - The object from which to pick the keys.
* @returns The new object with the specified keys picked.
*
* @example
* const input = { a: 1, b: 2, c: 3 };
* const pickedKeys = ['a', 'c'];
* const output = pick(pickedKeys, input);
* // output: { a: 1, c: 3 }
*/
export function pick(names: string[], obj: Record<string, unknown>): object {
const result: Record<string, unknown> = {};
let idx = 0;
while (idx < names.length) {
if (names[idx] in obj) result[names[idx]] = obj[names[idx]];
idx += 1;
}
return result;
}
+30
View File
@@ -0,0 +1,30 @@
# [←](../../Readme.md) Огляд Random утиліт
### # randomNumber(min?: number, max?: number): number
Повертає випадкове число з діапазону `[min, max]`. За замовчуванням `[0, 1]`.
```ts
randomNumber(1, 10); // → 7.42...
randomNumber(); // → 0.38...
```
---
### # randomKey(): string
Генерує випадковий рядковий ключ з префіксом `random-`.
```ts
randomKey(); // → 'random-k7f2h9x1q'
```
---
### # uid(): string
Генерує простий унікальний ідентифікатор на основі `Date.now()` та випадкового числа.
```ts
uid(); // → '17160394821573'
```
+3
View File
@@ -0,0 +1,3 @@
export * from './randomKey';
export * from './randomNumber';
export * from './uid';
+3
View File
@@ -0,0 +1,3 @@
export const randomKey: () => string = () => {
return `random-${Math.random().toString(36).slice(2, 11)}`;
};
+3
View File
@@ -0,0 +1,3 @@
export function randomNumber(min = 0, max = 1): number {
return Math.random() * (max - min) + min;
}
+3
View File
@@ -0,0 +1,3 @@
export function uid(): string {
return (Date.now() + Math.floor(Math.random() * 1000)).toString();
}
+12
View File
@@ -0,0 +1,12 @@
import { clsx, type ClassValue } from 'clsx';
import { twMerge } from 'tailwind-merge';
/**
* Merges Tailwind class names, resolving any conflicts.
*
* @param inputs - An array of class names to merge.
* @returns A string of merged and optimized class names.
*/
export function cn(...inputs: ClassValue[]): string {
return twMerge(clsx(inputs));
}
+1
View File
@@ -0,0 +1 @@
export * from './cn';