Move to useRender from BaseUI

Signed-off-by: Charles de Dreuille <charles.dedreuille@gmail.com>
This commit is contained in:
Charles de Dreuille
2025-04-04 08:58:07 +01:00
parent d83c4ae255
commit 8c2a4e9549
4 changed files with 73 additions and 49 deletions
+27
View File
@@ -15,6 +15,7 @@ import { RefAttributes } from 'react';
import type { RemixiconComponentType } from '@remixicon/react';
import { ScrollArea as ScrollArea_2 } from '@base-ui-components/react/scroll-area';
import { Tooltip as Tooltip_2 } from '@base-ui-components/react/tooltip';
import type { useRender } from '@base-ui-components/react/use-render';
// @public (undocumented)
export type AlignItems = 'stretch' | 'start' | 'center' | 'end';
@@ -696,6 +697,32 @@ export type JustifyContent =
| 'around'
| 'between';
// @public (undocumented)
export const Link: React_2.ForwardRefExoticComponent<
Omit<LinkProps, 'ref'> & React_2.RefAttributes<HTMLElement>
>;
// @public (undocumented)
export interface LinkProps extends useRender.ComponentProps<'a'> {
// (undocumented)
children: ReactNode;
// (undocumented)
className?: string;
// (undocumented)
style?: CSSProperties;
// (undocumented)
to?: string;
// (undocumented)
variant?:
| 'subtitle'
| 'body'
| 'caption'
| 'label'
| Partial<Record<Breakpoint, 'subtitle' | 'body' | 'caption' | 'label'>>;
// (undocumented)
weight?: 'regular' | 'bold' | Partial<Record<Breakpoint, 'regular' | 'bold'>>;
}
// @public (undocumented)
export const marginPropDefs: (spacingValues: string[]) => {
m: {
@@ -19,6 +19,7 @@ import type { Meta, StoryObj } from '@storybook/react';
import { Link } from './Link';
import { Flex } from '../Flex';
import { Text } from '../Text';
import { Link as RouterLink, MemoryRouter } from 'react-router-dom';
const meta = {
title: 'Components/Link',
@@ -74,6 +75,23 @@ export const Responsive: Story = {
},
};
export const CustomRender: Story = {
render: () => {
return (
<Flex gap="4" direction="column" align="start">
<Link render={<RouterLink to="/catalog" />}>Go to Catalog</Link>
</Flex>
);
},
decorators: [
Story => (
<MemoryRouter>
<Story />
</MemoryRouter>
),
],
};
export const Playground: Story = {
args: {
...Default.args,
+24 -39
View File
@@ -14,59 +14,44 @@
* limitations under the License.
*/
import React, { forwardRef, memo, ComponentType } from 'react';
import React, { forwardRef } from 'react';
import { useRender } from '@base-ui-components/react/use-render';
import { useResponsiveValue } from '../../hooks/useResponsiveValue';
import clsx from 'clsx';
import type { LinkProps, LinkRenderProps } from './types';
import type { LinkProps } from './types';
/** @public */
export const Link = memo(
forwardRef<HTMLAnchorElement, LinkProps>((props, ref) => {
const {
children,
variant = 'body',
weight = 'regular',
style,
className,
render,
to,
...restProps
} = props;
export const Link = forwardRef<HTMLElement, LinkProps>((props, ref) => {
const {
className,
variant = 'body',
weight = 'regular',
render = <a />,
...restProps
} = props;
const responsiveVariant = useResponsiveValue(variant);
const responsiveWeight = useResponsiveValue(weight);
const responsiveVariant = useResponsiveValue(variant);
const responsiveWeight = useResponsiveValue(weight);
const internalRef = React.useRef<HTMLElement | null>(null);
const linkProps: LinkRenderProps = {
const { renderElement } = useRender({
render,
props: {
className: clsx(
'canon-Link',
responsiveVariant && `canon-Link--variant-${responsiveVariant}`,
responsiveWeight && `canon-Link--weight-${responsiveWeight}`,
className,
),
style,
children,
to,
responsiveVariant,
responsiveWeight,
...restProps,
};
},
refs: [ref, internalRef],
});
if (render) {
// If render is a component type, wrap it in memo to prevent unnecessary re-renders
if (typeof render === 'function' && !render.length) {
const MemoizedComponent = memo(
render as ComponentType<LinkRenderProps>,
);
return <MemoizedComponent {...linkProps} />;
}
// If it's a render function, call it directly
const RenderComponent = render as (
props: LinkRenderProps,
) => React.ReactNode;
return <RenderComponent {...linkProps} />;
}
return <a ref={ref} href={to} {...linkProps} />;
}),
);
return renderElement();
});
Link.displayName = 'Link';
+4 -10
View File
@@ -14,14 +14,14 @@
* limitations under the License.
*/
import type { CSSProperties, ReactNode, ComponentType } from 'react';
import type { CSSProperties, ReactNode } from 'react';
import type { Breakpoint } from '../../types';
import type { useRender } from '@base-ui-components/react/use-render';
/** @public */
export interface LinkProps
extends Omit<React.AnchorHTMLAttributes<HTMLAnchorElement>, 'to'> {
export interface LinkProps extends useRender.ComponentProps<'a'> {
children: ReactNode;
to: string;
to?: string;
variant?:
| 'subtitle'
| 'body'
@@ -31,10 +31,4 @@ export interface LinkProps
weight?: 'regular' | 'bold' | Partial<Record<Breakpoint, 'regular' | 'bold'>>;
className?: string;
style?: CSSProperties;
render?:
| ((props: Omit<LinkProps, 'render'>) => ReactNode)
| ComponentType<Omit<LinkProps, 'render'>>;
}
/** @public */
export type LinkRenderProps = Omit<LinkProps, 'render'>;