mirror of
https://github.com/XRPLF/xrpl-dev-portal.git
synced 2025-12-06 17:27:57 +00:00
Add BDS link styles and update existing link styles
- Introduced new styles for BDS link icons, including hover and focus states. - Updated existing link styles to exclude BDS links from certain color and hover effects. - Ensured consistent styling across light and dark themes for BDS links. - Refactored landing page link styles to accommodate new BDS link classes.
This commit is contained in:
168
shared/components/Link/Link.tsx
Normal file
168
shared/components/Link/Link.tsx
Normal file
@@ -0,0 +1,168 @@
|
||||
import React from 'react';
|
||||
import clsx from 'clsx';
|
||||
import { LinkArrow, LinkArrowVariant } from './LinkArrow';
|
||||
|
||||
export type LinkVariant = 'internal' | 'external' | 'inline';
|
||||
export type LinkSize = 'small' | 'medium' | 'large';
|
||||
export type LinkIconType = 'arrow' | 'external' | null;
|
||||
|
||||
export interface LinkProps extends React.AnchorHTMLAttributes<HTMLAnchorElement> {
|
||||
/**
|
||||
* Link variant - internal, external, or inline
|
||||
* @default 'internal'
|
||||
*/
|
||||
variant?: LinkVariant;
|
||||
|
||||
/**
|
||||
* Size of the link
|
||||
* @default 'medium'
|
||||
*/
|
||||
size?: LinkSize;
|
||||
|
||||
/**
|
||||
* Icon type - arrow, external, or null
|
||||
* If null, icon is determined by variant (internal/external)
|
||||
* Arrow icons animate to chevron shape on hover
|
||||
* @default null
|
||||
*/
|
||||
icon?: LinkIconType;
|
||||
|
||||
/**
|
||||
* Disabled state - prevents navigation and applies disabled styles
|
||||
* @default false
|
||||
*/
|
||||
disabled?: boolean;
|
||||
|
||||
/**
|
||||
* Link URL (required)
|
||||
*/
|
||||
href: string;
|
||||
|
||||
/**
|
||||
* Link text content
|
||||
*/
|
||||
children: React.ReactNode;
|
||||
}
|
||||
|
||||
/**
|
||||
* Link Component
|
||||
*
|
||||
* A comprehensive link component supporting multiple sizes, icon types, and states.
|
||||
* Arrow icons animate to chevron shape on hover.
|
||||
*
|
||||
* Color states are handled automatically via CSS per theme:
|
||||
*
|
||||
* Light Mode:
|
||||
* - Enabled: Green 400 (#0DAA3E)
|
||||
* - Hover/Focus: Green 500 (#078139) + underline + arrow animates to chevron
|
||||
* - Active: Green 400 (#0DAA3E) + underline
|
||||
* - Visited: Lilac 400 (#7649E3)
|
||||
* - Disabled: Gray 400 (#A2A2A4)
|
||||
* - Focus outline: Black (#000000)
|
||||
*
|
||||
* Dark Mode:
|
||||
* - Enabled: Green 300 (#21E46B)
|
||||
* - Hover/Focus: Green 200 (#70EE97) + underline + arrow animates to chevron
|
||||
* - Active: Green 300 (#21E46B) + underline
|
||||
* - Visited: Lilac 300 (#C0A7FF)
|
||||
* - Disabled: Gray 500 (#838386)
|
||||
* - Focus outline: White (#FFFFFF)
|
||||
*
|
||||
* @see Link.md for full documentation
|
||||
*
|
||||
* @example
|
||||
* ```tsx
|
||||
* // Basic internal link (arrow animates to chevron on hover)
|
||||
* <Link href="/docs" size="medium">
|
||||
* View documentation
|
||||
* </Link>
|
||||
*
|
||||
* // External link
|
||||
* <Link href="https://example.com" variant="external" size="large">
|
||||
* External resource
|
||||
* </Link>
|
||||
*
|
||||
* // Disabled link
|
||||
* <Link href="#" disabled>
|
||||
* Coming soon
|
||||
* </Link>
|
||||
*
|
||||
* // Inline link (no icon)
|
||||
* <Link href="/docs" variant="inline">
|
||||
* Learn more
|
||||
* </Link>
|
||||
* ```
|
||||
*/
|
||||
export const Link: React.FC<LinkProps> = ({
|
||||
variant = 'internal',
|
||||
size = 'medium',
|
||||
icon = null,
|
||||
disabled = false,
|
||||
href,
|
||||
children,
|
||||
className,
|
||||
onClick,
|
||||
...rest
|
||||
}) => {
|
||||
// Determine icon type based on variant if not explicitly provided
|
||||
const getIconType = (): LinkArrowVariant | null => {
|
||||
if (icon === null) {
|
||||
// Auto-determine icon based on variant
|
||||
if (variant === 'external') {
|
||||
return 'external';
|
||||
}
|
||||
if (variant === 'internal') {
|
||||
return 'internal'; // Default to internal arrow for internal variant
|
||||
}
|
||||
return null; // Inline links have no icon
|
||||
}
|
||||
|
||||
// Map icon prop to LinkArrow variant
|
||||
if (icon === 'arrow') return 'internal';
|
||||
if (icon === 'external') return 'external';
|
||||
return null;
|
||||
};
|
||||
|
||||
const iconType = getIconType();
|
||||
const shouldShowIcon = variant !== 'inline' && iconType !== null;
|
||||
|
||||
const classes = clsx(
|
||||
'bds-link',
|
||||
`bds-link--${variant}`,
|
||||
`bds-link--${size}`,
|
||||
{
|
||||
'bds-link--disabled': disabled,
|
||||
},
|
||||
className
|
||||
);
|
||||
|
||||
const handleClick = (e: React.MouseEvent<HTMLAnchorElement>) => {
|
||||
if (disabled) {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
return;
|
||||
}
|
||||
onClick?.(e);
|
||||
};
|
||||
|
||||
return (
|
||||
<a
|
||||
href={disabled ? '#' : href}
|
||||
className={classes}
|
||||
onClick={handleClick}
|
||||
aria-disabled={disabled}
|
||||
{...rest}
|
||||
>
|
||||
{children}
|
||||
{shouldShowIcon && (
|
||||
<LinkArrow
|
||||
variant={iconType as LinkArrowVariant}
|
||||
size={size}
|
||||
disabled={disabled}
|
||||
/>
|
||||
)}
|
||||
</a>
|
||||
);
|
||||
};
|
||||
|
||||
Link.displayName = 'Link';
|
||||
Reference in New Issue
Block a user