-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
ee8597c
commit 7ad6843
Showing
7 changed files
with
204 additions
and
35 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,50 @@ | ||
import type { Meta, StoryObj } from "@storybook/react" | ||
import { IconUserBolt } from "@tabler/icons-react" | ||
|
||
import { Blurb } from "./Blurb" | ||
|
||
type Story = StoryObj<typeof Blurb> | ||
|
||
// Meta | ||
export default { | ||
title: "UI/Blurb", | ||
component: Blurb, | ||
args: { | ||
...Blurb.defaultProps, | ||
avatar: { | ||
src: "https://images.unsplash.com/photo-1517841905240-472988babdf9?q=80&w=250&h=250&auto=format&fit=crop", | ||
size: "lg", | ||
}, | ||
title: "John Doe", | ||
description: "Software Engineer", | ||
}, | ||
} satisfies Meta | ||
|
||
// Stories | ||
export const Default = { | ||
args: {}, | ||
} satisfies Story | ||
|
||
export const WithInitials = { | ||
args: { | ||
avatar: { | ||
initials: "John Doe", | ||
size: "lg", | ||
}, | ||
}, | ||
} satisfies Story | ||
|
||
export const WithCustomMarkup = { | ||
render: ({ avatar, title, description }) => ( | ||
<Blurb.Root className="rounded-md border p-3"> | ||
<IconUserBolt className="text-xs" /> | ||
|
||
<Blurb.Content> | ||
<Blurb.Description>{description}</Blurb.Description> | ||
<Blurb.Title>{title}</Blurb.Title> | ||
</Blurb.Content> | ||
|
||
<Blurb.Avatar {...avatar} /> | ||
</Blurb.Root> | ||
), | ||
} satisfies Story |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,132 @@ | ||
import { Slot } from "@radix-ui/react-slot" | ||
import { forwardRef, isValidElement } from "react" | ||
import type { ComponentPropsWithoutRef } from "react" | ||
|
||
import { type VariantProps, cx } from "~/shared/cva" | ||
import { Avatar, type AvatarElement, type AvatarProps } from "~/ui/Avatar" | ||
|
||
import { | ||
blurbContentVariants, | ||
blurbVariants, | ||
blurbDescriptionVariants, | ||
blurbTitleVariants, | ||
} from "./Blurb.variants" | ||
|
||
export type BlurbElement = HTMLDivElement | ||
|
||
type BlurbRootProps = ComponentPropsWithoutRef<"div"> & | ||
VariantProps<typeof blurbVariants> & { | ||
/** | ||
* If set to `true`, the button will be rendered as a child within the component. | ||
* This child component must be a valid React component. | ||
*/ | ||
asChild?: boolean | ||
} | ||
|
||
export type BlurbProps = BlurbRootProps & { | ||
/** | ||
* Represents the avatar displayed on the Blurb. | ||
*/ | ||
avatar?: AvatarProps | ||
|
||
/** | ||
* Represents the title displayed on the Blurb. | ||
*/ | ||
title?: string | ||
|
||
/** | ||
* Represents the description displayed on the Blurb. | ||
*/ | ||
description?: string | ||
} | ||
|
||
const BlurbRoot = forwardRef<BlurbElement, BlurbRootProps>( | ||
({ className, asChild, ...props }, ref) => { | ||
const useAsChild = asChild && isValidElement(props.children) | ||
const Component = useAsChild ? Slot : "div" | ||
|
||
return <Component ref={ref} className={cx(blurbVariants({ className }))} {...props} /> | ||
}, | ||
) | ||
|
||
const BlurbAvatar = forwardRef<AvatarElement, AvatarProps>(({ ...props }, ref) => { | ||
return <Avatar ref={ref} {...props} /> | ||
}) | ||
|
||
const BlurbContent = forwardRef< | ||
HTMLDivElement, | ||
ComponentPropsWithoutRef<"div"> & VariantProps<typeof blurbContentVariants> | ||
>(({ className, ...props }, ref) => { | ||
return <div ref={ref} className={cx(blurbContentVariants({ className }))} {...props} /> | ||
}) | ||
|
||
const BlurbTitle = forwardRef< | ||
HTMLSpanElement, | ||
ComponentPropsWithoutRef<"span"> & VariantProps<typeof blurbTitleVariants> | ||
>(({ children, className, ...rest }, ref) => { | ||
if (!children) { | ||
return null | ||
} | ||
|
||
return ( | ||
<span ref={ref} className={cx(blurbTitleVariants({ className }))} {...rest}> | ||
{children} | ||
</span> | ||
) | ||
}) | ||
|
||
const BlurbDescription = forwardRef< | ||
HTMLSpanElement, | ||
ComponentPropsWithoutRef<"span"> & VariantProps<typeof blurbDescriptionVariants> | ||
>(({ children, className, ...rest }, ref) => { | ||
if (!children) { | ||
return null | ||
} | ||
|
||
return ( | ||
<span ref={ref} className={cx(blurbDescriptionVariants({ className }))} {...rest}> | ||
{children} | ||
</span> | ||
) | ||
}) | ||
|
||
const BlurbBase = forwardRef<BlurbElement, BlurbProps>((props, ref) => { | ||
const { children, avatar, title, description, ...rest } = props | ||
|
||
return ( | ||
<BlurbRoot ref={ref} {...rest}> | ||
{avatar && <BlurbAvatar {...avatar} />} | ||
|
||
{(title || description) && ( | ||
<BlurbContent> | ||
<BlurbTitle>{title}</BlurbTitle> | ||
<BlurbDescription>{description}</BlurbDescription> | ||
</BlurbContent> | ||
)} | ||
|
||
{children} | ||
</BlurbRoot> | ||
) | ||
}) | ||
|
||
BlurbBase.displayName = "Blurb" | ||
BlurbRoot.displayName = "BlurbRoot" | ||
BlurbAvatar.displayName = "BlurbAvatar" | ||
BlurbContent.displayName = "BlurbContent" | ||
BlurbTitle.displayName = "BlurbTitle" | ||
BlurbDescription.displayName = "BlurbDescription" | ||
|
||
export const Blurb = Object.assign(BlurbBase, { | ||
Root: BlurbRoot, | ||
Avatar: BlurbAvatar, | ||
Content: BlurbContent, | ||
Title: BlurbTitle, | ||
Description: BlurbDescription, | ||
}) | ||
|
||
Blurb.defaultProps = { | ||
avatar: Avatar.defaultProps, | ||
title: "", | ||
description: "", | ||
asChild: false, | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,17 @@ | ||
import { cva } from "~/shared/cva" | ||
|
||
export const blurbVariants = cva({ | ||
base: "flex items-center gap-3 text-start", | ||
}) | ||
|
||
export const blurbContentVariants = cva({ | ||
base: "flex min-w-0 flex-1 flex-col gap-0.5", | ||
}) | ||
|
||
export const blurbTitleVariants = cva({ | ||
base: "text-sm font-medium truncate", | ||
}) | ||
|
||
export const blurbDescriptionVariants = cva({ | ||
base: "text-xs leading-tight opacity-60 truncate", | ||
}) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,2 @@ | ||
export { Blurb } from "./Blurb" | ||
export type { BlurbProps, BlurbElement } from "./Blurb" |