UNPKG

@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
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