Skip to content

Commit

Permalink
feat: add components
Browse files Browse the repository at this point in the history
  • Loading branch information
Pagebakers committed Feb 15, 2025
1 parent ce8c247 commit 15e093e
Show file tree
Hide file tree
Showing 36 changed files with 480 additions and 86 deletions.
5 changes: 5 additions & 0 deletions .changeset/poor-dots-learn.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@saas-ui/react': minor
---

Added new components, Accordion, Alert, Checkbox, HoverCard
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -107,7 +107,7 @@
"react": "18.3.1",
"react-dom": "18.3.1",
"@emotion/react": "11.11.1",
"@chakra-ui/react": "3.4.0",
"@chakra-ui/react": "3.7.0",
"@ark-ui/react": "4.8.1"
},
"packageManager": "yarn@4.6.0"
Expand Down
2 changes: 1 addition & 1 deletion packages/saas-ui-react/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,7 @@
"@saas-ui/hooks": "workspace:*"
},
"peerDependencies": {
"@chakra-ui/react": "^3.2.2",
"@chakra-ui/react": "^3.7.0",
"@emotion/react": "^11",
"react": ">=18",
"react-dom": ">=18"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,14 +7,15 @@ export const accordionSlotRecipe = defineSlotRecipe({
base: {
root: {
width: 'full',
'--accordion-radius': 'radii.l2',
'--accordion-radius': 'radii.panel.md',
},
item: {
overflowAnchor: 'none',
},
itemTrigger: {
display: 'flex',
alignItems: 'center',
justifyContent: 'space-between',
width: 'full',
outline: '0',
gap: '3',
Expand Down Expand Up @@ -49,7 +50,7 @@ export const accordionSlotRecipe = defineSlotRecipe({
transformOrigin: 'center',
color: 'fg.subtle',
_open: {
rotate: '180deg',
rotate: '90deg',
},
_icon: {
width: '1.2em',
Expand Down
54 changes: 54 additions & 0 deletions packages/saas-ui-react/src/components/accordion/accordion.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
'use client'

import * as React from 'react'

import { Accordion } from '@chakra-ui/react'

import { ChevronRightIcon } from '../icons/icons.tsx'

export interface ItemTriggerProps extends Accordion.ItemTriggerProps {
indicatorIcon?: React.ReactNode
indicatorPlacement?: 'start' | 'end'
}

export const ItemTrigger = React.forwardRef<
HTMLButtonElement,
ItemTriggerProps
>(function AccordionItemTrigger(props, ref) {
const {
children,
indicatorPlacement = 'end',
indicatorIcon = <ChevronRightIcon />,
...rest
} = props

const indicator = (
<Accordion.ItemIndicator>{indicatorIcon}</Accordion.ItemIndicator>
)

return (
<Accordion.ItemTrigger {...rest} ref={ref}>
{indicatorPlacement === 'start' && indicator}
{children}
{indicatorPlacement === 'end' && indicator}
</Accordion.ItemTrigger>
)
})

export interface ItemContentProps extends Accordion.ItemContentProps {}

export const ItemContent = React.forwardRef<HTMLDivElement, ItemContentProps>(
function AccordionItemContent(props, ref) {
return (
<Accordion.ItemContent>
<Accordion.ItemBody {...props} ref={ref} />
</Accordion.ItemContent>
)
},
)

export const Root = Accordion.Root
export const Item = Accordion.Item

export type RootProps = Accordion.RootProps
export type ItemProps = Accordion.ItemProps
1 change: 1 addition & 0 deletions packages/saas-ui-react/src/components/accordion/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export * as Accordion from './accordion.tsx'
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@ export const alertSlotRecipe = defineSlotRecipe({
display: 'flex',
alignItems: 'flex-start',
position: 'relative',
borderRadius: 'l3',
},
title: {
fontWeight: 'medium',
Expand Down Expand Up @@ -82,7 +81,7 @@ export const alertSlotRecipe = defineSlotRecipe({
bg: 'colorPalette.subtle',
color: 'colorPalette.fg',
shadow: 'inset 0 0 0px 1px var(--shadow-color)',
shadowColor: 'colorPalette.muted',
shadowColor: 'colorPalette.solid',
},
indicator: {
color: 'colorPalette.fg',
Expand All @@ -93,7 +92,7 @@ export const alertSlotRecipe = defineSlotRecipe({
root: {
color: 'colorPalette.fg',
shadow: 'inset 0 0 0px 1px var(--shadow-color)',
shadowColor: 'colorPalette.muted',
shadowColor: 'colorPalette.subtle',
},
indicator: {
color: 'colorPalette.fg',
Expand All @@ -118,6 +117,7 @@ export const alertSlotRecipe = defineSlotRecipe({
px: '3',
py: '3',
textStyle: 'xs',
borderRadius: 'panel.sm',
},
indicator: {
textStyle: 'lg',
Expand All @@ -129,6 +129,7 @@ export const alertSlotRecipe = defineSlotRecipe({
px: '4',
py: '4',
textStyle: 'sm',
borderRadius: 'panel.md',
},
indicator: {
textStyle: 'xl',
Expand All @@ -140,6 +141,7 @@ export const alertSlotRecipe = defineSlotRecipe({
px: '4',
py: '4',
textStyle: 'md',
borderRadius: 'panel.lg',
},
indicator: {
textStyle: '2xl',
Expand Down
57 changes: 57 additions & 0 deletions packages/saas-ui-react/src/components/alert/alert.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
'use client'

import { forwardRef } from 'react'

import { Alert as AlertPrimitive } from '@chakra-ui/react'

import { CloseButton } from '../close-button'

export interface AlertProps extends Omit<AlertPrimitive.RootProps, 'title'> {
startElement?: React.ReactNode
endElement?: React.ReactNode
title?: React.ReactNode
icon?: React.ReactElement
closable?: boolean
onClose?: () => void
}

export const Alert = forwardRef<HTMLDivElement, AlertProps>(
function Alert(props, ref) {
const {
title,
children,
icon,
closable,
onClose,
startElement,
endElement,
...rest
} = props
return (
<AlertPrimitive.Root ref={ref} {...rest}>
{startElement || (
<AlertPrimitive.Indicator>{icon}</AlertPrimitive.Indicator>
)}
{children ? (
<AlertPrimitive.Content>
<AlertPrimitive.Title>{title}</AlertPrimitive.Title>
<AlertPrimitive.Description>{children}</AlertPrimitive.Description>
</AlertPrimitive.Content>
) : (
<AlertPrimitive.Title flex="1">{title}</AlertPrimitive.Title>
)}
{endElement}
{closable && (
<CloseButton
size="sm"
pos="relative"
top="-2"
insetEnd="-2"
alignSelf="flex-start"
onClick={onClose}
/>
)}
</AlertPrimitive.Root>
)
},
)
1 change: 1 addition & 0 deletions packages/saas-ui-react/src/components/alert/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export { Alert, type AlertProps } from './alert.tsx'
2 changes: 1 addition & 1 deletion packages/saas-ui-react/src/components/badge/index.ts
Original file line number Diff line number Diff line change
@@ -1 +1 @@
export { Badge, type BadgeProps } from '@chakra-ui/react'
export { Badge, BadgePropsProvider, type BadgeProps } from '@chakra-ui/react'
21 changes: 7 additions & 14 deletions packages/saas-ui-react/src/components/breadcrumb/breadcrumb.tsx
Original file line number Diff line number Diff line change
@@ -1,13 +1,15 @@
'use client'

import { Children, Fragment, forwardRef, isValidElement } from 'react'

import { Breadcrumb, type SystemStyleObject } from '@chakra-ui/react'

interface BreadcrumbRootProps extends Breadcrumb.RootProps {
export interface RootProps extends Breadcrumb.RootProps {
separator?: React.ReactNode
separatorGap?: SystemStyleObject['gap']
}

const BreadcrumbRoot = forwardRef<HTMLDivElement, BreadcrumbRootProps>(
export const Root = forwardRef<HTMLDivElement, RootProps>(
function BreadcrumbRoot(props, ref) {
const { separator = '/', separatorGap, children, ...rest } = props
const validChildren = Children.toArray(children).filter(isValidElement)
Expand All @@ -31,15 +33,6 @@ const BreadcrumbRoot = forwardRef<HTMLDivElement, BreadcrumbRootProps>(
},
)

const BreadcrumbLink = Breadcrumb.Link
const BreadcrumbCurrentLink = Breadcrumb.CurrentLink
const BreadcrumbEllipsis = Breadcrumb.Ellipsis

export {
BreadcrumbRoot as Root,
BreadcrumbLink as Link,
BreadcrumbCurrentLink as CurrentLink,
BreadcrumbEllipsis as Ellipsis,
}

export type { BreadcrumbRootProps as RootProps }
export const Link = Breadcrumb.Link
export const CurrentLink = Breadcrumb.CurrentLink
export const Ellipsis = Breadcrumb.Ellipsis
2 changes: 2 additions & 0 deletions packages/saas-ui-react/src/components/button-group/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
export { ButtonGroup } from '@chakra-ui/react/button'
export type { ButtonGroupProps } from '@chakra-ui/react/button'
2 changes: 2 additions & 0 deletions packages/saas-ui-react/src/components/button/button.tsx
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
'use client'

import { forwardRef } from 'react'

import type { ButtonProps as ButtonPrimitiveProps } from '@chakra-ui/react'
Expand Down
2 changes: 2 additions & 0 deletions packages/saas-ui-react/src/components/card/index.ts
Original file line number Diff line number Diff line change
@@ -1 +1,3 @@
'use client'

export { Card } from '@chakra-ui/react'
Original file line number Diff line number Diff line change
@@ -1,13 +1,14 @@
import { defineSlotRecipe } from '@chakra-ui/react'
import { checkboxAnatomy } from '@chakra-ui/react/anatomy'

import { checkmarkRecipe } from './checkmark'
import { checkmarkRecipe } from '../checkmark/checkmark.recipe'

export const checkboxSlotRecipe = defineSlotRecipe({
slots: checkboxAnatomy.keys(),
className: 'chakra-checkbox',
base: {
root: {
colorPalette: 'accent',
display: 'inline-flex',
gap: '2',
alignItems: 'center',
Expand Down
1 change: 1 addition & 0 deletions packages/saas-ui-react/src/components/checkmark/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export { Checkmark, type CheckmarkProps } from '@chakra-ui/react'
97 changes: 97 additions & 0 deletions packages/saas-ui-react/src/components/clipboard/clipboard.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
import * as React from 'react'

import { Clipboard as ChakraClipboard } from '@chakra-ui/react'

import { Button, type ButtonProps } from '../button'
import { IconButton, type IconButtonProps } from '../icon-button'
import { CheckIcon, CopyIcon } from '../icons'
import { Input, type InputProps } from '../input'

const ClipboardIcon = React.forwardRef<
HTMLDivElement,
ChakraClipboard.IndicatorProps
>(function ClipboardIndicator(props, ref) {
const { children = <CopyIcon />, copied = <CheckIcon />, ...rest } = props
return (
<ChakraClipboard.Indicator copied={copied} {...rest} ref={ref}>
{children}
</ChakraClipboard.Indicator>
)
})

const ClipboardCopyText = React.forwardRef<
HTMLDivElement,
ChakraClipboard.IndicatorProps
>(function ClipboardCopyText(props, ref) {
const { children = 'Copy', copied = 'Copied', ...rest } = props
return (
<ChakraClipboard.Indicator copied={copied} {...rest} ref={ref}>
{children}
</ChakraClipboard.Indicator>
)
})

interface ClipboardButtonProps extends ButtonProps {
icon?: React.ReactNode
copiedIcon?: React.ReactNode
copied?: string
}

const ClipboardButton = React.forwardRef<
HTMLButtonElement,
ClipboardButtonProps
>(function ClipboardButton(props, ref) {
const { icon, copiedIcon, copied, children, ...rest } = props
return (
<ChakraClipboard.Trigger asChild>
<Button ref={ref} {...rest}>
<ClipboardIcon copied={copiedIcon}>{icon}</ClipboardIcon>
<ClipboardCopyText copied={copied}>{children}</ClipboardCopyText>
</Button>
</ChakraClipboard.Trigger>
)
})

interface ClipboardIconButtonProps extends IconButtonProps {
icon?: React.ReactNode
copiedIcon?: React.ReactNode
}

const ClipboardIconButton = React.forwardRef<
HTMLButtonElement,
ClipboardIconButtonProps
>(function ClipboardIconButton(props, ref) {
const { icon, copiedIcon, ...rest } = props
return (
<ChakraClipboard.Trigger asChild>
<IconButton ref={ref} size="xs" {...rest}>
<ClipboardIcon copied={copiedIcon}>{icon}</ClipboardIcon>
</IconButton>
</ChakraClipboard.Trigger>
)
})

const ClipboardInput = React.forwardRef<HTMLInputElement, InputProps>(
function ClipboardInputElement(props, ref) {
return (
<ChakraClipboard.Input asChild>
<Input ref={ref} {...props} />
</ChakraClipboard.Input>
)
},
)

const ClipboardRoot = ChakraClipboard.Root
const ClipboardLabel = ChakraClipboard.Label

export {
ClipboardButton as Button,
ClipboardIconButton as IconButton,
ClipboardInput as Input,
ClipboardLabel as Label,
ClipboardRoot as Root,
}

type ClipboardRootProps = ChakraClipboard.RootProps

export type { ClipboardRootProps as RootProps }
1 change: 1 addition & 0 deletions packages/saas-ui-react/src/components/clipboard/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export * as Clipboard from './clipboard'
Loading

0 comments on commit 15e093e

Please sign in to comment.