91 lines
2.6 KiB
TypeScript
91 lines
2.6 KiB
TypeScript
import type { LocationQuery } from 'vue-router'
|
|
import { t } from '@/i18n'
|
|
import {
|
|
DEFAULT_PAGE_SIZE,
|
|
PAGE_SIZES,
|
|
firstQueryValue,
|
|
parsePositiveInteger,
|
|
} from '@/lib/pageQuery'
|
|
|
|
export const ENTRY_SORT_FIELDS = ['id', 'created_at', 'updated_at'] as const
|
|
export type EntrySortField = (typeof ENTRY_SORT_FIELDS)[number]
|
|
export type EntrySortDirection = 'asc' | 'desc'
|
|
|
|
export const DEFAULT_ENTRY_SORT = 'updated_at:desc'
|
|
|
|
export interface EntryListQuery {
|
|
page: number
|
|
size: number
|
|
q: string
|
|
sort: string
|
|
}
|
|
|
|
const SORT_PATTERN = new RegExp(`^(${ENTRY_SORT_FIELDS.join('|')}):(asc|desc)$`)
|
|
|
|
export function parseEntryQuery(query: LocationQuery): EntryListQuery {
|
|
const size = parsePositiveInteger(firstQueryValue(query.size))
|
|
const sort = firstQueryValue(query.sort)
|
|
return {
|
|
page: parsePositiveInteger(firstQueryValue(query.page)) ?? 1,
|
|
size: size !== null && PAGE_SIZES.includes(size) ? size : DEFAULT_PAGE_SIZE,
|
|
q: '',
|
|
sort: SORT_PATTERN.test(sort) ? sort : DEFAULT_ENTRY_SORT,
|
|
}
|
|
}
|
|
|
|
export function toEntryRouteQuery(query: EntryListQuery) {
|
|
const route: Record<string, string> = {}
|
|
if (query.size !== DEFAULT_PAGE_SIZE) {
|
|
route.size = String(query.size)
|
|
}
|
|
if (query.sort !== DEFAULT_ENTRY_SORT) {
|
|
route.sort = query.sort
|
|
}
|
|
if (query.page !== 1) {
|
|
route.page = String(query.page)
|
|
}
|
|
return route
|
|
}
|
|
|
|
export function sameEntryQuery(left: EntryListQuery, right: EntryListQuery) {
|
|
return left.page === right.page && left.size === right.size && left.sort === right.sort
|
|
}
|
|
|
|
export function sortParts(sort: string) {
|
|
const [field, direction] = sort.split(':') as [EntrySortField, EntrySortDirection]
|
|
return { field, direction }
|
|
}
|
|
|
|
export function withSortField(sort: string, field: EntrySortField) {
|
|
return `${field}:${sortParts(sort).direction}`
|
|
}
|
|
|
|
export function withOppositeDirection(sort: string) {
|
|
const { field, direction } = sortParts(sort)
|
|
return `${field}:${direction === 'asc' ? 'desc' : 'asc'}`
|
|
}
|
|
|
|
export function sortFieldLabel(field: EntrySortField) {
|
|
if (field === 'id') {
|
|
return t('ID')
|
|
}
|
|
return field === 'created_at' ? t('Created') : t('Last updated')
|
|
}
|
|
|
|
export function sortDirectionLabel(sort: string) {
|
|
return sortParts(sort).direction === 'asc' ? t('Sort descending') : t('Sort ascending')
|
|
}
|
|
|
|
export function entryCaption(name: string, sort: string) {
|
|
const { field, direction } = sortParts(sort)
|
|
if (sort === DEFAULT_ENTRY_SORT) {
|
|
return t('{0}, sorted by last updated, newest first', [name])
|
|
}
|
|
const column = { id: t('id'), created_at: t('created'), updated_at: t('updated') }[field]
|
|
return t('{0}, sorted by {1}, {2}', [
|
|
name,
|
|
column,
|
|
direction === 'desc' ? t('descending') : t('ascending'),
|
|
])
|
|
}
|