# 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, '');