Portal/rsconcept/frontend/src/utils/utils.ts
Ivan 00934d5716
Some checks failed
Backend CI / build (3.12) (push) Has been cancelled
Frontend CI / build (22.x) (push) Has been cancelled
B: Fix oss filtering and error messages
2024-08-18 12:55:41 +03:00

161 lines
4.1 KiB
TypeScript

/**
* Module: Utility functions.
*/
import axios, { AxiosError, AxiosHeaderValue, AxiosResponse } from 'axios';
import { prompts } from './labels';
/**
* Checks if arguments is Node.
*/
export function assertIsNode(e: EventTarget | null): asserts e is Node {
if (e === null || !('nodeType' in e)) {
throw new TypeError(`Expected 'Node' but received '${e?.constructor.name ?? 'null'}'`);
}
}
/**
* Wrapper class for generalized text matching.
*
* If possible create regexp, otherwise use symbol matching.
*/
export class TextMatcher {
protected query: RegExp | string;
constructor(query: string, isPlainText?: boolean, isCaseSensitive?: boolean) {
if (isPlainText) {
query = query.replace(/[-[\]{}()*+?.,\\^$|#\s]/g, '\\$&');
}
try {
this.query = new RegExp(query, isCaseSensitive ? '' : 'i');
} catch (_exception: unknown) {
this.query = query;
}
}
test(text: string): boolean {
if (typeof this.query === 'string') {
return text.includes(this.query);
} else {
return !!text.match(this.query);
}
}
}
/**
* Text substitution guided by mapping and regular expression.
*/
export function applyPattern(text: string, mapping: Record<string, string>, pattern: RegExp): string {
if (text === '' || pattern === null) {
return text;
}
let posInput = 0;
let output = '';
const patternMatches = text.matchAll(pattern);
for (const segment of patternMatches) {
const entity = segment[0];
const start = segment.index ?? 0;
if (entity in mapping) {
output += text.substring(posInput, start);
output += mapping[entity];
posInput = start + segment[0].length;
}
}
output += text.substring(posInput);
return output;
}
/**
* Truncate text to max symbols. Add ellipsis if truncated.
*/
export function truncateText(text: string, maxSymbols: number): string {
if (text.length <= maxSymbols) {
return text;
}
const trimmedText = text.slice(0, maxSymbols);
const lastSpaceIndex = trimmedText.lastIndexOf(' ');
if (lastSpaceIndex === -1) {
return trimmedText + '...';
}
return trimmedText.slice(0, lastSpaceIndex) + '...';
}
/**
* Check if Axios response is html.
*/
export function isResponseHtml(response?: AxiosResponse) {
if (!response) {
return false;
}
const header = response.headers['content-type'] as AxiosHeaderValue;
if (!header) {
return false;
}
if (typeof header === 'number' || typeof header === 'boolean') {
return false;
}
// eslint-disable-next-line @typescript-eslint/no-unsafe-return, @typescript-eslint/no-unsafe-call
return header.includes('text/html');
}
/**
* Convert base64 string to Blob uint8.
*/
export function convertBase64ToBlob(base64String: string): Uint8Array {
const arr = base64String.split(',');
const bstr = atob(arr[1]);
let n = bstr.length;
const uint8Array = new Uint8Array(n);
while (n--) {
uint8Array[n] = bstr.charCodeAt(n);
}
return uint8Array;
}
/**
* Prompt user of confirming discarding changes before continue.
*/
export function promptUnsaved(): boolean {
return window.confirm(prompts.promptUnsaved);
}
/**
* Toggle tristate flag: undefined - true - false.
*/
export function toggleTristateFlag(prev: boolean | undefined): boolean | undefined {
if (prev === undefined) {
return true;
}
return prev ? false : undefined;
}
/**
* Toggle tristate color: gray - green - red .
*/
export function tripleToggleColor(value: boolean | undefined): string {
if (value === undefined) {
return 'clr-text-controls';
}
return value ? 'clr-text-green' : 'clr-text-red';
}
/**
* Extract error message from error object.
*/
export function extractErrorMessage(error: Error | AxiosError): string {
if (axios.isAxiosError(error)) {
if (error.response && error.response.status === 400) {
const data = error.response.data as Record<string, unknown>;
const keys = Object.keys(data);
if (keys.length === 1) {
const value = data[keys[0]];
if (typeof value === 'string') {
return `${keys[0]}: ${value}`;
}
}
}
}
return error.message;
}