UNPKG

@patreon/studio

Version:

Patreon Studio Design System

72 lines 4.17 kB
'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