@taiga-ui/cdk
Version:
Base library for creating Angular components and applications using Taiga UI principles regarding of actual visual appearance
236 lines (212 loc) • 7.7 kB
JavaScript
import { FormArray, FormGroup } from '@angular/forms';
import { tuiToInt } from '@taiga-ui/cdk/utils/math';
import { SIGNAL } from '@angular/core/primitives/signals';
import { InjectionToken, inject, DestroyRef, EnvironmentInjector, createComponent } from '@angular/core';
function tuiArrayRemove(array, index) {
return array.slice(0, Math.max(index, 0)).concat(array.slice(Math.max(index + 1, 0)));
}
function tuiArrayShallowEquals(a, b) {
return a.length === b.length && a.every((item, index) => Object.is(item, b[index]));
}
function tuiArrayToggle(array, item, identity) {
const index = identity
? array.findIndex((it) => identity(it, item))
: array.indexOf(item);
return index === -1 ? [...array, item] : tuiArrayRemove(array, index);
}
function tuiIsControlEmpty({ value = null }) {
return value === null || value === '' || (Array.isArray(value) && !value.length);
}
function tuiCountFilledControls(control) {
if (control instanceof FormArray) {
return control.controls.reduce((acc, nestedControl) => acc + tuiCountFilledControls(nestedControl), 0);
}
return control instanceof FormGroup
? Object.values(control.controls).reduce((acc, nestedControl) => acc + tuiCountFilledControls(nestedControl), 0)
: tuiToInt(!tuiIsControlEmpty(control));
}
function tuiIsString(value) {
return typeof value === 'string';
}
function tuiDefaultSort(x, y) {
if (x === y) {
return 0;
}
if (tuiIsString(x) && tuiIsString(y)) {
return x.localeCompare(y, undefined, { numeric: true });
}
return x > y ? 1 : -1;
}
function tuiDistanceBetweenTouches({ touches }) {
return Math.hypot((touches[0]?.clientX ?? 0) - (touches[1]?.clientX ?? 0), (touches[0]?.clientY ?? 0) - (touches[1]?.clientY ?? 0));
}
function tuiEaseInOutQuad(t) {
ngDevMode &&
console.assert(t >= 0 && t <= 1, 'Input must be between 0 and 1 inclusive but received ', t);
return t < 0.5 ? 2 * t * t : -1 + (4 - 2 * t) * t;
}
let autoId = 0;
function tuiGenerateId() {
return `tui_${autoId++}${Date.now().toString(36)}`;
}
function tuiIsFlat(items) {
return !Array.isArray(items[0]);
}
function tuiIsNumber(value) {
return typeof value === 'number';
}
function tuiIsObject(value) {
return typeof value === 'object' && !!value;
}
function tuiIsPresent(value) {
return value != null;
}
function tuiMarkControlAsTouchedAndValidate(control) {
if (control instanceof FormArray) {
control.controls.forEach((nestedControl) => {
tuiMarkControlAsTouchedAndValidate(nestedControl);
});
}
if (control instanceof FormGroup) {
Object.values(control.controls).forEach((nestedControl) => {
tuiMarkControlAsTouchedAndValidate(nestedControl);
});
}
control.markAsTouched();
control.updateValueAndValidity();
}
/**
* Checks identity for nullable elements.
*
* @param a element a
* @param b element b
* @param handler called if both elements are not null
* @return true if either both are null or they pass identity handler
*/
function tuiNullableSame(a, b, handler) {
if (a === null) {
return b === null;
}
return b === null ? false : handler(a, b);
}
/**
* Obfuscates a string by replacing certain characters with a symbol.
*
* @param value the input string to obfuscate
* @param symbol the symbol for obfuscation
* @return the obfuscated string
*
* The function determines which characters to obfuscate using a regular expression and the string's length:
* - 8 or more: show first 2 and last 2 characters
* - 4 to 7: show first and last character
* - less than 4: obfuscate all characters
* - obfuscates only alphanumeric characters
*/
function tuiObfuscate(value, symbol) {
if (!value) {
return value;
}
const match = /[\p{L}\p{N}]/gu;
let visible = 0;
let obfuscateIndexes = getObfuscateIndexes(value, match);
if (obfuscateIndexes.length >= 8) {
visible = 2;
}
else if (obfuscateIndexes.length >= 4) {
visible = 1;
}
obfuscateIndexes = obfuscateIndexes.slice(visible, obfuscateIndexes.length);
const lastIndex = obfuscateIndexes.length - visible;
obfuscateIndexes = obfuscateIndexes.slice(0, lastIndex < 0 ? 0 : lastIndex);
const result = value.split('');
obfuscateIndexes.forEach((index) => {
result[index] = symbol;
});
return result.join('');
}
function getObfuscateIndexes(value, match) {
if (!match) {
return Array.from({ length: value.length }).map((_, index) => index);
}
const obfuscateIndexes = [];
let matchResult;
let count = 0;
while ((matchResult = match.exec(value)) !== null && count < value.length) {
const start = matchResult.index;
const end = match.lastIndex - 1;
for (let i = start; i <= end; i++) {
obfuscateIndexes.push(i);
}
count++;
}
return obfuscateIndexes;
}
/**
* Adds 'px' to the number and turns it into a string
*/
function tuiPx(value) {
ngDevMode && console.assert(Number.isFinite(value), 'Value must be finite number');
return `${value}px`;
}
/**
* Sanitizes pasted or dropped text by removing invisible and control characters
* that can appear in clipboard or drag-and-drop data.
*
* Removes:
* 1. ASCII control characters (0x00–0x1F) — non-printable characters like
* NULL, BEL, BS, CR, LF, TAB, ESC, etc.
* 2. Extended control characters (0x7F–0x9F) — C1 control codes from ISO-8859-1,
* often found in legacy or malformed text encodings.
* 3. Zero-width and special Unicode characters:
* - U+200B ZERO WIDTH SPACE
* - U+200C ZERO WIDTH NON-JOINER
* - U+200D ZERO WIDTH JOINER
* - U+FEFF BYTE ORDER MARK (BOM)
*
* @param {string} value - The input string from clipboard or drag event.
* @returns {string} - A cleaned string without control or invisible characters.
*
* @example
* tuiSanitizeText('Hello\u0000World\u200B!');
* // → "HelloWorld!"
*/
function tuiSanitizeText(value) {
// eslint-disable-next-line no-control-regex
const controlCharsRegex = /[\x00-\x1F\x7F-\x9F]/g;
const zeroWidthCharsRegex = /[\u200B-\u200D\uFEFF]/g;
return value
.trim()
.replaceAll(controlCharsRegex, '')
.replaceAll(zeroWidthCharsRegex, '');
}
function tuiSetSignal(signal, value) {
if ('set' in signal) {
signal.set(value);
}
else if ('applyValueToInputSignal' in signal[SIGNAL]) {
signal[SIGNAL].applyValueToInputSignal(signal[SIGNAL], value);
}
else {
ngDevMode && console.assert(false, 'tuiSetSignal was called on read-only signal');
}
}
const MAP = new InjectionToken(ngDevMode ? 'MAP' : '', {
factory: () => {
const map = new Map();
inject(DestroyRef).onDestroy(() => map.forEach((component) => component.destroy()));
return map;
},
});
function tuiWithStyles(component) {
const map = inject(MAP);
const environmentInjector = inject(EnvironmentInjector);
if (!map.has(component)) {
map.set(component, createComponent(component, { environmentInjector }));
}
return;
}
/**
* Generated bundle index. Do not edit.
*/
export { tuiArrayRemove, tuiArrayShallowEquals, tuiArrayToggle, tuiCountFilledControls, tuiDefaultSort, tuiDistanceBetweenTouches, tuiEaseInOutQuad, tuiGenerateId, tuiIsControlEmpty, tuiIsFlat, tuiIsNumber, tuiIsObject, tuiIsPresent, tuiIsString, tuiMarkControlAsTouchedAndValidate, tuiNullableSame, tuiObfuscate, tuiPx, tuiSanitizeText, tuiSetSignal, tuiWithStyles };
//# sourceMappingURL=taiga-ui-cdk-utils-miscellaneous.mjs.map