From 8c2a4e95491d678fea4a82955219d8cc06b9702a Mon Sep 17 00:00:00 2001 From: Charles de Dreuille Date: Fri, 4 Apr 2025 08:58:07 +0100 Subject: [PATCH] Move to useRender from BaseUI Signed-off-by: Charles de Dreuille --- packages/canon/report.api.md | 27 ++++++++ .../src/components/Link/Link.stories.tsx | 18 ++++++ packages/canon/src/components/Link/Link.tsx | 63 +++++++------------ packages/canon/src/components/Link/types.ts | 14 ++--- 4 files changed, 73 insertions(+), 49 deletions(-) diff --git a/packages/canon/report.api.md b/packages/canon/report.api.md index 766b40febd..7335ee93f3 100644 --- a/packages/canon/report.api.md +++ b/packages/canon/report.api.md @@ -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 & React_2.RefAttributes +>; + +// @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>; + // (undocumented) + weight?: 'regular' | 'bold' | Partial>; +} + // @public (undocumented) export const marginPropDefs: (spacingValues: string[]) => { m: { diff --git a/packages/canon/src/components/Link/Link.stories.tsx b/packages/canon/src/components/Link/Link.stories.tsx index 856fe2532d..bdaa4006ae 100644 --- a/packages/canon/src/components/Link/Link.stories.tsx +++ b/packages/canon/src/components/Link/Link.stories.tsx @@ -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 ( + + }>Go to Catalog + + ); + }, + decorators: [ + Story => ( + + + + ), + ], +}; + export const Playground: Story = { args: { ...Default.args, diff --git a/packages/canon/src/components/Link/Link.tsx b/packages/canon/src/components/Link/Link.tsx index e3f636ea06..e9d0d6aadf 100644 --- a/packages/canon/src/components/Link/Link.tsx +++ b/packages/canon/src/components/Link/Link.tsx @@ -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((props, ref) => { - const { - children, - variant = 'body', - weight = 'regular', - style, - className, - render, - to, - ...restProps - } = props; +export const Link = forwardRef((props, ref) => { + const { + className, + variant = 'body', + weight = 'regular', + render = , + ...restProps + } = props; - const responsiveVariant = useResponsiveValue(variant); - const responsiveWeight = useResponsiveValue(weight); + const responsiveVariant = useResponsiveValue(variant); + const responsiveWeight = useResponsiveValue(weight); + const internalRef = React.useRef(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, - ); - return ; - } - // If it's a render function, call it directly - const RenderComponent = render as ( - props: LinkRenderProps, - ) => React.ReactNode; - return ; - } - - return ; - }), -); + return renderElement(); +}); Link.displayName = 'Link'; diff --git a/packages/canon/src/components/Link/types.ts b/packages/canon/src/components/Link/types.ts index 134509563a..20fa4a2f31 100644 --- a/packages/canon/src/components/Link/types.ts +++ b/packages/canon/src/components/Link/types.ts @@ -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, '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>; className?: string; style?: CSSProperties; - render?: - | ((props: Omit) => ReactNode) - | ComponentType>; } - -/** @public */ -export type LinkRenderProps = Omit;