53 lines
1.8 KiB
TypeScript
53 lines
1.8 KiB
TypeScript
import type { Filter } from '@/components/reui/filters'
|
|
|
|
/**
|
|
* Сериализация состояния страниц (таб/поиск/фильтры) в URL search-параметры:
|
|
* tab — id вкладки, q — строка поиска, f — JSON-массив фильтров.
|
|
* Пустые значения не пишутся в URL (deep-link остаётся чистым).
|
|
*/
|
|
|
|
export function filtersToSearch(filters: Filter[]): string | undefined {
|
|
if (filters.length === 0) return undefined
|
|
try {
|
|
return JSON.stringify(filters)
|
|
} catch {
|
|
return undefined
|
|
}
|
|
}
|
|
|
|
export function searchToFilters(raw: unknown): Filter[] {
|
|
if (typeof raw !== 'string' || raw.length === 0) return []
|
|
try {
|
|
const parsed: unknown = JSON.parse(raw)
|
|
if (!Array.isArray(parsed)) return []
|
|
return parsed.filter(
|
|
(x): x is Filter =>
|
|
typeof x === 'object' &&
|
|
x !== null &&
|
|
typeof (x as Filter).field === 'string' &&
|
|
typeof (x as Filter).operator === 'string' &&
|
|
Array.isArray((x as Filter).values),
|
|
)
|
|
} catch {
|
|
return []
|
|
}
|
|
}
|
|
|
|
/** Строка поиска: undefined при пустой, чтобы не писать пустой q в URL. */
|
|
export function searchToQ(raw: unknown): string | undefined {
|
|
return typeof raw === 'string' && raw.length > 0 ? raw : undefined
|
|
}
|
|
|
|
/** Вкладка: валидируется по списку допустимых id. */
|
|
export function searchToTab(
|
|
raw: unknown,
|
|
allowed: readonly string[],
|
|
): string | undefined {
|
|
return typeof raw === 'string' && allowed.includes(raw) ? raw : undefined
|
|
}
|
|
|
|
/** Флаг из search-параметра: только `true` пишется в URL. */
|
|
export function searchToFlag(raw: unknown): boolean | undefined {
|
|
return raw === true || raw === '1' ? true : undefined
|
|
}
|