-
Notifications
You must be signed in to change notification settings - Fork 29
/
Link.tsx
109 lines (97 loc) · 3.26 KB
/
Link.tsx
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
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
// @see https://github.com/mui-org/material-ui/blob/master/examples/nextjs/src/Link.js
// /* eslint-disable jsx-a11y/anchor-has-content */
import React, { Ref } from "react";
import PropTypes from "prop-types";
import clsx from "clsx";
import { useRouter } from "next/router";
import NextLink, { LinkProps as NextLinkProps } from "next/link";
import MuiLink, { LinkProps as MuiLinkProps } from "@material-ui/core/Link";
interface NextComposedProps extends NextLinkProps {
anchorProps?: React.HTMLProps<HTMLAnchorElement>;
}
/**
* A Next Link with an inner <a> anchor + ref passing
*/
const NextComposed = React.forwardRef<HTMLAnchorElement, NextComposedProps>(
function NextComposed(props, ref) {
const { anchorProps = {}, children, ...other } = props;
return (
<NextLink {...other}>
<a ref={ref} {...anchorProps} children={children} />
</NextLink>
);
}
);
NextComposed.propTypes = {
as: PropTypes.oneOfType([PropTypes.string, PropTypes.object]),
// href: PropTypes.oneOfType([PropTypes.string, PropTypes.object]), // FIXME: provokes a TS error
prefetch: PropTypes.bool,
};
type BaseLinkProps = {
activeClassName?: string;
children?: React.ReactNode;
naked?: boolean;
} & Pick<MuiLinkProps<"a", NextComposedProps>, "className" | "ref">;
// FIXME: this discriminated union doesn't work as expected based on the "naked" property
/** Props when one wants a Next Link + a <a> tag, without using Material UI Link */
// type NakedLinkProps = NextLinkProps & BaseLinkProps & { naked: true };
// type MuiNextLinkProps = MuiLinkProps<"a", NextComposedProps> &
// BaseLinkProps & { naked?: false };
// type LinkProps = MuiNextLinkProps | NakedLinkProps;
type LinkProps = NextLinkProps &
MuiLinkProps<"a", NextComposedProps> &
BaseLinkProps;
// A styled version of the Next.js Link component:
// https://nextjs.org/docs/#with-link
const Link: React.FC<LinkProps> = (props) => {
const {
href,
activeClassName = "active",
className: classNameProps,
// ref props from material ui
// NOTE: typings is currently wrong (07/2021), see https://github.com/mui-org/material-ui/issues/24901
ref,
naked,
...other
} = props;
const router = useRouter();
const pathname = typeof href === "string" ? href : href.pathname;
const className = clsx(classNameProps, {
[activeClassName]: router.pathname === pathname && activeClassName,
});
if (naked) {
return (
<NextComposed
anchorProps={{ className }}
ref={ref as Ref<HTMLAnchorElement>}
href={href}
{...other}
/>
);
}
return (
<MuiLink
component={NextComposed}
className={className}
ref={ref}
// Next type href (that can be an object) is not accepted for some reason
href={href}
{...other}
/>
);
};
/*
Link.propTypes = {
activeClassName: PropTypes.string,
as: PropTypes.oneOfType([PropTypes.string, PropTypes.object]),
className: PropTypes.string,
href: PropTypes.oneOfType([PropTypes.string, PropTypes.object]),
// innerRef: PropTypes.oneOfType([PropTypes.func, PropTypes.object]),
naked: PropTypes.bool,
onClick: PropTypes.func,
prefetch: PropTypes.bool,
};
*/
export default React.forwardRef<HTMLAnchorElement, LinkProps>((props, ref) => (
<Link {...props} ref={ref} />
));