File size: 1,370 Bytes
4fdaf19
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
import { useCallback, useEffect, useState } from 'react';

/** Hash-based routing (`#/<tab>/<view>`) — works with static file serving on
 * a Space (no server rewrites needed), gives real back/forward + deep links.
 * Returns the current tab/view and a navigate() that updates the hash. */

export interface Route {
  tab: string;
  view: string;
}

function parse(): Route {
  const raw = window.location.hash.replace(/^#\/?/, '');
  const [tab = '', view = ''] = raw.split('/');
  return { tab, view };
}

export function useHashRoute(defaultTab: string): [Route, (tab: string, view?: string) => void] {
  const [route, setRoute] = useState<Route>(() => {
    const r = parse();
    return { tab: r.tab || defaultTab, view: r.view };
  });

  useEffect(() => {
    const onChange = () => {
      const r = parse();
      setRoute({ tab: r.tab || defaultTab, view: r.view });
    };
    window.addEventListener('hashchange', onChange);
    // Normalize an empty hash on first load.
    if (!window.location.hash) window.location.hash = `#/${route.tab}`;
    return () => window.removeEventListener('hashchange', onChange);
    // eslint-disable-next-line react-hooks/exhaustive-deps
  }, []);

  const navigate = useCallback((tab: string, view?: string) => {
    window.location.hash = view ? `#/${tab}/${view}` : `#/${tab}`;
  }, []);

  return [route, navigate];
}