Skip to content

Commit

Permalink
feat: Compact button groups (#59)
Browse files Browse the repository at this point in the history
  • Loading branch information
ksk5280 authored Apr 27, 2021
1 parent 5b69eb0 commit 47f7975
Show file tree
Hide file tree
Showing 2 changed files with 45 additions and 32 deletions.
38 changes: 15 additions & 23 deletions src/components/ButtonGroup.stories.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { Meta } from "@storybook/react";
import { Css } from "src/Css";
import { ButtonGroup } from "src/index";

export default {
Expand All @@ -8,39 +9,30 @@ export default {

export function ButtonGroups() {
return (
<div>
<div css={Css.df.flexColumn.childGap2.$}>
<div>
<h2>Icon Only</h2>
<ButtonGroup buttons={[
{icon: "chevronLeft"},
{icon: "plus"},
{icon: "chevronRight"},
]}/>
<ButtonGroup buttons={[{ icon: "chevronLeft" }, { icon: "plus" }, { icon: "chevronRight" }]} />
<ButtonGroup size="md" buttons={[{ icon: "chevronLeft" }, { icon: "plus" }, { icon: "chevronRight" }]} />
</div>
<div>
<h2>Basic</h2>
<ButtonGroup buttons={[
{text: "Leading"},
{text: "Middle"},
{text: "Trailing"},
]}/>
<ButtonGroup buttons={[{ text: "Leading" }, { text: "Middle" }, { text: "Trailing" }]} />
<ButtonGroup size="md" buttons={[{ text: "Leading" }, { text: "Middle" }, { text: "Trailing" }]} />
</div>
<div>
<h2>Active</h2>
<ButtonGroup buttons={[
{text: "Leading", active: true },
{text: "Middle"},
{text: "Trailing"},
]}/>
<ButtonGroup buttons={[{ text: "Leading", active: true }, { text: "Middle" }, { text: "Trailing" }]} />
<ButtonGroup
size="md"
buttons={[{ text: "Leading", active: true }, { text: "Middle" }, { text: "Trailing" }]}
/>
</div>
<div>
<h2>Disabled</h2>
<ButtonGroup disabled buttons={[
{text: "Leading"},
{text: "Middle"},
{text: "Trailing"},
]}/>
<ButtonGroup disabled buttons={[{ text: "Leading" }, { text: "Middle" }, { text: "Trailing" }]} />
<ButtonGroup size="md" disabled buttons={[{ text: "Leading" }, { text: "Middle" }, { text: "Trailing" }]} />
</div>
</div>
)
}
);
}
39 changes: 30 additions & 9 deletions src/components/ButtonGroup.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,34 +6,43 @@ import { BeamButtonProps, BeamFocusableProps } from "src/interfaces";

interface ButtonGroupProps {
disabled?: boolean;
buttons: ButtonGroupButtonProps[];
buttons: Pick<ButtonGroupButtonProps, "text" | "icon" | "active">[];
size?: ButtonGroupSize;
}

interface ButtonGroupButtonProps extends BeamButtonProps, BeamFocusableProps {
text?: string;
icon?: IconProps["icon"];
// Active is used to indicate the active/selected button, as in a tab or toggle.
active?: boolean;
size: ButtonGroupSize;
}

export function ButtonGroup(props: ButtonGroupProps) {
const { buttons, disabled = false } = props;
const { buttons, disabled = false, size = "sm" } = props;
return (
<div css={Css.mPx(4).$}>
{buttons.map((b, i) => (
<ButtonGroupButton key={i} {...{ ...b, disabled }} />
<ButtonGroupButton key={i} {...{ ...b, disabled, size }} />
))}
</div>
);
}

export function ButtonGroupButton({icon, text, active, onClick: onPress, disabled, ...otherProps}: ButtonGroupButtonProps) {
export function ButtonGroupButton({
icon,
text,
active,
onClick: onPress,
disabled,
size,
...otherProps
}: ButtonGroupButtonProps) {
const ariaProps = { onPress, isDisabled: disabled, ...otherProps };
const ref = useRef(null);
const { buttonProps, isPressed } = useButton(ariaProps, ref);
const { isFocusVisible, focusProps } = useFocusRing(ariaProps);
const { hoverProps, isHovered } = useHover(ariaProps);
const buttonStyles = getButtonStyles();

return (
<button
Expand All @@ -43,11 +52,12 @@ export function ButtonGroupButton({icon, text, active, onClick: onPress, disable
{...hoverProps}
css={{
...Css.buttonBase.$,
...buttonStyles,
...getButtonStyles(),
...sizeStyles[size],
...(isFocusVisible ? defaultFocusRingStyles : {}),
...(active ? activeStyles : {}),
...(isPressed ? pressedStyles : isHovered ? hoverStyles : {}),
...(icon ? iconStyles : {}),
...(icon ? iconStyles[size] : {}),
}}
>
{icon && <Icon icon={icon} />}
Expand All @@ -60,11 +70,10 @@ const pressedStyles = Css.bgGray200.$;
const activeStyles = Css.bgGray300.$;
const hoverStyles = Css.bgGray100.$;
const defaultFocusRingStyles = Css.relative.z2.bshFocus.$;
const iconStyles = Css.px1.$;

function getButtonStyles() {
return {
...Css.z1.hPx(40).px2.bgWhite.bGray300.bw1.ba.gray800.m0.br0.$,
...Css.z1.px2.bgWhite.bGray300.bw1.ba.gray900.m0.br0.$,
"&:disabled": Css.gray400.cursorNotAllowed.bGray300.$,
// Our first button should have a rounded left border
"&:first-of-type": Css.add("borderRadius", "4px 0 0 4px").$,
Expand All @@ -74,3 +83,15 @@ function getButtonStyles() {
"&:not(:first-of-type)": Css.mlPx(-1).$,
};
}

const sizeStyles: Record<ButtonGroupSize, {}> = {
sm: Css.hPx(32).$,
md: Css.hPx(40).$,
};

const iconStyles: Record<ButtonGroupSize, {}> = {
sm: Css.pxPx(4).$,
md: Css.px1.$,
};

type ButtonGroupSize = "sm" | "md";

0 comments on commit 47f7975

Please sign in to comment.