@patreon/studio
Version:
Patreon Studio Design System
72 lines • 4.17 kB
JSX
'use client';
import cx from 'classnames';
import React, { useMemo } from 'react';
import { useLogger } from '../../hooks/useLogger';
import { classNameForBodyText } from '../../styles/classNameForBodyText';
import { classNameForTextLinkStyle } from '../../styles/classNameForTextLinkStyle';
import devError from '../../utilities/dev-error';
import { useFeatureFlag } from '../FeatureFlagProvider';
import styles from './TextLink.module.css';
function useClassList({ variant, decoration, size, weight, disabled, inline, className, iconProps, }) {
// only configure the decoration style if the updated text link style feature flag is enabled
const enableUpdatedTextLinkStyle = useFeatureFlag('updated_text_link_style');
const computedDecoration = enableUpdatedTextLinkStyle ? decoration ?? 'default' : 'none';
return useMemo(() => cx(styles.root, {
[styles.variantPrimary]: variant === 'primary',
[styles.variantMuted]: variant === 'muted',
[styles.variantCritical]: variant === 'critical',
[styles.variantSuccess]: variant === 'success',
[styles.variantNeutral]: variant === 'neutral',
[styles.displayInline]: inline,
[styles.displayInlineFlex]: !inline,
[styles.isDisabled]: disabled,
}, classNameForTextLinkStyle({
isActive: !disabled,
decoration: computedDecoration,
hasIconColor: !!iconProps?.color,
}), classNameForBodyText({ size, weight: weight ? weight : 'bold' }), className), [variant, inline, disabled, size, weight, computedDecoration, className, iconProps]);
}
export const TextLink = React.forwardRef(function TextLink({ variant = 'primary', decoration = 'default', size = 'md', disabled, href, onClick, icon, inline, disclosureIcon, children, iconProps, disclosureIconProps, weight, as: userAs, loggerId, loggerProps, rel, target, className, style, id, 'data-tag': dataTag, ...props }, ref) {
if (!href && !onClick) {
devError('A `href` or `onClick` must be provided in `TextLink`');
}
const log = useLogger();
const classList = useClassList({ variant, decoration, size, weight, disabled, inline, className, iconProps });
const LeftIcon = icon;
const RightIcon = disclosureIcon;
const content = (<>
{LeftIcon && <LeftIcon size="20px" color={iconProps?.color ?? 'currentColor'} {...iconProps}/>}
{children}
{RightIcon && (<RightIcon size="20px" color={disclosureIconProps?.color ?? 'currentColor'} {...disclosureIconProps}/>)}
</>);
if (href !== undefined) {
const Component = userAs ?? 'a';
const handleClick = (e) => {
log('textLinkClick', loggerId, loggerProps);
if (e.target instanceof HTMLElement) {
e.target.style.cursor = 'default';
}
// We use this cast because Next.js expects the event object to
// be provided to the onClick handler it injects via next/link.
// However, we don't want to expose this to the public API.
// Even though the object exists at runtime, we still want TypeScript
// compilation to fail if someone tries to access it.
// TODO: We should refactor TextLink to require an href, which will
// negate the need to hide `e` entirely.
onClick?.(e);
};
return (<Component ref={ref} aria-disabled={disabled} href={disabled ? undefined : href} onClick={disabled ? undefined : handleClick} role={disabled ? 'link' : undefined} rel={rel} target={target} className={classList} style={style} id={id} data-tag={dataTag} {...props}>
{content}
</Component>);
}
const handleClickWithEvent = (ev) => {
log('textLinkClick', loggerId, loggerProps);
onClick?.(ev);
};
return (<button ref={ref} aria-disabled={disabled} onClick={disabled ? undefined : handleClickWithEvent} className={classList} style={style} id={id} data-tag={dataTag}
// forms default a button without a type="button" to a submit, we don't ever want this for text links
type="button" {...props}>
{content}
</button>);
});
//# sourceMappingURL=index.jsx.map