-
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
Showing
11 changed files
with
274 additions
and
31 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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
--- | ||
"@spear-ai/storybook": minor | ||
"@spear-ai/ui": minor | ||
--- | ||
|
||
Created IconButton component(s). |
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,5 @@ | ||
--- | ||
"@spear-ai/ui": minor | ||
--- | ||
|
||
Made Spinner component more composable by making it inherit the text color. |
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,5 @@ | ||
--- | ||
"@spear-ai/ui": minor | ||
--- | ||
|
||
Changed Storybook Spinner color from `step-9` to `step-11` for consistency with loading states inside of UI components. |
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
121 changes: 121 additions & 0 deletions
121
packages/storybook/src/components/icon-button/index.stories.tsx
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,121 @@ | ||
import { ArrowTopRightIcon, PlusIcon } from "@radix-ui/react-icons"; | ||
import { | ||
IconButton, | ||
IconButtonIcon, | ||
IconButtonSpinner, | ||
LinkIconButton, | ||
} from "@spear-ai/ui/components/icon-button"; | ||
import type { Meta, StoryObj } from "@storybook/react"; | ||
import { useIntl } from "react-intl"; | ||
|
||
const PreviewIconButton = (properties: { | ||
color: "negative" | "neutral" | "positive" | "primary" | "x-negative" | "x-positive"; | ||
hasIcon: boolean; | ||
isCircle: boolean; | ||
isDisabled: boolean; | ||
isLink: boolean; | ||
isLoading: boolean; | ||
isSkeleton: boolean; | ||
size: "size-7" | "size-8" | "size-9"; | ||
variant: "ghost" | "outline" | "soft" | "solid"; | ||
}) => { | ||
const { color, hasIcon, isCircle, isDisabled, isLink, isLoading, isSkeleton, size, variant } = properties; | ||
const rounded = isCircle ? "rounded-full" : ""; | ||
const intl = useIntl(); | ||
|
||
if (isLink) { | ||
return ( | ||
<LinkIconButton | ||
aria-label={intl.formatMessage({ defaultMessage: "Fusce dignissim", id: "uEMDBb" })} | ||
className={`${size} ${rounded}`} | ||
color={color} | ||
href="https://ui.spear.ai" | ||
isDisabled={isDisabled} | ||
isLoading={isLoading} | ||
isSkeleton={isSkeleton} | ||
rel="nofollow" | ||
target="_blank" | ||
variant={variant} | ||
> | ||
{isLoading ? <IconButtonSpinner /> : null} | ||
{!isLoading && hasIcon ? ( | ||
<IconButtonIcon asChild> | ||
<ArrowTopRightIcon className="rtl:-scale-x-100" /> | ||
</IconButtonIcon> | ||
) : null} | ||
</LinkIconButton> | ||
); | ||
} | ||
|
||
return ( | ||
<IconButton | ||
aria-label={intl.formatMessage({ defaultMessage: "Fusce dignissim", id: "uEMDBb" })} | ||
className={`${size} ${rounded}`} | ||
color={color} | ||
isDisabled={isDisabled} | ||
isLoading={isLoading} | ||
isSkeleton={isSkeleton} | ||
variant={variant} | ||
> | ||
{isLoading ? <IconButtonSpinner /> : null} | ||
{!isLoading && hasIcon ? ( | ||
<IconButtonIcon asChild> | ||
<PlusIcon /> | ||
</IconButtonIcon> | ||
) : null} | ||
</IconButton> | ||
); | ||
}; | ||
|
||
const meta = { | ||
argTypes: { | ||
variant: { control: { type: "select" }, options: ["solid"] }, | ||
}, | ||
component: PreviewIconButton, | ||
} satisfies Meta<typeof PreviewIconButton>; | ||
|
||
type Story = StoryObj<typeof meta>; | ||
|
||
export const Standard: Story = { | ||
args: { | ||
color: "neutral", | ||
hasIcon: true, | ||
isCircle: false, | ||
isDisabled: false, | ||
isLink: false, | ||
isLoading: false, | ||
isSkeleton: false, | ||
size: "size-8", | ||
variant: "soft", | ||
}, | ||
argTypes: { | ||
color: { | ||
control: { | ||
type: "select", | ||
}, | ||
options: ["neutral", "primary", "negative", "x-negative", "positive", "x-positive"], | ||
}, | ||
size: { | ||
control: { | ||
labels: { | ||
"size-7": "7", | ||
"size-8": "8", | ||
"size-9": "9", | ||
}, | ||
type: "select", | ||
}, | ||
options: ["size-7", "size-8", "size-9"], | ||
}, | ||
variant: { | ||
control: { | ||
type: "select", | ||
}, | ||
options: ["ghost", "outline", "soft", "solid"], | ||
}, | ||
}, | ||
parameters: { | ||
layout: "centered", | ||
}, | ||
}; | ||
|
||
export default meta; |
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
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,97 @@ | ||
import { Slot } from "@radix-ui/react-slot"; | ||
import { ComponentPropsWithoutRef, ElementRef, forwardRef, SVGAttributes } from "react"; | ||
import { Button as ButtonPrimitive, Link as LinkPrimitive } from "react-aria-components"; | ||
import { Spinner } from "@/components/spinner"; | ||
import { cx } from "@/helpers/cx"; | ||
|
||
export const IconButton = forwardRef< | ||
ElementRef<typeof ButtonPrimitive>, | ||
ComponentPropsWithoutRef<typeof ButtonPrimitive> & { | ||
className?: string | undefined; | ||
color?: "negative" | "neutral" | "positive" | "primary" | "x-negative" | "x-positive" | undefined; | ||
isLoading?: boolean | undefined; | ||
isPrimary?: boolean | undefined; | ||
isSkeleton?: boolean | undefined; | ||
variant?: "ghost" | "outline" | "soft" | "solid"; | ||
} | ||
>( | ||
( | ||
{ className, color = "neutral", isLoading = false, isSkeleton = false, variant = "soft", ...properties }, | ||
reference, | ||
) => { | ||
const mergedClassName = cx( | ||
"text-neutral-a-11 data-[variant='solid']:bg-neutral-9 data-[variant='solid']:data-[color='primary']:bg-primary-9 data-[variant='solid']:text-neutral-contrast data-[variant='solid']:data-[color='primary']:text-primary-contrast data-[color='primary']:text-primary-a-11 data-[variant='soft']:bg-neutral-a-3 data-[variant='soft']:data-[color='primary']:bg-primary-3 hover:bg-neutral-a-4 hover:data-[variant='solid']:bg-neutral-10 data-[color='primary']:hover:bg-primary-a-4 hover:data-[variant='solid']:data-[color='primary']:bg-primary-10 pressed:data-[color='primary']:bg-primary-a-5 pressed:bg-neutral-a-5 data-[variant='outline']:outline-neutral-a-7 data-[variant='outline']:focus-visible:outline-primary-a-7 data-[variant='outline']:data-[color='primary']:outline-primary-a-7 data-[color='negative']:hover:bg-negative-a-4 data-[color='negative']:text-negative-a-11 data-[variant='outline']:data-[color='negative']:outline-negative-a-7 data-[variant='soft']:data-[color='negative']:bg-negative-a-3 data-[variant='solid']:data-[color='negative']:text-negative-contrast data-[variant='solid']:data-[color='negative']:bg-negative-9 hover:data-[variant='solid']:data-[color='negative']:bg-negative-10 pressed:data-[color='negative']:bg-negative-a-5 data-[color='x-negative']:hover:bg-x-negative-a-4 data-[color='x-negative']:text-x-negative-a-11 data-[variant='outline']:data-[color='x-negative']:outline-x-negative-a-7 data-[variant='soft']:data-[color='x-negative']:bg-x-negative-a-3 data-[variant='solid']:data-[color='x-negative']:text-x-negative-contrast data-[variant='solid']:data-[color='x-negative']:bg-x-negative-9 hover:data-[variant='solid']:data-[color='x-negative']:bg-x-negative-10 pressed:data-[color='x-negative']:bg-x-negative-a-5 data-[color='positive']:hover:bg-positive-a-4 data-[color='positive']:text-positive-a-11 data-[variant='outline']:data-[color='positive']:outline-positive-a-7 data-[variant='soft']:data-[color='positive']:bg-positive-a-3 data-[variant='solid']:data-[color='positive']:text-positive-contrast data-[variant='solid']:data-[color='positive']:bg-positive-9 hover:data-[variant='solid']:data-[color='positive']:bg-positive-10 pressed:data-[color='positive']:bg-positive-a-5 data-[color='x-positive']:hover:bg-x-positive-a-4 data-[color='x-positive']:text-x-positive-a-11 data-[variant='outline']:data-[color='x-positive']:outline-x-positive-a-7 data-[variant='soft']:data-[color='x-positive']:bg-x-positive-a-3 data-[variant='solid']:data-[color='x-positive']:text-x-positive-contrast data-[variant='solid']:data-[color='x-positive']:bg-x-positive-9 hover:data-[variant='solid']:data-[color='x-positive']:bg-x-positive-10 pressed:data-[color='x-positive']:bg-x-positive-a-5 disabled:text-neutral-a-8 data-[variant='outline']:disabled:outline-neutral-a-6 data-[variant='solid']:disabled:bg-neutral-a-3 size-8 cursor-default rounded-md p-1.5 shadow-sm data-[is-skeleton=true]:pointer-events-none data-[is-skeleton=true]:animate-pulse data-[variant='outline']:outline data-[variant='outline']:outline-offset-0", | ||
className, | ||
); | ||
return ( | ||
<ButtonPrimitive | ||
className={mergedClassName} | ||
data-color={color} | ||
data-is-loading={isLoading} | ||
data-is-skeleton={isSkeleton} | ||
data-variant={variant} | ||
{...properties} | ||
ref={reference} | ||
/> | ||
); | ||
}, | ||
); | ||
|
||
IconButton.displayName = "IconButton"; | ||
|
||
export const LinkIconButton = forwardRef< | ||
ElementRef<typeof LinkPrimitive>, | ||
ComponentPropsWithoutRef<typeof LinkPrimitive> & { | ||
className?: string | undefined; | ||
color?: "negative" | "neutral" | "positive" | "primary" | "x-negative" | "x-positive" | undefined; | ||
isLoading?: boolean | undefined; | ||
isPrimary?: boolean | undefined; | ||
isSkeleton?: boolean | undefined; | ||
variant?: "ghost" | "outline" | "soft" | "solid"; | ||
} | ||
>( | ||
( | ||
{ className, color = "neutral", isLoading = false, isSkeleton = false, variant = "soft", ...properties }, | ||
reference, | ||
) => { | ||
const mergedClassName = cx( | ||
"text-neutral-a-11 data-[variant='solid']:bg-neutral-9 data-[variant='solid']:data-[color='primary']:bg-primary-9 data-[variant='solid']:text-neutral-contrast data-[variant='solid']:data-[color='primary']:text-primary-contrast data-[color='primary']:text-primary-a-11 data-[variant='soft']:bg-neutral-a-3 data-[variant='soft']:data-[color='primary']:bg-primary-3 hover:bg-neutral-a-4 hover:data-[variant='solid']:bg-neutral-10 data-[color='primary']:hover:bg-primary-a-4 hover:data-[variant='solid']:data-[color='primary']:bg-primary-10 pressed:data-[color='primary']:bg-primary-a-5 pressed:bg-neutral-a-5 data-[variant='outline']:outline-neutral-a-7 data-[variant='outline']:focus-visible:outline-primary-a-7 data-[variant='outline']:data-[color='primary']:outline-primary-a-7 data-[color='negative']:hover:bg-negative-a-4 data-[color='negative']:text-negative-a-11 data-[variant='outline']:data-[color='negative']:outline-negative-a-7 data-[variant='soft']:data-[color='negative']:bg-negative-a-3 data-[variant='solid']:data-[color='negative']:text-negative-contrast data-[variant='solid']:data-[color='negative']:bg-negative-9 hover:data-[variant='solid']:data-[color='negative']:bg-negative-10 pressed:data-[color='negative']:bg-negative-a-5 data-[color='x-negative']:hover:bg-x-negative-a-4 data-[color='x-negative']:text-x-negative-a-11 data-[variant='outline']:data-[color='x-negative']:outline-x-negative-a-7 data-[variant='soft']:data-[color='x-negative']:bg-x-negative-a-3 data-[variant='solid']:data-[color='x-negative']:text-x-negative-contrast data-[variant='solid']:data-[color='x-negative']:bg-x-negative-9 hover:data-[variant='solid']:data-[color='x-negative']:bg-x-negative-10 pressed:data-[color='x-negative']:bg-x-negative-a-5 data-[color='positive']:hover:bg-positive-a-4 data-[color='positive']:text-positive-a-11 data-[variant='outline']:data-[color='positive']:outline-positive-a-7 data-[variant='soft']:data-[color='positive']:bg-positive-a-3 data-[variant='solid']:data-[color='positive']:text-positive-contrast data-[variant='solid']:data-[color='positive']:bg-positive-9 hover:data-[variant='solid']:data-[color='positive']:bg-positive-10 pressed:data-[color='positive']:bg-positive-a-5 data-[color='x-positive']:hover:bg-x-positive-a-4 data-[color='x-positive']:text-x-positive-a-11 data-[variant='outline']:data-[color='x-positive']:outline-x-positive-a-7 data-[variant='soft']:data-[color='x-positive']:bg-x-positive-a-3 data-[variant='solid']:data-[color='x-positive']:text-x-positive-contrast data-[variant='solid']:data-[color='x-positive']:bg-x-positive-9 hover:data-[variant='solid']:data-[color='x-positive']:bg-x-positive-10 pressed:data-[color='x-positive']:bg-x-positive-a-5 disabled:text-neutral-a-8 data-[variant='outline']:disabled:outline-neutral-a-6 data-[variant='solid']:disabled:bg-neutral-a-3 size-8 cursor-default rounded-md p-1.5 shadow-sm data-[is-skeleton=true]:pointer-events-none data-[is-skeleton=true]:animate-pulse data-[variant='outline']:outline data-[variant='outline']:outline-offset-0", | ||
className, | ||
); | ||
return ( | ||
<LinkPrimitive | ||
className={mergedClassName} | ||
data-color={color} | ||
data-is-loading={isLoading} | ||
data-is-skeleton={isSkeleton} | ||
data-variant={variant} | ||
{...properties} | ||
ref={reference} | ||
/> | ||
); | ||
}, | ||
); | ||
|
||
LinkIconButton.displayName = "LinkIconButton"; | ||
|
||
export const IconButtonIcon = forwardRef< | ||
SVGSVGElement, | ||
SVGAttributes<SVGElement> & { asChild?: boolean | undefined } | ||
>(({ asChild = false, className, ...properties }, reference) => { | ||
const Component = asChild ? Slot : "svg"; | ||
const mergedClassName = cx("h-full w-full", className); | ||
// @ts-expect-error the Slot component’s type definition doesn’t play nice with SVGs | ||
return <Component aria-hidden className={mergedClassName} ref={reference} {...properties} />; | ||
}); | ||
|
||
IconButtonIcon.displayName = "IconButtonIcon"; | ||
|
||
export const IconButtonSpinner = forwardRef< | ||
ElementRef<typeof Spinner>, | ||
ComponentPropsWithoutRef<typeof Spinner> | ||
>(({ className, ...properties }, reference) => { | ||
const mergedClassName = cx("size-full", className); | ||
return <Spinner className={mergedClassName} {...properties} ref={reference} />; | ||
}); | ||
|
||
IconButtonSpinner.displayName = "IconButtonSpinner"; |
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
Oops, something went wrong.