Skip to content

Commit

Permalink
[sparkle] Add split button (#8602)
Browse files Browse the repository at this point in the history
* [sparkle] Add split button

* bump

* review comments
  • Loading branch information
tdraier authored Nov 13, 2024
1 parent 3f7c39e commit 71e2303
Show file tree
Hide file tree
Showing 4 changed files with 148 additions and 1 deletion.
2 changes: 1 addition & 1 deletion sparkle/src/components/Button.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ export type ButtonVariantType = (typeof BUTTON_VARIANTS)[number];

export const BUTTON_SIZES = ["xs", "sm", "md"] as const;

type ButtonSizeType = (typeof BUTTON_SIZES)[number];
export type ButtonSizeType = (typeof BUTTON_SIZES)[number];

const styleVariants: Record<ButtonVariantType, string> = {
primary:
Expand Down
90 changes: 90 additions & 0 deletions sparkle/src/components/SplitButton.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
import { cva } from "class-variance-authority";
import React, { useState } from "react";

import {
Button,
ButtonSizeType,
MetaButtonProps,
} from "@sparkle/components/Button";
import {
DropdownMenu,
DropdownMenuContent,
DropdownMenuItem,
DropdownMenuTrigger,
} from "@sparkle/components/Dropdown";
import { Separator } from "@sparkle/components/Separator";
import { ChevronDownIcon } from "@sparkle/icons";
import { cn } from "@sparkle/lib";

const separatorSizeVariants: Record<ButtonSizeType, string> = {
xs: "s-h-3",
sm: "s-h-5",
md: "s-h-7",
};

const separatorVariants = cva("s--ml-[1px]", {
variants: {
size: separatorSizeVariants,
},
});

interface SplitButtonActionProps {
label: string;
icon?: React.ComponentType;
tooltip?: string;
disabled?: boolean;
onClick?: (event: React.MouseEvent<HTMLButtonElement, MouseEvent>) => void;
}

export interface SplitButtonProps extends Omit<MetaButtonProps, "children"> {
actions: SplitButtonActionProps[];
defaultAction: SplitButtonActionProps;
}

export const SplitButton = React.forwardRef<
HTMLButtonElement,
SplitButtonProps
>(({ actions, className, size, variant, disabled, ...props }, ref) => {
const [current, setCurrent] = useState(props.defaultAction);
return (
<div className="s-flex s-items-center">
<Button
{...props}
size={size}
variant={variant}
label={current.label}
icon={current.icon}
disabled={disabled || current.disabled}
onClick={(e) => current.onClick && current.onClick(e)}
ref={ref}
className={cn("s-rounded-r-none s-border-r-0", className)}
/>
<div className={separatorVariants({ size })}>
<Separator orientation="vertical" />
</div>
<DropdownMenu>
<DropdownMenuTrigger asChild>
<Button
{...props}
size={size}
variant={variant}
icon={ChevronDownIcon}
disabled={disabled}
className={cn("s-rounded-l-none s-border-l-0", className)}
/>
</DropdownMenuTrigger>
<DropdownMenuContent align="end">
{actions.map((action, index) => (
<DropdownMenuItem
key={index}
label={action.label}
icon={action.icon}
disabled={action.disabled}
onClick={() => setCurrent(action)}
/>
))}
</DropdownMenuContent>
</DropdownMenu>
</div>
);
});
1 change: 1 addition & 0 deletions sparkle/src/components/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,7 @@ export { Searchbar } from "./Searchbar";
export { Separator } from "./Separator";
export { SliderToggle } from "./SliderToggle";
export { default as Spinner } from "./Spinner";
export { SplitButton } from "./SplitButton";
export { Tabs, TabsContent, TabsList, TabsTrigger } from "./Tabs";
export { TextArea } from "./TextArea";
export {
Expand Down
56 changes: 56 additions & 0 deletions sparkle/src/stories/SplitButton.stories.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
import type { Meta, StoryObj } from "@storybook/react";
import React from "react";

import { BUTTON_SIZES, BUTTON_VARIANTS } from "@sparkle/components/Button";

import { PlusIcon, RobotIcon, SplitButton } from "../index_with_tw_base";

const meta: Meta<React.ComponentProps<typeof SplitButton>> = {
title: "Primitives/SplitButton",
component: SplitButton,
argTypes: {
variant: {
description: "The visual style variant of the button",
options: BUTTON_VARIANTS,
control: { type: "select" },
},
size: {
description: "The size of the button",
options: BUTTON_SIZES,
control: { type: "select" },
},
},
};

export default meta;
type Story = StoryObj<typeof meta>;

export const ExampleButton: Story = {
args: {
variant: "outline",
size: "md",
defaultAction: {
label: "First",
icon: PlusIcon,
tooltip: "First tooltip",
},
actions: [
{
label: "First",
icon: PlusIcon,
tooltip: "First tooltip",
},
{
label: "Second",
icon: RobotIcon,
tooltip: "Second tooltip",
disabled: true,
},
{
label: "Third",
icon: PlusIcon,
tooltip: "Third tooltip",
},
],
},
};

0 comments on commit 71e2303

Please sign in to comment.