Link polymorphic static typing

This commit is contained in:
Treycos 2024-08-26 02:21:50 +02:00 committed by GitHub
parent ae7b187e41
commit a2e06e9e65
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
1 changed files with 63 additions and 70 deletions

View File

@ -1,96 +1,89 @@
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;
to?: string;
target?: string;
isDisabled?: LinkProps<C>['disabled'];
noRouter?: boolean;
onPress?(event: SyntheticEvent): void;
};
export interface LinkProps extends React.HTMLProps<HTMLAnchorElement> { export default function Link<C extends ElementType = 'button'>({
className?: string; className,
component?: component,
| string to,
| FunctionComponent<LinkProps> target,
| ComponentClass<LinkProps, unknown>; type,
to?: string; isDisabled,
target?: string; noRouter,
isDisabled?: boolean; onPress,
noRouter?: boolean; ...otherProps
onPress?(event: SyntheticEvent): void; }: LinkProps<C>) {
} const Component = component || 'button';
function Link(props: LinkProps) {
const {
className,
component = 'button',
to,
target,
type,
isDisabled,
noRouter = false,
onPress,
...otherProps
} = props;
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.Sonarr.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 (
<a
href={to}
target={target || (toLink ? '_blank' : '_self')}
rel={toLink ? 'noreferrer' : undefined}
className={linkClass}
onClick={onClick}
{...otherProps}
/>
);
}
return React.createElement(el, elementProps); return (
<RouterLink
to={`${window.Sonarr.urlBase}/${to.replace(/^\//, '')}`}
target={target}
className={linkClass}
onClick={onClick}
{...otherProps}
/>
);
}
return (
<Component
type={type || 'button'}
target={target}
className={linkClass}
disabled={isDisabled}
onClick={onClick}
{...otherProps}
/>
);
} }
export default Link;