Skip to content

Commit

Permalink
feat: export components from root
Browse files Browse the repository at this point in the history
  • Loading branch information
Pagebakers committed Nov 24, 2024
1 parent 7808a76 commit f62c3d6
Show file tree
Hide file tree
Showing 16 changed files with 367 additions and 170 deletions.
5 changes: 5 additions & 0 deletions .changeset/dry-berries-report.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@saas-ui/react': minor
---

Export all components from root barrel file
75 changes: 75 additions & 0 deletions packages/saas-ui-react/src/components/avatar/avatar.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
'use client'

import { forwardRef } from 'react'

import type { GroupProps, SlotRecipeProps } from '@chakra-ui/react'
import { Avatar as ChakraAvatar, Group } from '@chakra-ui/react'

type ImageProps = React.ImgHTMLAttributes<HTMLImageElement>

export interface AvatarProps extends ChakraAvatar.RootProps {
name?: string
src?: string
srcSet?: string
loading?: ImageProps['loading']
icon?: React.ReactElement
fallback?: React.ReactNode
}

export const Avatar = forwardRef<HTMLDivElement, AvatarProps>(
function Avatar(props, ref) {
const { name, src, srcSet, loading, icon, fallback, children, ...rest } =
props
return (
<ChakraAvatar.Root ref={ref} {...rest}>
<AvatarFallback name={name} icon={icon}>
{fallback}
</AvatarFallback>
<ChakraAvatar.Image src={src} srcSet={srcSet} loading={loading} />
{children}
</ChakraAvatar.Root>
)
},
)

interface AvatarFallbackProps extends ChakraAvatar.FallbackProps {
name?: string
icon?: React.ReactElement
}

const AvatarFallback = forwardRef<HTMLDivElement, AvatarFallbackProps>(
function AvatarFallback(props, ref) {
const { name, icon, children, ...rest } = props
return (
<ChakraAvatar.Fallback ref={ref} {...rest}>
{children}
{name != null && children == null && <>{getInitials(name)}</>}
{name == null && children == null && (
<ChakraAvatar.Icon asChild={!!icon}>{icon}</ChakraAvatar.Icon>
)}
</ChakraAvatar.Fallback>
)
},
)

function getInitials(name: string) {
const names = name.trim().split(' ')
const firstName = names[0] != null ? names[0] : ''
const lastName = names.length > 1 ? names[names.length - 1] : ''
return firstName && lastName
? `${firstName.charAt(0)}${lastName.charAt(0)}`
: firstName.charAt(0)
}

interface AvatarGroupProps extends GroupProps, SlotRecipeProps<'avatar'> {}

export const AvatarGroup = forwardRef<HTMLDivElement, AvatarGroupProps>(
function AvatarGroup(props, ref) {
const { size, variant, borderless, ...rest } = props
return (
<ChakraAvatar.PropsProvider value={{ size, variant, borderless }}>
<Group gap="0" spaceX="-3" ref={ref} {...rest} />
</ChakraAvatar.PropsProvider>
)
},
)
1 change: 1 addition & 0 deletions packages/saas-ui-react/src/components/avatar/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export * from './avatar.tsx'
1 change: 1 addition & 0 deletions packages/saas-ui-react/src/components/menu/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export * as Menu from './menu.tsx'
129 changes: 129 additions & 0 deletions packages/saas-ui-react/src/components/menu/menu.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,129 @@
'use client'

import { forwardRef } from 'react'

import { AbsoluteCenter, Menu as ChakraMenu, Portal } from '@chakra-ui/react'
import { LuCheck, LuChevronRight } from 'react-icons/lu'

interface MenuContentProps extends ChakraMenu.ContentProps {
portalled?: boolean
portalRef?: React.RefObject<HTMLElement>
}

