All files / web/src/arcade-games/know-your-world/utils regionSizeUtils.ts

82.19% Statements 120/146
100% Branches 10/10
36.36% Functions 4/11
82.19% Lines 120/146

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 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 1471x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x                       1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 12x 12x 12x 12x 12x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 11x 11x 11x 11x 1x 1x 1x 1x 1x 1x 4x 4x 4x 4x 4x 4x 1x 1x 1x 1x 1x 1x 5x 5x 5x 5x 5x 5x 5x 5x 5x 5x 15x 6x 6x 15x 5x 5x 14x 5x 5x 14x 5x 5x 5x 1x 1x 1x 1x 1x 1x       1x 1x 1x 1x 1x 1x         1x 1x 1x 1x 1x     1x 1x 1x 1x 1x     1x 1x 1x 1x 1x     1x 1x 1x 1x 1x      
/**
 * Utility functions for converting between region filter arrays and min/max ranges.
 * Used by the DrillDownMapSelector's RangeThermometer component.
 * Supports size, importance, and population filter criteria.
 */
 
import type { RegionSize, ImportanceLevel, PopulationLevel, FilterCriteria } from '../maps'
import { ALL_REGION_SIZES, ALL_IMPORTANCE_LEVELS, ALL_POPULATION_LEVELS } from '../maps'
 
/**
 * Generic type for all filter level types
 */
export type FilterLevel = RegionSize | ImportanceLevel | PopulationLevel
 
/**
 * Get the appropriate "ALL_*_LEVELS" array for a given filter criteria
 */
export function getAllLevelsForCriteria<T extends FilterLevel>(criteria: FilterCriteria): T[] {
  switch (criteria) {
    case 'size':
      return ALL_REGION_SIZES as T[]
    case 'importance':
      return ALL_IMPORTANCE_LEVELS as T[]
    case 'population':
      return ALL_POPULATION_LEVELS as T[]
    default:
      return ALL_REGION_SIZES as T[]
  }
}
 
/**
 * Convert an array of sizes to min/max values for the range thermometer.
 * Sorts the sizes by their position in ALL_REGION_SIZES and returns the first and last.
 *
 * @example
 * sizesToRange(['medium', 'small', 'large']) // returns ['large', 'small']
 * sizesToRange(['medium']) // returns ['medium', 'medium']
 */
export function sizesToRange(sizes: RegionSize[]): [RegionSize, RegionSize] {
  const sorted = [...sizes].sort(
    (a, b) => ALL_REGION_SIZES.indexOf(a) - ALL_REGION_SIZES.indexOf(b)
  )
  return [sorted[0], sorted[sorted.length - 1]]
}
 
/**
 * Convert min/max range values back to an array of sizes.
 * Returns all sizes between min and max (inclusive) in ALL_REGION_SIZES order.
 *
 * @example
 * rangeToSizes('large', 'small') // returns ['large', 'medium', 'small']
 * rangeToSizes('medium', 'medium') // returns ['medium']
 */
export function rangeToSizes(min: RegionSize, max: RegionSize): RegionSize[] {
  const minIdx = ALL_REGION_SIZES.indexOf(min)
  const maxIdx = ALL_REGION_SIZES.indexOf(max)
  return ALL_REGION_SIZES.slice(minIdx, maxIdx + 1)
}
 
/**
 * Calculate which regions are excluded based on the current size filter.
 * Returns IDs of regions that exist but are filtered out.
 */
export function calculateExcludedRegions(
  allRegionIds: string[],
  filteredRegionIds: string[]
): string[] {
  const filteredSet = new Set(filteredRegionIds)
  return allRegionIds.filter((id) => !filteredSet.has(id))
}
 
/**
 * Calculate preview changes when hovering over a different size range.
 * Returns which regions would be added or removed if the user clicked.
 */
export function calculatePreviewChanges(
  currentIncludedIds: string[],
  previewIncludedIds: string[]
): { addRegions: string[]; removeRegions: string[] } {
  const currentSet = new Set(currentIncludedIds)
  const previewSet = new Set(previewIncludedIds)
 
  const addRegions: string[] = []
  const removeRegions: string[] = []
 
  for (const id of previewSet) {
    if (!currentSet.has(id)) {
      addRegions.push(id)
    }
  }
 
  for (const id of currentSet) {
    if (!previewSet.has(id)) {
      removeRegions.push(id)
    }
  }
 
  return { addRegions, removeRegions }
}
 
/**
 * Generic: Convert an array of levels to min/max values for the range thermometer.
 * Works with any filter criteria (size, importance, population).
 */
export function levelsToRange<T extends FilterLevel>(levels: T[], allLevels: T[]): [T, T] {
  const sorted = [...levels].sort((a, b) => allLevels.indexOf(a) - allLevels.indexOf(b))
  return [sorted[0], sorted[sorted.length - 1]]
}
 
/**
 * Generic: Convert min/max range values back to an array of levels.
 * Works with any filter criteria (size, importance, population).
 */
export function rangeToLevels<T extends FilterLevel>(min: T, max: T, allLevels: T[]): T[] {
  const minIdx = allLevels.indexOf(min)
  const maxIdx = allLevels.indexOf(max)
  return allLevels.slice(minIdx, maxIdx + 1)
}
 
/**
 * Importance-specific: Convert array to range
 */
export function importanceToRange(levels: ImportanceLevel[]): [ImportanceLevel, ImportanceLevel] {
  return levelsToRange(levels, ALL_IMPORTANCE_LEVELS)
}
 
/**
 * Importance-specific: Convert range to array
 */
export function rangeToImportance(min: ImportanceLevel, max: ImportanceLevel): ImportanceLevel[] {
  return rangeToLevels(min, max, ALL_IMPORTANCE_LEVELS)
}
 
/**
 * Population-specific: Convert array to range
 */
export function populationToRange(levels: PopulationLevel[]): [PopulationLevel, PopulationLevel] {
  return levelsToRange(levels, ALL_POPULATION_LEVELS)
}
 
/**
 * Population-specific: Convert range to array
 */
export function rangeToPopulation(min: PopulationLevel, max: PopulationLevel): PopulationLevel[] {
  return rangeToLevels(min, max, ALL_POPULATION_LEVELS)
}