export const VIEWS = [ 'dashboard', 'links', 'history', 'schedule', 'settings', ] as const; export type View = ( typeof VIEWS )[ number ]; export type RouteParams = Readonly< Partial< Record< string, string > > >; export type Route = { view: View; params: RouteParams; }; const HASH_PREFIX = /^#\/?/; const QUERY_MARK = '?'; const DEFAULT_VIEW: View = 'dashboard'; function isView( name: string ): name is View { return ( VIEWS as readonly string[] ).includes( name ); } function readParams( queryText: string ): RouteParams { const params: Record< string, string > = {}; for ( const [ name, paramValue ] of new URLSearchParams( queryText ) ) { if ( paramValue !== '' ) { params[ name ] = paramValue; } } return params; } export function parseRoute( hash: string ): Route { const routeText = hash.replace( HASH_PREFIX, '' ); const markPosition = routeText.indexOf( QUERY_MARK ); const hasQuery = markPosition !== -1; const viewName = hasQuery ? routeText.slice( 0, markPosition ) : routeText; const queryText = hasQuery ? routeText.slice( markPosition + 1 ) : ''; return { view: isView( viewName ) ? viewName : DEFAULT_VIEW, params: readParams( queryText ), }; } export function formatRoute( view: View, params: RouteParams ): string { const query = new URLSearchParams(); for ( const name of Object.keys( params ).sort() ) { const paramValue = params[ name ]; if ( paramValue !== undefined && paramValue !== '' ) { query.set( name, paramValue ); } } const queryText = query.toString(); const path = view === DEFAULT_VIEW ? '' : view; const querySuffix = queryText === '' ? '' : `?${ queryText }`; return `#/${ path }${ querySuffix }`; }