SPB Git

spb/countryatlas

Public
2.5 KB · 51 lines typescript
1/**2 * URL contract of `/rankings/[indicator]`:3 *   ?year=2020            ranking year (default: latest; the API falls back to the nearest available year)4 *   &group=oecd           group slug (world · regions · continents · income groups · organisations); default world5 *   &sort=asc|desc        default = the API's ("asc" when lower is better, else "desc")6 *   &highlight=canada     country slug pinned at the top of the list7 *   &q=fra                text filter within the ranking8 *   &history=CAN,USA,FRA  countries of the rank-over-time panel (≤ 5 ISO3)9 * Canonical URL = path + `group` (when not world); `year`, `sort`, `highlight`, `q`, `history` are volatile.10 */11export interface RankingState {12  year: number | null;13  group: string;14  sort: 'asc' | 'desc' | null;15  highlight: string | null;16  q: string;17  history: string[];18}1920type ParamsLike = { get(name: string): string | null } | Record<string, string | string[] | undefined>;21function read(params: ParamsLike, key: string): string | null {22  if (typeof (params as { get?: unknown }).get === 'function') return (params as { get(name: string): string | null }).get(key);23  const v = (params as Record<string, string | string[] | undefined>)[key];24  return Array.isArray(v) ? (v[0] ?? null) : (v ?? null);25}2627export function parseRankingState(params: ParamsLike): RankingState {28  const y = Number(read(params, 'year'));29  const sort = read(params, 'sort');30  const group = (read(params, 'group') ?? 'world').toLowerCase().replace(/[^a-z0-9-]/g, '') || 'world';31  const highlight = (read(params, 'highlight') ?? '').toLowerCase().replace(/[^a-z0-9-]/g, '') || null;32  const history = (read(params, 'history') ?? '')33    .split(',')34    .map((s) => s.trim().toUpperCase())35    .filter((s) => /^[A-Z]{3}$/.test(s))36    .slice(0, 5);37  return { year: Number.isInteger(y) && y >= 1800 && y <= 2100 ? y : null, group, sort: sort === 'asc' || sort === 'desc' ? sort : null, highlight, q: (read(params, 'q') ?? '').slice(0, 60), history };38}3940export function rankingQuery(state: Partial<RankingState>): string {41  const p = new URLSearchParams();42  if (state.year != null) p.set('year', String(state.year));43  if (state.group && state.group !== 'world') p.set('group', state.group);44  if (state.sort) p.set('sort', state.sort);45  if (state.highlight) p.set('highlight', state.highlight);46  if (state.q) p.set('q', state.q);47  if (state.history?.length) p.set('history', state.history.join(','));48  const s = p.toString();49  return s ? `?${s}` : '';50}51
Files · d588bee37a233436d1bef2f0970b4b557e87e5f7
Menu

Theme