/
vshmidt
/
label-studio
Обзор
Документация
Войти
/
vshmidt
/
label-studio
Код
Запросы
0
Задачи
Вики
Пакеты
0
Релизы
0
Аналитика
Безопасность
develop
web/libs/editor/src/utils/utilities.ts
275 строк
7 KB
Yousif Yassi
fix: FIT-720: Avoid loading all annotations in LabelStream when the user is admin and goes to previous tasks (#9359)
17 фев 2026, 22:05
Не верифицирован
17 фев 2026, 22:05
3f25dc3
Код
Авторство
О чём код?
import { formatDistanceToNow } from "date-fns"; import { destroy, detach } from "mobx-state-tree"; import { camelCase, snakeCase } from "@humansignal/core/lib/utils/string"; /** * Internal helper to check if parameter is a string * @param {*} value * @returns {boolean} */ export const isString = (value: any): value is string => { return typeof value === "string" || value instanceof String; }; /** * Internal helper to check if string is empty * @param {*} value * @returns {boolean} */ export const isStringEmpty = (value: string) => { if (!isString(value)) { return false; } return value.length === 0; }; /** * Internal helper to check if string is JSON * @param {string} value * @returns {boolean} */ export const isStringJSON = (value: string) => { if (isString(value)) { try { JSON.parse(value); } catch (e) { return false; } return true; } return false; }; /** * Check if text is url * @param {*} i * @param {*} text */ export function getUrl(i: number, text: string) { const stringToTest = text.slice(i); const myRegexp = /^(https?:\/\/(?:www\.|(?!www))[^\s.]+\.[^\s]{2,}|www\.[^\s]+\.[^\s]{2,})/g; // eslint-disable-line no-useless-escape const match = myRegexp.exec(stringToTest); return match && match.length ? match[1] : ""; } /** * Check if given string is a valid url for object data * @param {string} str - String to check * @param {boolean} [relative=true] - Whether relative urls are good or nood */ export function isValidObjectURL(str: string, relative = false) { if (typeof str !== "string") return false; if (relative && str.startsWith("/")) return true; return /^https?:\/\//.test(str); } /** * Convert MS to Time String * Example: 2000 -> 00:00:02 * @param {number} ms * @returns {string} */ export function toTimeString(ms: number) { if (typeof ms === "number") { return new Date(ms).toUTCString().match(/(\d\d:\d\d:\d\d)/)?.[0]; } } export function flatten(arr: any[]): any[] { return arr.reduce<any>( (flat, toFlatten) => flat.concat(Array.isArray(toFlatten) ? flatten(toFlatten) : toFlatten), [], ); } export function hashCode(str: string) { let hash = 0; if (str.length === 0) { return `${hash}`; } for (let i = 0; i < str.length; i++) { const char = str.charCodeAt(i); hash = (hash << 5) - hash + char; hash = hash & hash; // Convert to 32bit integer } return `${hash}`; } export function atobUnicode(str: string) { // Going backwards: from bytestream, to percent-encoding, to original string. return decodeURIComponent( atob(str) .split("") .map((c) => `%${`00${c.charCodeAt(0).toString(16)}`.slice(-2)}`) .join(""), ); } /** * Makes string safe to use inside dangerouslySetInnerHTML * @param {string} unsafe */ export function escapeHtml(unsafe: string) { return (unsafe ?? "") .replace(/&/g, "&") .replace(/</g, "<") .replace(/>/g, ">") .replace(/"/g, """) .replace(/'/g, "'"); } /** * Compares two arrays; order matters * @template T * @param {T[]} arr1 array 1 * @param {T[]} arr2 array 2 */ export function isArraysEqual(arr1: any[], arr2: any[]) { return arr1.length === arr2.length && arr1.every((value, index) => arr2[index] === value); } /** * Convert any value to an array * @template T * @param {T} value * @returns {T[]} */ export function wrapArray(value: any[]) { return ([] as any[]).concat(...[value]); } /** * If given one element, wrap it in an array. Removes missing items. Returns empty array for undefined. * @template T * @param {T | T[]} arg * @returns {T[]} **/ export function toArray<T>(arg: undefined | T | (T | undefined)[]): T[] { return (Array.isArray(arg) ? arg : [arg]).filter((v) => v !== undefined); } export function delay(ms = 0) { return new Promise((resolve) => setTimeout(resolve, ms)); } export const isDefined = <T>(value: T | null | undefined): value is T => { return value !== null && value !== undefined; }; type ClosestParentPredicate<T> = (el: T) => boolean; type ClosestParentGetter<T> = (el: T) => T; export function findClosestParent<T extends { parent: any }>( el: T, predicate: ClosestParentPredicate<T> = () => true, parentGetter: ClosestParentGetter<T> = (el) => el.parent, ) { while ((el = parentGetter(el))) { if (predicate(el)) { return el; } } return null; } export function clamp(x: number, min: number, max: number) { return Math.min(max, Math.max(min, x)); } export const chunks = <T extends any[]>(source: T, chunkSize: number): T[][] => { const result = []; let i; let j; for (i = 0, j = source.length; i < j; i += chunkSize) { result.push(source.slice(i, i + chunkSize)); } return result; }; /** * This name supposed to be username, but most likely it's first_name and last_name * @param {string} createdBy string like "[<name> ]<email>, <id>" * @returns {string} email */ export const emailFromCreatedBy = (createdBy: string) => { // get the email followed by id and cut off the id return createdBy?.match(/([^@,\s]+@[^@,\s]+)(,\s*\d+)?$/)?.[1]; }; export const camelizeKeys = (object: any): Record<string, unknown> => { return Object.fromEntries( Object.entries(object).map(([key, value]) => { if (Object.prototype.toString.call(value) === "[object Object]") { return [camelCase(key), camelizeKeys(value)]; } return [camelCase(key), value]; }), ); }; export const snakeizeKeys = (object: any): Record<string, unknown> => { return Object.fromEntries( Object.entries(object).map(([key, value]) => { if (Object.prototype.toString.call(value) === "[object Object]") { return [snakeCase(key), snakeizeKeys(value)]; } return [snakeCase(key), value]; }), ); }; export function minMax(items: number[]) { return items.reduce<number[]>((acc, val) => { acc[0] = acc[0] === undefined || val < acc[0] ? val : acc[0]; acc[1] = acc[1] === undefined || val > acc[1] ? val : acc[1]; return acc; }, []); } // Detects if current OS is macOS export function isMacOS() { return navigator.platform.indexOf("Mac") > -1; } export const triggerResizeEvent = () => { const event = new Event("resize"); event.initEvent("resize", false, false); window.dispatchEvent(event); }; export const humanDateDiff = (date: string | number): string => { const fnsDate = formatDistanceToNow(new Date(date), { addSuffix: true }); if (fnsDate === "less than a minute ago") return "just now"; return fnsDate; }; export const destroyMSTObject = (object: any) => { if (object) { detach(object); destroy(object); } }; // fixes `observe` - it watches only the changes of primitive props of observables used, // so pass all the required primitives to this stub and they'll be observed export const fixMobxObserve = (..._toObserve: any[]) => {}; /** * Sort annotations by createdDate in place. This function mutates the input array, so don't pass original list. * Use the same ordering in different places to keep it consistent. Just sort to have the latest first. * @param {object[]} annotations * @returns {object[]} sorted list of annotations */ export const sortAnnotations = (annotations: any[]) => { return annotations.sort((a, b) => new Date(b.createdDate).getTime() - new Date(a.createdDate).getTime()); };