Skip to content

Commit

Permalink
[ui] Support round icon buttons. Enable forced square aspect ratio fo…
Browse files Browse the repository at this point in the history
…r icons (#13501)

- Add a `square` prop to Icons to force square aspect ratio. This was
important here to ensure consistent padding on round icon buttons
regardless of icon width.
- Move some one-off paddings from Button to theme defaults.
- `roundSingleChar` and `roundIcon` are now theme button kinds with
specific paddings. They are set as the default kind when Button detects
either scenario from button props.

<img width="725" alt="Screenshot 2024-11-18 at 5 51 41 PM"
src="https://github.com/user-attachments/assets/0d9f37f6-f06b-4484-b131-102dc6cef297">
GitOrigin-RevId: 72fe0fb76d6ca76ad01ef09f9506ba8331eb17d4
  • Loading branch information
coreymartin authored and Lightspark Eng committed Nov 19, 2024
1 parent 57b972d commit fafff8e
Show file tree
Hide file tree
Showing 5 changed files with 117 additions and 68 deletions.
98 changes: 34 additions & 64 deletions packages/ui/src/components/Button.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ import {
isThemeOrColorKey,
type ThemeOrColorKey,
} from "../styles/themes.js";
import { TokenSize, type TokenSizeKey } from "../styles/tokens/typography.js";
import { type TokenSizeKey } from "../styles/tokens/typography.js";
import { applyTypography } from "../styles/typography.js";
import { type NewRoutesType } from "../types/index.js";
import { select } from "../utils/emotion.js";
Expand All @@ -45,6 +45,8 @@ export const buttonKinds = [
"warning",
"tertiary",
"quaternary",
"roundSingleChar",
"roundIcon",
] as const;
export type ButtonKind = (typeof buttonKinds)[number];

Expand Down Expand Up @@ -82,52 +84,6 @@ export type ButtonProps = {
borderRadius?: ButtonBorderRadius | undefined;
};

type PaddingProps = {
paddingY: number;
size: TokenSizeKey;
iconSide?: IconSide | undefined;
isRound: boolean;
kind: ButtonKind;
};

const roundPaddingsX = {
[TokenSize.ExtraSmall]: 10,
[TokenSize.Small]: 10,
[TokenSize.Schmedium]: 10,
[TokenSize.Medium]: 14,
[TokenSize.Mlarge]: 19,
[TokenSize.Large]: 19,
} as const;

const paddingsX = {
[TokenSize.ExtraSmall]: 16,
[TokenSize.Small]: 24,
[TokenSize.Schmedium]: 24,
[TokenSize.Medium]: 24,
[TokenSize.Mlarge]: 24,
[TokenSize.Large]: 24,
} as const;

function getPaddingX(size: TokenSizeKey, kind: ButtonKind, isRound: boolean) {
return kind === "ghost"
? 0
: isRound
? roundPaddingsX[size]
: paddingsX[size];
}

function getPadding({ paddingY, kind, size, iconSide, isRound }: PaddingProps) {
const paddingX = getPaddingX(size, kind, isRound);
const paddingForIcon = 0;
return kind === "ghost"
? 0
: `${paddingY}px ${
paddingX + (iconSide === "right" ? paddingForIcon : 0)
}px ${paddingY}px ${
paddingX + (iconSide === "left" ? paddingForIcon : 0)
}px`;
}

function resolveBackgroundColorKey(
theme: Theme,
kind: ButtonKind,
Expand Down Expand Up @@ -169,8 +125,18 @@ function resolveProp<T, K extends ButtonsThemeKey>(
}

function resolveProps(props: ButtonProps, theme: Theme) {
const isSingleCharRoundButton = Boolean(
props.text && props.text.length === 1 && !props.icon,
);
const isRoundIconButton = Boolean(!props.text && props.icon);
const defaultKind = isSingleCharRoundButton
? "roundSingleChar"
: isRoundIconButton
? "roundIcon"
: "secondary";

const {
kind = "secondary",
kind = defaultKind,
size: sizeProp,
paddingY: paddingYType = "regular",
borderRadius,
Expand All @@ -181,6 +147,8 @@ function resolveProps(props: ButtonProps, theme: Theme) {
const size = resolveProp(sizeProp, kind, "defaultSize", theme);
const defaultPaddingsY = resolveProp(null, kind, "defaultPaddingsY", theme);
const defaultPaddingY = defaultPaddingsY[size];
const defaultPaddingsX = resolveProp(null, kind, "defaultPaddingsX", theme);
const defaultPaddingX = defaultPaddingsX[size];

const backgroundColor = resolveBackgroundColorKey(
theme,
Expand Down Expand Up @@ -232,7 +200,11 @@ function resolveProps(props: ButtonProps, theme: Theme) {
typeof defaultPaddingY === "number"
? defaultPaddingY
: defaultPaddingY[paddingYType],
borderRadius: resolveProp(borderRadius, kind, "defaultBorderRadius", theme),
paddingX: defaultPaddingX,
borderRadius:
kind === "roundSingleChar" || kind === "roundIcon"
? 999
: resolveProp(borderRadius, kind, "defaultBorderRadius", theme),
backgroundColor,
borderColor,
hoverBackgroundColor,
Expand All @@ -248,6 +220,7 @@ export function Button(props: ButtonProps) {
kind,
typography,
size,
paddingX,
paddingY,
text,
to,
Expand Down Expand Up @@ -289,6 +262,7 @@ export function Button(props: ButtonProps) {
text={text}
typography={typography}
kind={kind}
paddingX={paddingX}
>
<Loading size={defaultIconWidth} center={false} kind={loadingKind} />
</ButtonIcon>
Expand All @@ -300,10 +274,12 @@ export function Button(props: ButtonProps) {
text={text}
typography={typography}
kind={kind}
paddingX={paddingX}
>
<Icon
{...icon}
width={icon.width || defaultIconWidth}
square
color={typography.color}
/>
</ButtonIcon>
Expand Down Expand Up @@ -344,8 +320,6 @@ export function Button(props: ButtonProps) {
</Fragment>
);

const isSingleCharRoundButton = Boolean(text && text.length === 1 && !icon);

const commonProps = {
id,
kind,
Expand All @@ -354,14 +328,14 @@ export function Button(props: ButtonProps) {
onClick,
fullWidth,
iconSide,
paddingX,
paddingY,
backgroundColor,
borderColor,
hoverBackgroundColor,
hoverBorderColor,
activeBackgroundColor,
activeBorderColor,
isRound: isSingleCharRoundButton,
isLoading: loading,
disabled: disabled || loading,
css: {
Expand Down Expand Up @@ -400,10 +374,10 @@ export function Button(props: ButtonProps) {
}

type StyledButtonProps = {
paddingX: number;
paddingY: number;
kind: ButtonKind;
isLoading: boolean;
isRound: boolean;
typography: {
type: AllowedButtonTypographyTypes;
size: TokenSizeKey;
Expand All @@ -424,6 +398,7 @@ type StyledButtonProps = {
};

const buttonStyle = ({
paddingX,
paddingY,
kind,
theme,
Expand All @@ -434,7 +409,6 @@ const buttonStyle = ({
zIndex,
iconSide,
borderColor: borderColorProp,
isRound,
borderRadius,
backgroundColor,
hoverBackgroundColor,
Expand Down Expand Up @@ -475,14 +449,10 @@ const buttonStyle = ({
box-sizing: content-box;
border: 1px solid;
border-color: ${borderColor};
border-radius: ${isRound ? "100%" : `${borderRadius}px`};
padding: ${getPadding({
paddingY,
kind,
size: typography.size,
iconSide,
isRound,
})};
border-radius: ${typeof borderRadius === "number"
? `${borderRadius}px`
: borderRadius};
padding: ${paddingY}px ${paddingX}px;
transition:
background-color 0.2s ease-out,
border-color 0.2s ease-out;
Expand All @@ -507,14 +477,14 @@ interface ButtonIconProps {
iconSide?: IconSide | undefined;
text?: string | undefined;
kind: ButtonKind;
paddingX: number;
}

const ButtonIcon = styled.div<ButtonIconProps>`
display: flex;
align-items: center;
justify-content: center;
${({ iconSide, kind, typography }) =>
`${iconSide}: ${getPaddingX(typography.size, kind, false)}px;`}
${({ iconSide, paddingX }) => `${iconSide}: ${paddingX}px;`}
`;

export const StyledButton = styled(UnstyledButton)<StyledButtonProps>`
Expand Down
23 changes: 22 additions & 1 deletion packages/ui/src/components/Icon/Icon.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ type IconProps<I extends IconName> = {
color?: FontColorKey | undefined;
tutorialStep?: number;
id?: string;
square?: boolean | undefined;
/* Require iconProps if icon takes a props object and at least one of its props is required: */
} & (RequiredKeys<ComponentPropsWithoutRef<(typeof iconMap)[I]>> extends never
? { iconProps?: ComponentPropsWithoutRef<(typeof iconMap)[I]> }
Expand All @@ -31,6 +32,7 @@ export function Icon<I extends IconName>({
className,
name,
width,
square = false,
tutorialStep,
id,
mr: mrProp = 0,
Expand All @@ -46,6 +48,7 @@ export function Icon<I extends IconName>({
/** Assume width is px relative to the root font size but specify
* in ems to preserve scale for larger font sizes */
const w = parseFloat((width / rootFontSizePx).toFixed(2));
const h = square ? w : "auto";
const mr =
typeof mrProp === "number"
? `${parseFloat((mrProp / rootFontSizePx).toFixed(2))}em`
Expand Down Expand Up @@ -74,6 +77,8 @@ export function Icon<I extends IconName>({
id={id}
className={className}
w={w}
h={h}
square={square}
mr={mr}
ml={ml}
mt={mt}
Expand All @@ -89,19 +94,24 @@ export function Icon<I extends IconName>({

type IconContainerProps = {
w: number;
h: number | "auto";
mr: string;
ml: string;
mt: string;
mb: string;
verticalAlign: string | number;
fontColor?: FontColorKey | undefined;
square: boolean;
};

export const IconContainer = styled.span<IconContainerProps>`
pointer-events: none;
display: inline-flex;
${({ mr, ml, mt, mb, w }) => `
align-items: center;
justify-content: center;
${({ mr, ml, mt, mb, w, h }) => `
width: ${w}em;
height: ${typeof h === "number" ? `${h}em` : h};
/* ensure no shrink in flex containers: */
min-width: ${w}em;
${mr ? `margin-right: ${mr};` : ""}
Expand All @@ -113,6 +123,17 @@ export const IconContainer = styled.span<IconContainerProps>`
vertical-align: ${({ verticalAlign }) =>
isString(verticalAlign) ? verticalAlign : `${verticalAlign}em`};
${({ square }) => {
return square
? `
svg {
width: 100%;
height: 100%;
}
`
: "";
}}
${({ theme, fontColor }) => {
const color = getFontColor(theme, fontColor, "inherit");
return `
Expand Down
4 changes: 2 additions & 2 deletions packages/ui/src/icons/BrokenChainLink.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,8 @@ export function BrokenChainLink({ strokeWidth = "1" }: PathProps) {
fill="none"
>
<path
fill-rule="evenodd"
clip-rule="evenodd"
fillRule="evenodd"
clipRule="evenodd"
d="M9.59613 1.71666C10.4891 1.49342 11.394 2.03636 11.6172 2.92934L12.4506 6.26267C12.6738 7.15567 12.1309 8.06056 11.2379 8.28381C10.3449 8.50706 9.44003 7.96413 9.21678 7.07113L8.38345 3.73779C8.1602 2.84481 8.70313 1.93991 9.59613 1.71666ZM30.8912 9.10938C27.6345 5.85274 22.3545 5.85274 19.0979 9.10938L19.0902 9.11712L17.8377 10.3531C17.1827 10.9997 16.1274 10.9928 15.4808 10.3376C14.8342 9.68249 14.8412 8.62724 15.4963 7.98067L16.745 6.74826C21.3037 2.19397 28.6912 2.19534 33.2482 6.75234C37.8054 11.3094 37.8065 18.697 33.2522 23.2558L33.2482 23.2597L32.0199 24.5043C31.3734 25.1595 30.318 25.1663 29.663 24.5198C29.0079 23.8732 29.0009 22.818 29.6474 22.1628L30.8912 20.9027C34.1479 17.646 34.1479 12.366 30.8912 9.10938ZM1.71678 9.59601C1.94003 8.70302 2.84492 8.16009 3.73792 8.38332L7.07125 9.21666C7.96423 9.43991 8.50717 10.3448 8.28392 11.2378C8.06068 12.1308 7.15578 12.6737 6.2628 12.4505L2.92947 11.6171C2.03647 11.3939 1.49353 10.489 1.71678 9.59601ZM10.3377 15.4807C10.9929 16.1272 10.9998 17.1825 10.3532 17.8377L9.10952 19.0978C5.85288 22.3545 5.85287 27.6345 9.10948 30.8912C12.3661 34.1477 17.6462 34.1477 20.9027 30.8912L20.9105 30.8833L22.163 29.6473C22.818 29.0007 23.8734 29.0077 24.5199 29.6628C25.1665 30.318 25.1595 31.3732 24.5044 32.0198L23.2599 33.2482L23.2557 33.2522C18.697 37.8065 11.3095 37.8052 6.75247 33.2482C2.19543 28.691 2.1941 21.3035 6.74847 16.7448L7.98078 15.4962C8.62737 14.841 9.68262 14.8341 10.3377 15.4807ZM31.7169 28.7627C31.94 27.8697 32.8449 27.3267 33.7379 27.55L37.0712 28.3833C37.9642 28.6065 38.5072 29.5115 38.2839 30.4045C38.0607 31.2975 37.1559 31.8403 36.2629 31.6172L32.9295 30.7838C32.0365 30.5605 31.4935 29.6557 31.7169 28.7627ZM28.7629 31.7167C29.6559 31.4933 30.5607 32.0363 30.7839 32.9293L31.6172 36.2627C31.8405 37.1557 31.2975 38.0605 30.4045 38.2838C29.5115 38.507 28.6067 37.9642 28.3835 37.0712L27.5502 33.7378C27.3269 32.8448 27.8699 31.9398 28.7629 31.7167Z"
fill="currentColor"
stroke="currentColor"
Expand Down
2 changes: 2 additions & 0 deletions packages/ui/src/icons/Dollar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,11 @@ export function Dollar({
return (
<svg
width="100%"
height="auto"
viewBox="0 0 10 13"
fill="none"
xmlns="http://www.w3.org/2000/svg"
preserveAspectRatio="xMidYMid meet"
>
<path
d="M7.90922 2.61607H5.00156M5.00156 2.61607H3.57422C2.26039 2.61607 1.19531 3.48552 1.19531 4.55804C1.19531 5.63055 2.26039 6.5 3.57422 6.5H5.00156M5.00156 2.61607V1.0625M5.00156 2.61607V6.5M5.00156 6.5H6.42891C7.74274 6.5 8.80781 7.36945 8.80781 8.44196C8.80781 9.51448 7.74274 10.3839 6.42891 10.3839H5.00156M5.00156 6.5V10.3839M5.00156 10.3839H1.61833M5.00156 10.3839V11.9375"
Expand Down
Loading

0 comments on commit fafff8e

Please sign in to comment.