const MenuContent = forwardRef<HTMLDivElement, MenuContentProps>(
function MenuContent(props, ref) {
const { portalled = true, portalRef, ...rest } = props
return (
<Portal disabled={!portalled} container={portalRef}>
<ChakraMenu.Positioner>
<ChakraMenu.Content ref={ref} {...rest} />
</ChakraMenu.Positioner>
</Portal>
)
},
)

const MenuArrow = forwardRef<HTMLDivElement, ChakraMenu.ArrowProps>(
function MenuArrow(props, ref) {
return (
<ChakraMenu.Arrow ref={ref} {...props}>
<ChakraMenu.ArrowTip />
</ChakraMenu.Arrow>
)
},
)

const MenuCheckboxItem = forwardRef<
HTMLDivElement,
ChakraMenu.CheckboxItemProps
>(function MenuCheckboxItem(props, ref) {
return (
<ChakraMenu.CheckboxItem ref={ref} {...props}>
<ChakraMenu.ItemIndicator hidden={false}>
<LuCheck />
</ChakraMenu.ItemIndicator>
{props.children}
</ChakraMenu.CheckboxItem>
)
})

const MenuRadioItem = forwardRef<HTMLDivElement, ChakraMenu.RadioItemProps>(
function MenuRadioItem(props, ref) {
const { children, ...rest } = props
return (
<ChakraMenu.RadioItem ps="8" ref={ref} {...rest}>
<AbsoluteCenter axis="horizontal" left="4" asChild>
<ChakraMenu.ItemIndicator>
<LuCheck />
</ChakraMenu.ItemIndicator>
</AbsoluteCenter>
<ChakraMenu.ItemText>{children}</ChakraMenu.ItemText>
</ChakraMenu.RadioItem>
)
},
)

const MenuItemGroup = forwardRef<HTMLDivElement, ChakraMenu.ItemGroupProps>(
function MenuItemGroup(props, ref) {
const { title, children, ...rest } = props
return (
<ChakraMenu.ItemGroup ref={ref} {...rest}>
{title && (
<ChakraMenu.ItemGroupLabel userSelect="none">
{title}
</ChakraMenu.ItemGroupLabel>
)}
{children}
</ChakraMenu.ItemGroup>
)
},
)

interface MenuTriggerItemProps extends ChakraMenu.ItemProps {
startIcon?: React.ReactNode
}

const MenuTriggerItem = forwardRef<HTMLDivElement, MenuTriggerItemProps>(
function MenuTriggerItem(props, ref) {
const { startIcon, children, ...rest } = props
return (
<ChakraMenu.TriggerItem ref={ref} {...rest}>
{startIcon}
{children}
<LuChevronRight />
</ChakraMenu.TriggerItem>
)
},
)

const MenuRadioItemGroup = ChakraMenu.RadioItemGroup
const MenuContextTrigger = ChakraMenu.ContextTrigger
const MenuRoot = ChakraMenu.Root
const MenuSeparator = ChakraMenu.Separator

const MenuItem = ChakraMenu.Item
const MenuItemText = ChakraMenu.ItemText
const MenuItemCommand = ChakraMenu.ItemCommand
const MenuTrigger = ChakraMenu.Trigger

export {
MenuRoot as Root,
MenuContent as Content,
MenuArrow as Arrow,
MenuCheckboxItem as CheckboxItem,
MenuRadioItem as RadioItem,
MenuItemGroup as ItemGroup,
MenuTriggerItem as TriggerItem,
MenuRadioItemGroup as RadioItemGroup,
MenuContextTrigger as ContextTrigger,
MenuSeparator as Separator,
MenuItem as Item,
MenuItemText as ItemText,
MenuItemCommand as ItemCommand,
MenuTrigger as Trigger,
}

export type {
MenuContentProps as ContentProps,
MenuTriggerItemProps as TriggerItemProps,
}
Empty file.
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
'use client'

import { forwardRef, useMemo } from 'react'

import { For, SegmentGroup } from '@chakra-ui/react'

