import { goto as svelteGoto } from '$app/navigation'; import { SortDirection } from '$gql/graphql'; import JsonURL from '@jsonurl/jsonurl'; import { type PaginationData } from './Pagination'; import { type SortData } from './Sort'; import { toastError } from './Toasts'; function paramToNum<T>(value: string | null, fallback: T) { if (value) { const number = +value; if (Number.isNaN(number) || number < 0) { return fallback; } return number; } return fallback; } export function parseSortData<T>(params: URLSearchParams, fallback: T): SortData<T> { return { on: (params.get('s') as T) || fallback, direction: (params.get('d') as SortDirection) || SortDirection.Ascending, seed: paramToNum(params.get('r'), undefined) }; } export function parsePaginationData(params: URLSearchParams, defaultItems = 120): PaginationData { return { page: paramToNum(params.get('p'), 1), items: paramToNum(params.get('i'), defaultItems) }; } export function parseFilter<T>(params: URLSearchParams): T { const param = params.get('f'); if (!param) return {} as T; try { return JsonURL.parse(param, { AQF: true, impliedObject: {} }) as T; } catch (e) { return {} as T; } } interface NavigationOptions { to?: string; params: URLSearchParams; options?: Parameters<typeof svelteGoto>[1]; } export function goto({ to = '', params, options }: NavigationOptions) { svelteGoto(`${to}?${params.toString()}`, options).catch(() => toastError('Navigation failed')); } interface NavigationParameters<T> { filter?: T; sort?: Partial<SortData<string>>; pagination?: Partial<PaginationData>; } function paramsFrom<T>( { pagination, filter, sort }: NavigationParameters<T>, current?: URLSearchParams ) { const params = new URLSearchParams(current); if (filter !== undefined) { const json = JsonURL.stringify(filter, { AQF: true, impliedObject: true }); if (json) { params.set('f', json); } else { params.delete('f'); } } if (sort !== undefined) { if (sort.on !== undefined) { params.set('s', sort.on); } if (sort.direction !== undefined) { params.set('d', sort.direction); } if (sort.seed !== undefined) { params.set('r', sort.seed.toString()); } } params.delete('p'); if (pagination?.items) { params.set('i', pagination.items.toString()); } if (pagination?.page) { params.set('p', pagination.page.toString()); } return params; } export function navigate(parameters: NavigationParameters<object>, current?: URLSearchParams) { goto({ params: paramsFrom(parameters, current), options: { noScroll: false, keepFocus: true, replaceState: true } }); } export function href<T>(base: string, params: NavigationParameters<T>) { return `/${base}/?${paramsFrom(params).toString()}`; }