Passed
Push — task/application-profile-react... ( 9a6ac9...5f3abf )
by Yonathan
07:55
created

resources/assets/js/helpers/router.tsx   A

Complexity

Total Complexity 5
Complexity/F 0

Size

Lines of Code 105
Function Count 0

Duplication

Duplicated Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
wmc 5
eloc 82
mnd 5
bc 5
fnc 0
dl 0
loc 105
rs 10
bpm 0
cpm 0
noi 0
c 0
b 0
f 0
1
import { createBrowserHistory, Location } from "history";
2
import UniversalRouter, { Routes } from "universal-router";
3
import React, { useState, useEffect, useMemo, ReactElement } from "react";
4
import { IntlShape, MessageDescriptor } from "react-intl";
5
import { removeBaseUrl } from "./routes";
6
7
const HISTORY = createBrowserHistory();
8
9
// Current implementation adapted from https://codesandbox.io/s/vyx8q7jvk7
10
11
export const useLocation = (): Location<any> => {
12
  const history = HISTORY;
13
  const [location, setLocation] = useState(history.location);
14
  useEffect((): (() => void) => {
15
    const unListen = history.listen(({ location: newLocation }): void =>
16
      setLocation(newLocation),
17
    );
18
    return (): void => unListen();
19
  }, [history]);
20
  return location;
21
};
22
23
// Scroll to element specified in the url hash, if possible
24
export const useUrlHash = (): string => {
25
  const location = useLocation();
26
  const [hashFound, setHashFound] = useState(false);
27
  useEffect((): void => {
28
    if (location.hash && !hashFound) {
29
      const hash = location.hash.startsWith("#")
30
        ? location.hash.substring(1)
31
        : location.hash;
32
      const element = document.getElementById(hash);
33
      if (element) {
34
        setHashFound(true);
35
        window.scrollTo(0, element.offsetTop);
36
      }
37
    }
38
  }, [location.hash, hashFound]);
39
  return location.hash;
40
};
41
42
export const navigate = (url: string): void => {
43
  // The history object has been initialized with the app's base url, so ensure it's not also part of the specified url.
44
  const path = removeBaseUrl(url);
45
  HISTORY.push(path);
46
};
47
48
export const redirect = (url: string): void => {
49
  // The history object has been initialized with the app's base url, so ensure it's not also part of the specified url.
50
  const path = removeBaseUrl(url);
51
  HISTORY.replace(path);
52
};
53
54
export interface RouterResult {
55
  title: MessageDescriptor;
56
  component: ReactElement;
57
  redirect?: string;
58
}
59
60
export const useRouter = (
61
  routes: Routes<any, RouterResult>,
62
  intl: IntlShape,
63
): React.ReactElement | null => {
64
  const location = useLocation();
65
  const router = useMemo(() => new UniversalRouter(routes), [routes]);
66
  const [component, setComponent] = useState<React.ReactElement | null>(null);
67
  const path = location.pathname;
68
  // Render the result of routing
69
  useEffect((): void => {
70
    router.resolve(path).then((result): void => {
71
      if (result.redirect) {
72
        redirect(result.redirect);
73
      } else {
74
        // Dynamically update the page title and header on step changes
75
        const title = intl.formatMessage(result.title);
76
        document.title = title;
77
        const h1 = document.querySelector("h1");
78
        if (h1) h1.innerHTML = title;
79
        setComponent(result.component);
80
      }
81
    });
82
  }, [intl, location, router]);
83
84
  return component;
85
};
86
87
export const Link: React.FC<{ href: string; title: string }> = ({
88
  href,
89
  title,
90
  children,
91
  ...props
92
}): React.ReactElement => (
93
  <a
94
    href={href}
95
    title={title}
96
    {...props}
97
    onClick={(event): void => {
98
      event.preventDefault();
99
      navigate(href);
100
    }}
101
  >
102
    {children}
103
  </a>
104
);
105