1
0
mirror of https://github.com/Radarr/Radarr.git synced 2024-10-02 14:17:19 +02:00

Link polymorphic static typing

(cherry picked from commit a2e06e9e650642518b926a61f624a2c7a49c0988)
(cherry picked from commit cfa2f4d4c6e35d7b9ddd2e1da2e59f7287859516)

Closes #10363
Closes #10348
This commit is contained in:
Bogdan 2024-09-03 15:54:25 +03:00
parent b4b5ad9567
commit 0d6ce5ea49

View File

@ -1,96 +1,93 @@
import classNames from 'classnames'; import classNames from 'classnames';
import React, { import React, {
ComponentClass, ComponentPropsWithoutRef,
FunctionComponent, ElementType,
SyntheticEvent, SyntheticEvent,
useCallback, useCallback,
} from 'react'; } from 'react';
import { Link as RouterLink } from 'react-router-dom'; import { Link as RouterLink } from 'react-router-dom';
import styles from './Link.css'; import styles from './Link.css';
interface ReactRouterLinkProps { export type LinkProps<C extends ElementType = 'button'> =
to?: string; ComponentPropsWithoutRef<C> & {
} component?: C;
export interface LinkProps extends React.HTMLProps<HTMLAnchorElement> {
className?: string;
component?:
| string
| FunctionComponent<LinkProps>
| ComponentClass<LinkProps, unknown>;
to?: string; to?: string;
target?: string; target?: string;
isDisabled?: boolean; isDisabled?: LinkProps<C>['disabled'];
noRouter?: boolean; noRouter?: boolean;
onPress?(event: SyntheticEvent): void; onPress?(event: SyntheticEvent): void;
} };
function Link(props: LinkProps) {
const { export default function Link<C extends ElementType = 'button'>({
className, className,
component = 'button', component,
to, to,
target, target,
type, type,
isDisabled, isDisabled,
noRouter = false, noRouter,
onPress, onPress,
...otherProps ...otherProps
} = props; }: LinkProps<C>) {
const Component = component || 'button';
const onClick = useCallback( const onClick = useCallback(
(event: SyntheticEvent) => { (event: SyntheticEvent) => {
if (!isDisabled && onPress) { if (isDisabled) {
onPress(event); return;
} }
onPress?.(event);
}, },
[isDisabled, onPress] [isDisabled, onPress]
); );
const linkProps: React.HTMLProps<HTMLAnchorElement> & ReactRouterLinkProps = { const linkClass = classNames(
target,
};
let el = component;
if (to) {
if (/\w+?:\/\//.test(to)) {
el = 'a';
linkProps.href = to;
linkProps.target = target || '_blank';
linkProps.rel = 'noreferrer';
} else if (noRouter) {
el = 'a';
linkProps.href = to;
linkProps.target = target || '_self';
} else {
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore
el = RouterLink;
linkProps.to = `${window.Radarr.urlBase}/${to.replace(/^\//, '')}`;
linkProps.target = target;
}
}
if (el === 'button' || el === 'input') {
linkProps.type = type || 'button';
linkProps.disabled = isDisabled;
}
linkProps.className = classNames(
className, className,
styles.link, styles.link,
to && styles.to, to && styles.to,
isDisabled && 'isDisabled' isDisabled && 'isDisabled'
); );
const elementProps = { if (to) {
...otherProps, const toLink = /\w+?:\/\//.test(to);
type,
...linkProps,
};
elementProps.onClick = onClick; if (toLink || noRouter) {
return (
return React.createElement(el, elementProps); <a
href={to}
target={target || (toLink ? '_blank' : '_self')}
rel={toLink ? 'noreferrer' : undefined}
className={linkClass}
onClick={onClick}
{...otherProps}
/>
);
} }
export default Link; return (
<RouterLink
to={`${window.Radarr.urlBase}/${to.replace(/^\//, '')}`}
target={target}
className={linkClass}
onClick={onClick}
{...otherProps}
/>
);
}
return (
<Component
type={
component === 'button' || component === 'input'
? type || 'button'
: type
}
target={target}
className={linkClass}
disabled={isDisabled}
onClick={onClick}
{...otherProps}
/>
);
}