import React, { HTMLAttributes, forwardRef } from "react"; import type { AkselColor } from "../types"; import { BodyLong, Heading } from "../typography"; import { LinkAnchor, LinkAnchorArrow, LinkAnchorOverlay, LinkAnchorProps, } from "../utils/components/link-anchor"; import { cl, createStrictContext } from "../utils/helpers"; /* ------------------------------ LinkCard Root ----------------------------- */ interface LinkCardProps extends HTMLAttributes { /** * @default true */ arrow?: boolean; /** * Adjusts arrow position. * @default "baseline" */ arrowPosition?: "baseline" | "center"; /** * Changes padding and typo sizes. * @default "medium" */ size?: "small" | "medium"; /** * Overrides inherited color. * * We reccomend avoiding status-colors (`info`, `success`, `warning`, `danger`) in LinkCards. * @see 🏷️ {@link AkselColor} * @see [📝 Documentation](https://aksel.nav.no/grunnleggende/styling/farger-tokens) */ "data-color"?: AkselColor; /** * Changes the HTML element used for the root element. * * **When using `section`, provide either `aria-label` or `aria-labelledby` for better accessibility.** * `axe-core` might warn about unique landmarks if you have multiple Accordions on page with the same label. * In those cases consider updating to unique `aria-label` or `aria-labelledby` props. * @see [📝 Landmarks unique](https://dequeuniversity.com/rules/axe/4.6/landmark-unique) * * * **When using `article`, make sure `` is a heading and not a `span`.** * @default "div" */ as?: "div" | "section" | "article"; } type LinkCardContextProps = { size: LinkCardProps["size"]; }; const { Provider: LinkCardContextProvider, useContext: useLinkCardContext } = createStrictContext({ name: "LinkCardContextProvider", }); interface LinkCardComponent extends React.ForwardRefExoticComponent< LinkCardProps & React.RefAttributes > { /** * @see 🏷️ {@link LinkCardTitleProps} */ Title: typeof LinkCardTitle; /** * @see 🏷️ {@link LinkCardAnchorProps} */ Anchor: typeof LinkCardAnchor; /** * @see 🏷️ {@link LinkCardDescriptionProps} */ Description: typeof LinkCardDescription; /** * @see 🏷️ {@link LinkCardFooterProps} */ Footer: typeof LinkCardFooter; /** * @see 🏷️ {@link LinkCardIconProps} */ Icon: typeof LinkCardIcon; /** * @see 🏷️ {@link LinkCardImageProps} */ Image: typeof LinkCardImage; } /** * Accessible clickable card as a link. * * @see [📝 Documentation](https://aksel.nav.no/komponenter/core/linkcard) * @see 🏷️ {@link LinkCardProps} * * * @example * ```tsx * * * * * * * LinkCard title * * * * This is a description of the link card. * * Footer content * * ``` */ export const LinkCard = forwardRef( ( { children, className, arrow = true, arrowPosition = "baseline", size = "medium", as: Component = "div", ...restProps }: LinkCardProps, forwardedRef, ) => { return ( {children} {arrow && ( )} ); }, ) as LinkCardComponent; /* ---------------------------- LinkCard Title ---------------------------- */ type LinkCardTitleProps = HTMLAttributes & { children: React.ReactNode; /** * Heading tag. Use "span" if you want a non header defining card * (eg. you have a lot of them all at once, such as in a grid) * @default "span" */ as?: "span" | "h2" | "h3" | "h4" | "h5" | "h6"; }; /** * @see 🏷️ {@link LinkCardTitleProps} */ export const LinkCardTitle = forwardRef( ( { children, as = "span", className, ...restProps }: LinkCardTitleProps, forwardedRef, ) => { const context = useLinkCardContext(); return ( {children} ); }, ); /* ---------------------------- LinkCard Anchor ---------------------------- */ type LinkCardAnchorProps = LinkAnchorProps; /** * @see 🏷️ {@link LinkCardAnchorProps} */ export const LinkCardAnchor = LinkAnchor; /* ---------------------------- LinkCard Description ---------------------------- */ interface LinkCardDescriptionProps extends HTMLAttributes { children: React.ReactNode; } /** * @see 🏷️ {@link LinkCardDescriptionProps} */ export const LinkCardDescription = forwardRef< HTMLDivElement, LinkCardDescriptionProps >( ( { children, className, ...restProps }: LinkCardDescriptionProps, forwardedRef, ) => { return (
{children}
); }, ); /* ---------------------------- LinkCard Footer ---------------------------- */ interface LinkCardFooterProps extends HTMLAttributes { children: React.ReactNode; } /** * @see 🏷️ {@link LinkCardFooterProps} */ export const LinkCardFooter = forwardRef( ( { children, className, ...restProps }: LinkCardFooterProps, forwardedRef, ) => { return (
{children}
); }, ); /* ---------------------------- LinkCard Icon ---------------------------- */ interface LinkCardIconProps extends HTMLAttributes { children: React.ReactNode; } /** * @see 🏷️ {@link LinkCardIconProps} */ export const LinkCardIcon = forwardRef( ({ children, className, ...restProps }: LinkCardIconProps, forwardedRef) => { return (
{children}
); }, ); /* ---------------------------- LinkCard Image ---------------------------- */ type ImageAspectRatio = "1/1" | "16/9" | "16/10" | "4/3" | (string & {}); interface LinkCardImageProps extends HTMLAttributes { children: React.ReactNode; /** * The aspect-ratio CSS property allows you to define the desired width-to-height ratio of an element's box. * This means that even if the parent container or viewport size changes, the browser will adjust the element's dimensions to maintain the specified width-to-height ratio. */ aspectRatio?: ImageAspectRatio; } /** * @see 🏷️ {@link LinkCardImageProps} */ export const LinkCardImage = forwardRef( ( { children, className, aspectRatio, style, ...restProps }: LinkCardImageProps, forwardedRef, ) => { return (
{children}
); }, ); LinkCard.Title = LinkCardTitle; LinkCard.Anchor = LinkCardAnchor; LinkCard.Description = LinkCardDescription; LinkCard.Footer = LinkCardFooter; LinkCard.Icon = LinkCardIcon; LinkCard.Image = LinkCardImage; export type { LinkCardAnchorProps, LinkCardDescriptionProps, LinkCardFooterProps, LinkCardIconProps, LinkCardImageProps, LinkCardProps, LinkCardTitleProps, };