interface Item {
value: string
label: React.ReactNode
disabled?: boolean
}

export interface SegmentedControlProps extends SegmentGroup.RootProps {
items: Array<string | Item>
}

function normalize(items: Array<string | Item>): Item[] {
return items.map((item) => {
if (typeof item === 'string') return { value: item, label: item }
return item
})
}

export const SegmentedControl = forwardRef<
HTMLDivElement,
SegmentedControlProps
>(function SegmentedControl(props, ref) {
const { items, ...rest } = props
const data = useMemo(() => normalize(items), [items])

return (
<SegmentGroup.Root ref={ref} {...rest}>
<SegmentGroup.Indicator />
<For each={data}>
{(item) => (
<SegmentGroup.Item
key={item.value}
value={item.value}
disabled={item.disabled}
>
<SegmentGroup.ItemText>{item.label}</SegmentGroup.ItemText>
<SegmentGroup.ItemHiddenInput />
</SegmentGroup.Item>
)}
</For>
</SegmentGroup.Root>
)
})
2 changes: 2 additions & 0 deletions packages/saas-ui-react/src/components/snackbar/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
export * from './snackbar.tsx'
export { useSnackbar } from './snackbar.context.ts'
10 changes: 10 additions & 0 deletions packages/saas-ui-react/src/components/snackbar/snackbar.context.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import { type CreateToasterReturn, createContext } from '@chakra-ui/react'

export interface SnackbarContextValue {
snackbar: CreateToasterReturn
}

export const [SnackbarProvider, useSnackbar] =
createContext<SnackbarContextValue>({
name: 'SnackbarContext',
})
61 changes: 61 additions & 0 deletions packages/saas-ui-react/src/components/snackbar/snackbar.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
'use client'

import { useMemo } from 'react'

import {
Toaster as ChakraToaster,
CreateToasterReturn,
Portal,
Spinner,
Stack,
Toast,
createToaster,
} from '@chakra-ui/react'

import { SnackbarProvider } from './snackbar.context.ts'

export interface SnackbarProps extends CreateToasterReturn {
children: React.ReactNode
}

export const Snackbar = (props: SnackbarProps) => {
const { children, ...rest } = props

const toaster = useMemo(
() =>
createToaster({
placement: 'bottom-end',
pauseOnPageIdle: true,
...rest,
}),
[],
)

return (
<SnackbarProvider value={{ snackbar: toaster }}>
<Portal>
<ChakraToaster toaster={toaster} insetInline={{ mdDown: '4' }}>
{(toast) => (
<Toast.Root width={{ md: 'sm' }}>
{toast.type === 'loading' ? (
<Spinner size="sm" color="colorPalette.solid" />
) : (
<Toast.Indicator />
)}
<Stack gap="1" flex="1" maxWidth="100%">
{toast.title && <Toast.Title>{toast.title}</Toast.Title>}
{toast.description && (
<Toast.Description>{toast.description}</Toast.Description>
)}
</Stack>
{toast.action && (
<Toast.ActionTrigger>{toast.action.label}</Toast.ActionTrigger>
)}
{toast.meta?.closable && <Toast.CloseTrigger />}
</Toast.Root>
)}
</ChakraToaster>
</Portal>
</SnackbarProvider>
)
}
1 change: 1 addition & 0 deletions packages/saas-ui-react/src/components/tooltip/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export * from './tooltip.tsx'
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { Tooltip as ChakraTooltip, Portal } from "@chakra-ui/react"
import { forwardRef } from "react"
import { forwardRef } from 'react'

import { Tooltip as ChakraTooltip, Portal } from '@chakra-ui/react'

export interface TooltipProps extends ChakraTooltip.RootProps {
showArrow?: boolean
Expand Down
34 changes: 0 additions & 34 deletions packages/saas-ui-react/src/compositions/empty-state.tsx

This file was deleted.

Loading

0 comments on commit f62c3d6

Please sign in to comment.