All files / src utils.tsx

100% Statements 47/47
92.3% Branches 24/26
100% Functions 12/12
100% Lines 46/46

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110            82x                   14x         45x 45x 90x 90x 90x 36x       36x 30x   54x 43x     45x           6x 36x   90x     90x 36x 12x   36x             15x 3x   15x 30x 12x           31x       31x 1x   30x 30x 30x 60x         20x 20x 1x   19x 19x 19x 19x       19x     19x 19x     14x 14x   14x 7x  
import { type AutocompleteItemType } from './components/autocomplete-item';
 
export function getLastIndexOfSubstringIgnoreCase(
  string: string,
  substring: string
) {
  return string.toLowerCase().lastIndexOf(substring.toLowerCase());
}
 
export type BasicItem<Item> = {
  label?: string;
  id: string;
  tags?: string[];
  items?: BasicItem<Item>[];
};
 
export const getNodePaths = <Item extends BasicItem<Item>>(
  items: Item[],
  id?: string,
  path: Item[] = []
) => {
  let nodePaths: Omit<Item, 'items'>[][] = [];
  items.forEach((node) => {
    const { items, ...thisNode } = node;
    const nodePath = [...path, thisNode];
    if (items) {
      const result = getNodePaths(items, id, nodePath) as Omit<
        Item,
        'items'
      >[][];
      if (result.length) {
        nodePaths = [...nodePaths, ...result];
      }
    } else if (!id || thisNode.id === id) {
      nodePaths = [...nodePaths, nodePath];
    }
  });
  return nodePaths;
};
 
export function prepareTreeDataForAutocomplete<Item extends BasicItem<Item>>(
  flattenedTreeData: Item[][]
) {
  return flattenedTreeData.map((items) => {
    const autocompleteItem: AutocompleteItemType = {
      id: items[items.length - 1].id,
      pathLabel: items.map((item) => item.label).join(' / '),
      itemLabel: items[items.length - 1].label || items[items.length - 1].id,
    };
    const tags = items.flatMap((item) => item.tags || []);
    if (tags.length) {
      autocompleteItem.tags = tags;
    }
    return autocompleteItem;
  });
}
 
export function* getSingleChildren<Item extends BasicItem<Item>>(
  children: Item[]
): Generator<string, void, never> {
  if (children.length === 1) {
    yield children[0].id;
  }
  for (const child of children) {
    if (child.items) {
      yield* getSingleChildren(child.items);
    }
  }
}
 
export function formatLargeNumber(x: string | number) {
  const string = x.toString();
 
  // If number is converted to scientific notation return as is
  // because commas can't be added as the coefficient will be < 10
  if (string.includes('e')) {
    return string;
  }
  const [integer, decimal] = x.toString().split('.');
  const integerWithCommas = integer.replace(/\B(?=(\d{3})+(?!\d))/g, ',');
  return [integerWithCommas, decimal]
    .filter((el) => typeof el !== 'undefined')
    .join('.');
}
 
export function formatBytesNumber(bytes: string | number, decimals = 0) {
  const bytesNumber = +bytes;
  if (!bytesNumber) {
    return '0 Bytes';
  }
  const positiveDecimals = decimals < 0 ? 0 : decimals;
  const baseFactor = 1024;
  const units = ['Bytes', 'KB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB'];
  const unitsIndex = Math.min(
    Math.floor(Math.log(bytesNumber) / Math.log(baseFactor)),
    units.length - 1
  );
  const number = (bytesNumber / baseFactor ** unitsIndex).toFixed(
    positiveDecimals
  );
  const unit = units[unitsIndex];
  return `${formatLargeNumber(parseFloat(number))} ${unit}`;
}
 
const reProtocol = /^(https?:)?(\/\/)?/;
const reTrailingSlashes = /(\/+$)/;
 
export const tidyUrlString = (url: string) =>
  url.replace(reProtocol, '').replace(reTrailingSlashes, '');