diff --git a/packages/raystack/components/accordion/accordion-content.tsx b/packages/raystack/components/accordion/accordion-content.tsx index 5dc7183bc..088048717 100644 --- a/packages/raystack/components/accordion/accordion-content.tsx +++ b/packages/raystack/components/accordion/accordion-content.tsx @@ -2,22 +2,18 @@ import { Accordion as AccordionPrimitive } from '@base-ui/react'; import { cx } from 'class-variance-authority'; -import { ElementRef, forwardRef } from 'react'; import styles from './accordion.module.css'; -export const AccordionContent = forwardRef< - ElementRef, - AccordionPrimitive.Panel.Props ->(({ className, children, ...props }, ref) => ( - +export const AccordionContent = ({ + className, + children, + ...props +}: AccordionPrimitive.Panel.Props) => ( +
{children}
-)); +); AccordionContent.displayName = 'Accordion.Content'; diff --git a/packages/raystack/components/accordion/accordion-item.tsx b/packages/raystack/components/accordion/accordion-item.tsx index 3b487fa5c..0b937b8c7 100644 --- a/packages/raystack/components/accordion/accordion-item.tsx +++ b/packages/raystack/components/accordion/accordion-item.tsx @@ -2,20 +2,19 @@ import { Accordion as AccordionPrimitive } from '@base-ui/react'; import { cx } from 'class-variance-authority'; -import { ElementRef, forwardRef } from 'react'; import styles from './accordion.module.css'; -export const AccordionItem = forwardRef< - ElementRef, - AccordionPrimitive.Item.Props ->(({ className, children, ...props }, ref) => ( +export const AccordionItem = ({ + className, + children, + ...props +}: AccordionPrimitive.Item.Props) => ( {children} -)); +); AccordionItem.displayName = 'Accordion.Item'; diff --git a/packages/raystack/components/accordion/accordion-root.tsx b/packages/raystack/components/accordion/accordion-root.tsx index be7030010..6544de9cd 100644 --- a/packages/raystack/components/accordion/accordion-root.tsx +++ b/packages/raystack/components/accordion/accordion-root.tsx @@ -2,7 +2,6 @@ import { Accordion as AccordionPrimitive } from '@base-ui/react'; import { cx } from 'class-variance-authority'; -import { ElementRef, forwardRef } from 'react'; import styles from './accordion.module.css'; type AccordionSingleProps = Omit< @@ -27,61 +26,52 @@ type AccordionMultipleProps = Omit< export type AccordionRootProps = AccordionSingleProps | AccordionMultipleProps; -export const AccordionRoot = forwardRef< - ElementRef, - AccordionRootProps ->( - ( - { - className, - multiple = false, - value, - defaultValue, - onValueChange, - ...rest - }, - ref - ) => { - // Convert value to array format for Base UI - const baseValue = value - ? Array.isArray(value) - ? value - : [value] - : undefined; +export const AccordionRoot = ({ + className, + multiple = false, + value, + defaultValue, + onValueChange, + ...rest +}: AccordionRootProps) => { + // Convert value to array format for Base UI + const baseValue = value + ? Array.isArray(value) + ? value + : [value] + : undefined; - const baseDefaultValue = defaultValue - ? Array.isArray(defaultValue) - ? defaultValue - : [defaultValue] - : undefined; + const baseDefaultValue = defaultValue + ? Array.isArray(defaultValue) + ? defaultValue + : [defaultValue] + : undefined; - const handleValueChange = ( - newValue: string[], - eventDetails: AccordionPrimitive.Root.ChangeEventDetails - ) => { - if (onValueChange) { - if (multiple) { - (onValueChange as (value: string[]) => void)(newValue); - } else { - (onValueChange as (value: string | undefined) => void)( - newValue[0] || undefined - ); - } + const handleValueChange = ( + newValue: string[], + eventDetails: AccordionPrimitive.Root.ChangeEventDetails + ) => { + if (onValueChange) { + if (multiple) { + (onValueChange as (value: string[]) => void)(newValue); + } else { + (onValueChange as (value: string | undefined) => void)( + newValue[0] || undefined + ); } - }; + } + }; - return ( - - ); - } -); + return ( + + ); +}; -AccordionRoot.displayName = 'Accordion.Root'; +AccordionRoot.displayName = 'Accordion'; diff --git a/packages/raystack/components/accordion/accordion-trigger.tsx b/packages/raystack/components/accordion/accordion-trigger.tsx index ef50a0ad3..c52735e4d 100644 --- a/packages/raystack/components/accordion/accordion-trigger.tsx +++ b/packages/raystack/components/accordion/accordion-trigger.tsx @@ -3,16 +3,15 @@ import { Accordion as AccordionPrimitive } from '@base-ui/react'; import { ChevronDownIcon } from '@radix-ui/react-icons'; import { cx } from 'class-variance-authority'; -import { ElementRef, forwardRef } from 'react'; import styles from './accordion.module.css'; -export const AccordionTrigger = forwardRef< - ElementRef, - AccordionPrimitive.Trigger.Props ->(({ className, children, ...props }, ref) => ( +export const AccordionTrigger = ({ + className, + children, + ...props +}: AccordionPrimitive.Trigger.Props) => ( @@ -20,6 +19,6 @@ export const AccordionTrigger = forwardRef< -)); +); AccordionTrigger.displayName = 'Accordion.Trigger'; diff --git a/packages/raystack/components/alert-dialog/alert-dialog-content.tsx b/packages/raystack/components/alert-dialog/alert-dialog-content.tsx index e60acfdda..824cbb154 100644 --- a/packages/raystack/components/alert-dialog/alert-dialog-content.tsx +++ b/packages/raystack/components/alert-dialog/alert-dialog-content.tsx @@ -2,7 +2,6 @@ import { AlertDialog as AlertDialogPrimitive } from '@base-ui/react'; import { cx } from 'class-variance-authority'; -import { type ElementRef, forwardRef } from 'react'; import styles from '../dialog/dialog.module.css'; import { CloseButton } from './alert-dialog-misc'; @@ -18,51 +17,42 @@ export interface AlertDialogContentProps showNestedAnimation?: boolean; } -export const AlertDialogContent = forwardRef< - ElementRef, - AlertDialogContentProps ->( - ( - { - className, - children, - showCloseButton = true, - overlay, - width, - style, - showNestedAnimation = true, - ...props - }, - ref - ) => { - return ( - - { + return ( + + + + - - - {children} - {showCloseButton && } - - - - ); - } -); + style={{ width, ...style }} + {...props} + > + {children} + {showCloseButton && } + + + + ); +}; AlertDialogContent.displayName = 'AlertDialog.Content'; diff --git a/packages/raystack/components/alert-dialog/alert-dialog-misc.tsx b/packages/raystack/components/alert-dialog/alert-dialog-misc.tsx index f0fe92e4b..d242a196f 100644 --- a/packages/raystack/components/alert-dialog/alert-dialog-misc.tsx +++ b/packages/raystack/components/alert-dialog/alert-dialog-misc.tsx @@ -3,62 +3,58 @@ import { AlertDialog as AlertDialogPrimitive } from '@base-ui/react'; import { Cross1Icon } from '@radix-ui/react-icons'; import { cx } from 'class-variance-authority'; -import { ComponentPropsWithoutRef, type ElementRef, forwardRef } from 'react'; +import { type ComponentProps } from 'react'; import styles from '../dialog/dialog.module.css'; import { Flex } from '../flex'; -export const AlertDialogHeader = forwardRef< - ElementRef, - ComponentPropsWithoutRef ->(({ className, ...props }, ref) => ( +export const AlertDialogHeader = ({ + className, + ...props +}: ComponentProps) => ( -)); +); AlertDialogHeader.displayName = 'AlertDialog.Header'; -export const AlertDialogFooter = forwardRef< - ElementRef, - ComponentPropsWithoutRef ->(({ className, ...props }, ref) => ( +export const AlertDialogFooter = ({ + className, + ...props +}: ComponentProps) => ( -)); +); AlertDialogFooter.displayName = 'AlertDialog.Footer'; -export const AlertDialogBody = forwardRef< - ElementRef, - ComponentPropsWithoutRef ->(({ className, ...props }, ref) => ( +export const AlertDialogBody = ({ + className, + ...props +}: ComponentProps) => ( -)); +); AlertDialogBody.displayName = 'AlertDialog.Body'; -export const CloseButton = forwardRef< - ElementRef, - AlertDialogPrimitive.Close.Props ->(({ className, ...props }, ref) => { +export const CloseButton = ({ + className, + ...props +}: AlertDialogPrimitive.Close.Props) => { return ( ); -}); +}; CloseButton.displayName = 'AlertDialog.CloseButton'; -export const AlertDialogTitle = forwardRef< - ElementRef, - AlertDialogPrimitive.Title.Props ->(({ className, ...props }, ref) => { +export const AlertDialogTitle = ({ + className, + ...props +}: AlertDialogPrimitive.Title.Props) => { return ( ); -}); +}; AlertDialogTitle.displayName = 'AlertDialog.Title'; -export const AlertDialogDescription = forwardRef< - ElementRef, - AlertDialogPrimitive.Description.Props ->(({ className, ...props }, ref) => { +export const AlertDialogDescription = ({ + className, + ...props +}: AlertDialogPrimitive.Description.Props) => { return ( ); -}); +}; AlertDialogDescription.displayName = 'AlertDialog.Description'; diff --git a/packages/raystack/components/amount/amount.tsx b/packages/raystack/components/amount/amount.tsx index 2f6964e1c..3815e737e 100644 --- a/packages/raystack/components/amount/amount.tsx +++ b/packages/raystack/components/amount/amount.tsx @@ -1,6 +1,6 @@ -import { forwardRef } from 'react'; +import { type ComponentProps } from 'react'; -export interface AmountProps { +export interface AmountProps extends ComponentProps<'span'> { /** * The monetary value to display * For large numbers (> 2^53), pass the value as string to maintain precision @@ -141,74 +141,68 @@ function isValidCurrency(currency: string): boolean { * * ``` */ -export const Amount = forwardRef( - ( - { - value = 0, - currency = 'USD', - locale = 'en-US', - hideDecimals = false, - currencyDisplay = 'symbol', - minimumFractionDigits, - maximumFractionDigits, - groupDigits = true, - valueInMinorUnits = true - }, - ref - ) => { - try { - if ( - typeof value === 'number' && - Math.abs(value) > Number.MAX_SAFE_INTEGER - ) { - console.warn( - `Warning: The number ${value} exceeds JavaScript's safe integer limit (${Number.MAX_SAFE_INTEGER}). ` + - 'For large numbers, pass the value as a string to maintain precision.' - ); - } - - const validCurrency = isValidCurrency(currency) ? currency : 'USD'; - if (validCurrency !== currency) { - console.warn( - `Invalid currency code: ${currency}. Falling back to USD.` - ); - } - - const decimals = getCurrencyDecimals(validCurrency); - - // Handle minor units - use string manipulation for strings and Math.pow for numbers - const baseValue = - valueInMinorUnits && decimals > 0 - ? typeof value === 'string' - ? value.slice(0, -decimals) + '.' + value.slice(-decimals) - : value / Math.pow(10, decimals) - : value; - - // Remove decimals if hideDecimals is true - handle string and number separately - // Note: Not all numbers passed is converted to string as methods like Math.trunc - // or toString cannot handle large numbers thus, we need to handle it separately (large numbers passed in value throws console warning). - const finalBaseValue = hideDecimals - ? typeof baseValue === 'string' - ? baseValue.split('.')[0] - : Math.trunc(baseValue) - : baseValue; - - const formattedValue = new Intl.NumberFormat(locale, { - style: 'currency' as const, - currency: validCurrency.toUpperCase(), - currencyDisplay, - minimumFractionDigits: hideDecimals ? 0 : minimumFractionDigits, - maximumFractionDigits: hideDecimals ? 0 : maximumFractionDigits, - useGrouping: groupDigits - // @ts-ignore - Handling large numbers as string or number, so we need to pass the value as string or number. - }).format(finalBaseValue); - - return {formattedValue}; - } catch (error) { - console.error('Error formatting amount:', error); - return {value}; +export const Amount = ({ + value = 0, + currency = 'USD', + locale = 'en-US', + hideDecimals = false, + currencyDisplay = 'symbol', + minimumFractionDigits, + maximumFractionDigits, + groupDigits = true, + valueInMinorUnits = true, + ...props +}: AmountProps) => { + try { + if ( + typeof value === 'number' && + Math.abs(value) > Number.MAX_SAFE_INTEGER + ) { + console.warn( + `Warning: The number ${value} exceeds JavaScript's safe integer limit (${Number.MAX_SAFE_INTEGER}). ` + + 'For large numbers, pass the value as a string to maintain precision.' + ); + } + + const validCurrency = isValidCurrency(currency) ? currency : 'USD'; + if (validCurrency !== currency) { + console.warn(`Invalid currency code: ${currency}. Falling back to USD.`); } + + const decimals = getCurrencyDecimals(validCurrency); + + // Handle minor units - use string manipulation for strings and Math.pow for numbers + const baseValue = + valueInMinorUnits && decimals > 0 + ? typeof value === 'string' + ? value.slice(0, -decimals) + '.' + value.slice(-decimals) + : value / Math.pow(10, decimals) + : value; + + // Remove decimals if hideDecimals is true - handle string and number separately + // Note: Not all numbers passed is converted to string as methods like Math.trunc + // or toString cannot handle large numbers thus, we need to handle it separately (large numbers passed in value throws console warning). + const finalBaseValue = hideDecimals + ? typeof baseValue === 'string' + ? baseValue.split('.')[0] + : Math.trunc(baseValue) + : baseValue; + + const formattedValue = new Intl.NumberFormat(locale, { + style: 'currency' as const, + currency: validCurrency.toUpperCase(), + currencyDisplay, + minimumFractionDigits: hideDecimals ? 0 : minimumFractionDigits, + maximumFractionDigits: hideDecimals ? 0 : maximumFractionDigits, + useGrouping: groupDigits + // @ts-ignore - Handling large numbers as string or number, so we need to pass the value as string or number. + }).format(finalBaseValue); + + return {formattedValue}; + } catch (error) { + console.error('Error formatting amount:', error); + return {value}; } -); +}; Amount.displayName = 'Amount'; diff --git a/packages/raystack/components/announcement-bar/announcement-bar.tsx b/packages/raystack/components/announcement-bar/announcement-bar.tsx index 817ef3d36..302c73418 100644 --- a/packages/raystack/components/announcement-bar/announcement-bar.tsx +++ b/packages/raystack/components/announcement-bar/announcement-bar.tsx @@ -1,13 +1,13 @@ 'use client'; -import { type VariantProps, cva } from 'class-variance-authority'; +import { cva, type VariantProps } from 'class-variance-authority'; import { ReactNode } from 'react'; import { Flex } from '../flex'; import { Text } from '../text'; import styles from './announcement-bar.module.css'; -const announementBar = cva(styles['announcement-bar'], { +const announcementBar = cva(styles['announcement-bar'], { variants: { variant: { gradient: styles['announcement-bar-gradient'], @@ -20,7 +20,7 @@ const announementBar = cva(styles['announcement-bar'], { } }); -type AnnouncementBarProps = VariantProps & { +type AnnouncementBarProps = VariantProps & { leadingIcon?: ReactNode; className?: string; text: string; @@ -41,7 +41,7 @@ export const AnnouncementBar = ({ }: AnnouncementBarProps) => { return ( ); }; + +AnnouncementBar.displayName = 'AnnouncementBar'; diff --git a/packages/raystack/components/avatar/avatar.tsx b/packages/raystack/components/avatar/avatar.tsx index ee0a7170c..48dde77eb 100644 --- a/packages/raystack/components/avatar/avatar.tsx +++ b/packages/raystack/components/avatar/avatar.tsx @@ -1,12 +1,6 @@ import { Avatar as AvatarPrimitive } from '@base-ui/react/avatar'; import { cva, cx, VariantProps } from 'class-variance-authority'; -import { - ComponentPropsWithoutRef, - forwardRef, - isValidElement, - ReactElement, - ReactNode -} from 'react'; +import { ComponentProps, isValidElement, ReactElement, ReactNode } from 'react'; import styles from './avatar.module.css'; import { AVATAR_COLORS } from './utils'; @@ -160,64 +154,73 @@ export interface AvatarProps className?: string; } -const AvatarRoot = forwardRef( - ( - { className, alt, src, fallback, size, radius, variant, color, ...props }, - ref - ) => ( - - - - {fallback} - - - ) +const AvatarRoot = ({ + className, + alt, + src, + fallback, + size, + radius, + variant, + color, + ...props +}: AvatarProps) => ( + + + + {fallback} + + ); -AvatarRoot.displayName = AvatarPrimitive.Root.displayName; +AvatarRoot.displayName = 'Avatar'; export const Avatar = AvatarRoot; -export interface AvatarGroupProps extends ComponentPropsWithoutRef<'div'> { +export interface AvatarGroupProps extends ComponentProps<'div'> { children: React.ReactElement[]; max?: number; } -export const AvatarGroup = forwardRef( - ({ children, max, className, ...props }, ref) => { - const avatars = max ? children.slice(0, max) : children; - const count = max && children.length > max ? children.length - max : 0; +export const AvatarGroup = ({ + children, + max, + className, + ...props +}: AvatarGroupProps) => { + const avatars = max ? children.slice(0, max) : children; + const count = max && children.length > max ? children.length - max : 0; - // Overflow avatar matches the first avatar styling - const firstAvatarProps = getAvatarProps(avatars[0]); + // Overflow avatar matches the first avatar styling + const firstAvatarProps = getAvatarProps(avatars[0]); - return ( -
- {avatars.map((avatar, index) => ( -
- {avatar} -
- ))} - {count > 0 && ( -
- -
- )} -
- ); - } -); + return ( +
+ {avatars.map((avatar, index) => ( +
+ {avatar} +
+ ))} + {count > 0 && ( +
+ +
+ )} +
+ ); +}; + +AvatarGroup.displayName = 'Avatar.Group'; diff --git a/packages/raystack/components/badge/badge.tsx b/packages/raystack/components/badge/badge.tsx index 613f545d0..222e0d9cd 100644 --- a/packages/raystack/components/badge/badge.tsx +++ b/packages/raystack/components/badge/badge.tsx @@ -1,47 +1,49 @@ -import { cva, type VariantProps } from "class-variance-authority"; -import { ReactNode } from "react"; +import { cva, type VariantProps } from 'class-variance-authority'; +import { ComponentProps, ReactNode } from 'react'; -import styles from "./badge.module.css"; +import styles from './badge.module.css'; const badge = cva(styles['badge'], { variants: { variant: { - accent: styles["badge-accent"], - warning: styles["badge-warning"], - danger: styles["badge-danger"], - success: styles["badge-success"], - neutral: styles["badge-neutral"], - gradient: styles["badge-gradient"] + accent: styles['badge-accent'], + warning: styles['badge-warning'], + danger: styles['badge-danger'], + success: styles['badge-success'], + neutral: styles['badge-neutral'], + gradient: styles['badge-gradient'] }, size: { - micro: styles["badge-micro"], - small: styles["badge-small"], - regular: styles["badge-regular"], + micro: styles['badge-micro'], + small: styles['badge-small'], + regular: styles['badge-regular'] }, defaultVariants: { - variant: "accent", - size: "small" + variant: 'accent', + size: 'small' } - }, + } }); -type BadgeProps = VariantProps & { - icon?: ReactNode; - children: ReactNode; - className?: string; - screenReaderText?: string; -} +type BadgeProps = VariantProps & + ComponentProps<'span'> & { + icon?: ReactNode; + children: ReactNode; + className?: string; + screenReaderText?: string; + }; -export const Badge = ({ +export const Badge = ({ variant = 'accent', size = 'small', icon, children, className, - screenReaderText + screenReaderText, + ...props }: BadgeProps) => { return ( - + {icon && {icon}} {screenReaderText && ( {screenReaderText} @@ -51,4 +53,4 @@ export const Badge = ({ ); }; -Badge.displayName = "Badge"; +Badge.displayName = 'Badge'; diff --git a/packages/raystack/components/box/__tests__/box.test.tsx b/packages/raystack/components/box/__tests__/box.test.tsx index 36b61e8e1..55d71d12b 100644 --- a/packages/raystack/components/box/__tests__/box.test.tsx +++ b/packages/raystack/components/box/__tests__/box.test.tsx @@ -1,7 +1,6 @@ import { render, screen } from '@testing-library/react'; import { describe, expect, it, vi } from 'vitest'; import { Box } from '../box'; -import styles from '../box.module.css'; describe('Box', () => { describe('Basic Rendering', () => { @@ -34,25 +33,6 @@ describe('Box', () => { }); }); - describe('ClassName Handling', () => { - it('applies custom className', () => { - const { container } = render(Content); - const box = container.querySelector('.custom-class'); - expect(box).toBeInTheDocument(); - expect(box).toHaveClass(styles.box); - }); - - it('combines multiple classNames', () => { - const { container } = render( - Content - ); - const box = container.querySelector(`.${styles.box}`); - expect(box).toHaveClass('class1'); - expect(box).toHaveClass('class2'); - expect(box).toHaveClass(styles.box); - }); - }); - describe('Ref Forwarding', () => { it('forwards ref correctly', () => { const ref = vi.fn(); @@ -154,8 +134,8 @@ describe('Box', () => { }); it('renders with empty string children', () => { - const { container } = render({``}); - const box = container.querySelector(`.${styles.box}`); + const { container } = render({``}); + const box = container.querySelector('#test-box'); expect(box).toBeInTheDocument(); expect(box?.textContent).toBe(''); }); diff --git a/packages/raystack/components/box/box.module.css b/packages/raystack/components/box/box.module.css deleted file mode 100644 index 9f946dcbe..000000000 --- a/packages/raystack/components/box/box.module.css +++ /dev/null @@ -1,3 +0,0 @@ -.box { - box-sizing: border-box; -} \ No newline at end of file diff --git a/packages/raystack/components/box/box.tsx b/packages/raystack/components/box/box.tsx index 9618584c3..87d923193 100644 --- a/packages/raystack/components/box/box.tsx +++ b/packages/raystack/components/box/box.tsx @@ -1,23 +1,7 @@ -import { cva, VariantProps } from "class-variance-authority"; -import { HTMLAttributes, PropsWithChildren } from "react"; +import { ComponentProps } from 'react'; -import styles from "./box.module.css"; - -// Note: This is the old component just copied in v1 folder as it is used in v1. - -const box = cva(styles.box); - -type BoxProps = PropsWithChildren> & - HTMLAttributes & { - ref?: React.RefObject; - }; - -export function Box({ children, className, ref, ...props }: BoxProps) { - return ( -
- {children} -
- ); +export function Box(props: ComponentProps<'div'>) { + return
; } -Box.displayName = "Box"; +Box.displayName = 'Box'; diff --git a/packages/raystack/components/breadcrumb/breadcrumb-item.tsx b/packages/raystack/components/breadcrumb/breadcrumb-item.tsx index 36f00e6f9..18c3d82eb 100644 --- a/packages/raystack/components/breadcrumb/breadcrumb-item.tsx +++ b/packages/raystack/components/breadcrumb/breadcrumb-item.tsx @@ -3,9 +3,8 @@ import { ChevronDownIcon } from '@radix-ui/react-icons'; import { cx } from 'class-variance-authority'; import React, { + ComponentProps, cloneElement, - forwardRef, - HTMLAttributes, ReactElement, ReactNode } from 'react'; @@ -18,80 +17,72 @@ export interface BreadcrumbDropdownItem { onClick?: React.MouseEventHandler; } -export interface BreadcrumbItemProps extends HTMLAttributes { +export interface BreadcrumbItemProps extends ComponentProps<'a'> { leadingIcon?: ReactNode; current?: boolean; dropdownItems?: BreadcrumbDropdownItem[]; - href?: string; as?: ReactElement; } -export const BreadcrumbItem = forwardRef< - HTMLAnchorElement, - BreadcrumbItemProps ->( - ( - { - as, - children, - className, - leadingIcon, - current, - href, - dropdownItems, - ...props - }, - ref - ) => { - const renderedElement = as ?? ; - const label = ( - <> - {leadingIcon && ( - {leadingIcon} - )} - {children && {children}} - - ); +export const BreadcrumbItem = ({ + ref, + as, + children, + className, + leadingIcon, + current, + href, + dropdownItems, + ...props +}: BreadcrumbItemProps) => { + const renderedElement = as ?? ; + const label = ( + <> + {leadingIcon && ( + {leadingIcon} + )} + {children && {children}} + + ); - if (dropdownItems) { - return ( - - - {label} - - - - {dropdownItems.map((dropdownItem, dropdownIndex) => ( - - {dropdownItem.label} - - ))} - - - ); - } + if (dropdownItems) { return ( -
  • - {cloneElement( - renderedElement, - { - className: cx( - styles['breadcrumb-link'], - current && styles['breadcrumb-link-active'] - ), - href, - ...props, - ...renderedElement.props - }, - label - )} -
  • + + + {label} + + + + {dropdownItems.map((dropdownItem, dropdownIndex) => ( + + {dropdownItem.label} + + ))} + + ); } -); + return ( +
  • + {cloneElement( + renderedElement, + { + className: cx( + styles['breadcrumb-link'], + current && styles['breadcrumb-link-active'] + ), + href, + ...props, + ...renderedElement.props + }, + label + )} +
  • + ); +}; -BreadcrumbItem.displayName = 'BreadcrumbItem'; +BreadcrumbItem.displayName = 'Breadcrumb.Item'; diff --git a/packages/raystack/components/breadcrumb/breadcrumb-misc.tsx b/packages/raystack/components/breadcrumb/breadcrumb-misc.tsx index 52bc07c90..83e580d88 100644 --- a/packages/raystack/components/breadcrumb/breadcrumb-misc.tsx +++ b/packages/raystack/components/breadcrumb/breadcrumb-misc.tsx @@ -2,63 +2,37 @@ import { ChevronRightIcon, DotsHorizontalIcon } from '@radix-ui/react-icons'; import { cx } from 'class-variance-authority'; -import { HTMLAttributes, forwardRef } from 'react'; +import { ComponentProps } from 'react'; import styles from './breadcrumb.module.css'; -export interface BreadcrumbEllipsisProps - extends HTMLAttributes {} +export interface BreadcrumbEllipsisProps extends ComponentProps<'span'> {} -export const BreadcrumbEllipsis = forwardRef< - HTMLSpanElement, - BreadcrumbEllipsisProps ->( - ( - { - className, - children = , - ...props - }, - ref - ) => { - return ( - - {children} - - ); - } -); +export const BreadcrumbEllipsis = ({ + className, + children = , + ...props +}: BreadcrumbEllipsisProps) => { + return ( + + {children} + + ); +}; -BreadcrumbEllipsis.displayName = 'BreadcrumbEllipsis'; +BreadcrumbEllipsis.displayName = 'Breadcrumb.Ellipsis'; -export interface BreadcrumbSeparatorProps - extends HTMLAttributes {} +export interface BreadcrumbSeparatorProps extends ComponentProps<'span'> {} -export const BreadcrumbSeparator = forwardRef< - HTMLSpanElement, - BreadcrumbSeparatorProps ->( - ( - { - children = , - className, - ...props - }, - ref - ) => { - return ( - - {children} - - ); - } -); +export const BreadcrumbSeparator = ({ + children = , + className, + ...props +}: BreadcrumbSeparatorProps) => { + return ( + + {children} + + ); +}; -BreadcrumbSeparator.displayName = 'BreadcrumbSeparator'; +BreadcrumbSeparator.displayName = 'Breadcrumb.Separator'; diff --git a/packages/raystack/components/breadcrumb/breadcrumb-root.tsx b/packages/raystack/components/breadcrumb/breadcrumb-root.tsx index 2fdded41e..3bcf4ae9c 100644 --- a/packages/raystack/components/breadcrumb/breadcrumb-root.tsx +++ b/packages/raystack/components/breadcrumb/breadcrumb-root.tsx @@ -1,7 +1,7 @@ 'use client'; -import { type VariantProps, cva } from 'class-variance-authority'; -import { HTMLAttributes, forwardRef } from 'react'; +import { cva, type VariantProps } from 'class-variance-authority'; +import { ComponentProps } from 'react'; import styles from './breadcrumb.module.css'; const breadcrumbVariants = cva(styles['breadcrumb'], { @@ -18,20 +18,19 @@ const breadcrumbVariants = cva(styles['breadcrumb'], { export interface BreadcrumbProps extends VariantProps, - HTMLAttributes {} + ComponentProps<'nav'> {} -export const BreadcrumbRoot = forwardRef( - ({ className, children, size = 'medium', ...props }, ref) => { - return ( - - ); - } -); +export const BreadcrumbRoot = ({ + className, + children, + size = 'medium', + ...props +}: BreadcrumbProps) => { + return ( + + ); +}; -BreadcrumbRoot.displayName = 'BreadcrumbRoot'; +BreadcrumbRoot.displayName = 'Breadcrumb'; diff --git a/packages/raystack/components/button/button.tsx b/packages/raystack/components/button/button.tsx index 25914fa5f..33bcbdbe7 100644 --- a/packages/raystack/components/button/button.tsx +++ b/packages/raystack/components/button/button.tsx @@ -1,6 +1,6 @@ import { Button as ButtonPrimitive } from '@base-ui/react'; import { cva, type VariantProps } from 'class-variance-authority'; -import { ElementRef, forwardRef, PropsWithChildren, ReactNode } from 'react'; +import { ReactNode } from 'react'; import { Spinner } from '../spinner'; import styles from './button.module.css'; @@ -124,7 +124,7 @@ const getLoaderOnlyClass = (size: 'small' | 'normal' | null) => ? styles['loader-only-button-small'] : styles['loader-only-button-normal']; -type ButtonProps = PropsWithChildren> & +type ButtonProps = VariantProps & ButtonPrimitive.Props & { loading?: boolean; loaderText?: ReactNode; @@ -135,69 +135,60 @@ type ButtonProps = PropsWithChildren> & style?: React.CSSProperties; }; -export const Button = forwardRef< - ElementRef, - ButtonProps ->( - ( - { - className, - variant = 'solid', - color = 'accent', - size = 'normal', - disabled, - loading, - loaderText, - leadingIcon, - trailingIcon, - maxWidth, - width, - style = {}, - children, - render, - ...props - }, - ref - ) => { - const isLoaderOnly = loading && !loaderText; - const widthStyle = { maxWidth, width }; - const buttonStyle = { ...widthStyle, ...style }; +export const Button = ({ + className, + variant = 'solid', + color = 'accent', + size = 'normal', + disabled, + loading, + loaderText, + leadingIcon, + trailingIcon, + maxWidth, + width, + style = {}, + children, + render, + ...props +}: ButtonProps) => { + const isLoaderOnly = loading && !loaderText; + const widthStyle = { maxWidth, width }; + const buttonStyle = { ...widthStyle, ...style }; - return ( - - {loading ? ( - <> - - {loaderText && ( - {loaderText} - )} - - ) : ( - <> - {leadingIcon && ( - - {leadingIcon} - - )} - {children} - {trailingIcon && ( - - {trailingIcon} - - )} - - )} - - ); - } -); + return ( + + {loading ? ( + <> + + {loaderText && ( + {loaderText} + )} + + ) : ( + <> + {leadingIcon && ( + + {leadingIcon} + + )} + {children} + {trailingIcon && ( + + {trailingIcon} + + )} + + )} + + ); +}; Button.displayName = 'Button'; diff --git a/packages/raystack/components/calendar/calendar.tsx b/packages/raystack/components/calendar/calendar.tsx index 0f3096c22..1e4c92991 100644 --- a/packages/raystack/components/calendar/calendar.tsx +++ b/packages/raystack/components/calendar/calendar.tsx @@ -210,3 +210,5 @@ export const Calendar = function ({ /> ); }; + +Calendar.displayName = 'Calendar'; diff --git a/packages/raystack/components/calendar/date-picker.tsx b/packages/raystack/components/calendar/date-picker.tsx index e06d0a317..531d19d87 100644 --- a/packages/raystack/components/calendar/date-picker.tsx +++ b/packages/raystack/components/calendar/date-picker.tsx @@ -4,7 +4,13 @@ import { CalendarIcon } from '@radix-ui/react-icons'; import { cx } from 'class-variance-authority'; import dayjs from 'dayjs'; import customParseFormat from 'dayjs/plugin/customParseFormat'; -import { useCallback, useEffect, useRef, useState } from 'react'; +import { + isValidElement, + useCallback, + useEffect, + useRef, + useState +} from 'react'; import { PropsBase, PropsSingleRequired } from 'react-day-picker'; import { InputField } from '../input-field'; import { InputFieldProps } from '../input-field/input-field'; @@ -193,7 +199,9 @@ export function DatePicker({ return ( - {trigger} + {trigger}} + /> ); } + +DatePicker.displayName = 'DatePicker'; diff --git a/packages/raystack/components/calendar/range-picker.tsx b/packages/raystack/components/calendar/range-picker.tsx index 416cd1592..d1eec604d 100644 --- a/packages/raystack/components/calendar/range-picker.tsx +++ b/packages/raystack/components/calendar/range-picker.tsx @@ -3,7 +3,7 @@ import { CalendarIcon } from '@radix-ui/react-icons'; import { cx } from 'class-variance-authority'; import dayjs from 'dayjs'; -import { useCallback, useMemo, useState } from 'react'; +import { isValidElement, useCallback, useMemo, useState } from 'react'; import { DateRange, PropsBase, PropsRangeRequired } from 'react-day-picker'; import { Flex } from '../flex'; import { InputField } from '../input-field'; @@ -167,7 +167,9 @@ export function RangePicker({ return ( - {trigger} + {trigger}} + /> ); } + +RangePicker.displayName = 'RangePicker';