Skip to content

Commit

Permalink
feat: add button 'loading' state
Browse files Browse the repository at this point in the history
  • Loading branch information
arturbien committed Dec 11, 2023
1 parent b79cb7f commit cf1af6d
Show file tree
Hide file tree
Showing 4 changed files with 102 additions and 14 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,7 @@ export const Size: Story = {
},
render: (args) => (
<Flex align="center" gap="2">
<Button {...args} size="4" />
<Button {...args} size="3" />
<Button {...args} size="2" />
<Button {...args} size="1" />
Expand Down Expand Up @@ -154,3 +155,20 @@ export const WithIcons: Story = {
</Flex>
),
};

export const Loading: Story = {
args: {
children: 'Button',
size: buttonPropDefs.size.default,
color: buttonPropDefs.color.default,
loading: true,
},
render: (args) => (
<Flex align="center" gap="4">
<Button {...args} variant="classic" />
<Button {...args} variant="soft" />
<Button {...args} variant="surface" />
<Button {...args} variant="ghost" />
</Flex>
),
};
Original file line number Diff line number Diff line change
Expand Up @@ -123,3 +123,20 @@ export const HighContrast: Story = {
</Flex>
),
};

export const Loading: Story = {
args: {
children: <ExampleIcon size={16} />,
size: buttonPropDefs.size.default,
color: buttonPropDefs.color.default,
loading: true,
},
render: (args) => (
<Flex align="center" gap="4">
<IconButton {...args} variant="classic" />
<IconButton {...args} variant="soft" />
<IconButton {...args} variant="surface" />
<IconButton {...args} variant="ghost" />
</Flex>
),
};
51 changes: 40 additions & 11 deletions packages/frosted-ui/src/components/base-button.css
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
flex-shrink: 0;
user-select: none;
vertical-align: top;
color: var(--base-button-color);
}

/***************************************************************************************************
Expand All @@ -30,21 +31,25 @@
--base-button-classic-active-padding-top: 1px;
--base-button-height: var(--space-5);
border-radius: var(--radius-3);
--base-button-spinner-size: 12px;
}
&:where(.fui-r-size-2) {
--base-button-classic-active-padding-top: 2px;
--base-button-height: var(--space-6);
border-radius: var(--radius-4);
--base-button-spinner-size: 16px;
}
&:where(.fui-r-size-3) {
--base-button-classic-active-padding-top: 2px;
--base-button-height: var(--space-7);
border-radius: var(--radius-4);
--base-button-spinner-size: 18px;
}
&:where(.fui-r-size-4) {
--base-button-classic-active-padding-top: 2px;
--base-button-height: var(--space-8);
border-radius: var(--radius-5);
--base-button-spinner-size: 22px;
}
}
}
Expand Down Expand Up @@ -73,7 +78,7 @@

.fui-BaseButton:where(.fui-variant-classic) {
background-color: var(--accent-9);
color: var(--accent-9-contrast);
--base-button-color: var(--accent-9-contrast);
position: relative;
z-index: 0;

Expand All @@ -92,7 +97,7 @@

&:where(.fui-high-contrast) {
background-color: var(--accent-12);
color: var(--gray-1);
--base-button-color: var(--gray-1);
}
&:where(:focus-visible) {
outline: 2px solid var(--color-focus-root);
Expand Down Expand Up @@ -130,7 +135,7 @@
}
&:where([data-disabled]) {
cursor: var(--cursor-disabled);
color: var(--gray-a8);
--base-button-color: var(--gray-a8);
background-color: var(--gray-3);
box-shadow:
/* highlight*/
Expand All @@ -147,14 +152,14 @@
/* soft / ghost */

.fui-BaseButton:where(.fui-variant-soft, .fui-variant-ghost) {
color: var(--accent-a11);
--base-button-color: var(--accent-a11);

&:where(.fui-high-contrast) {
color: var(--accent-12);
--base-button-color: var(--accent-12);
}
&:where([data-disabled]) {
cursor: var(--cursor-disabled);
color: var(--gray-a8);
--base-button-color: var(--gray-a8);
background-color: var(--gray-a3);
}
}
Expand All @@ -179,7 +184,7 @@
}
&:where([data-disabled]) {
cursor: var(--cursor-disabled);
color: var(--gray-a8);
--base-button-color: var(--gray-a8);
background-color: var(--gray-a3);
}
}
Expand All @@ -202,7 +207,7 @@
}
&:where([data-disabled]) {
cursor: var(--cursor-disabled);
color: var(--gray-a8);
--base-button-color: var(--gray-a8);
background-color: transparent;
}
}
Expand All @@ -214,7 +219,7 @@
box-shadow:
inset 0 0 0 1px var(--accent-a5),
0px 2px 2px 0px var(--black-a1);
color: var(--accent-12);
--base-button-color: var(--accent-12);

@media (hover: hover) {
&:where(:hover) {
Expand All @@ -239,12 +244,36 @@
outline-offset: -1px;
}
&:where(.fui-high-contrast) {
color: var(--accent-12);
--base-button-color: var(--accent-12);
}
&:where([data-disabled]) {
cursor: var(--cursor-disabled);
color: var(--gray-a8);
--base-button-color: var(--gray-a8);
box-shadow: inset 0 0 0 1px var(--gray-a6);
background-color: var(--gray-a2);
}
}

/* Loading state */

@keyframes fui-button-spin {
from {
transform: rotate(0deg);
}
to {
transform: rotate(360deg);
}
}

.fui-BaseButton:where([aria-busy]) {
color: transparent;
pointer-events: none;
}

.fui-BaseButtonSpinner {
position: absolute;
width: var(--base-button-spinner-size);
height: var(--base-button-spinner-size);
color: var(--base-button-color);
animation: fui-button-spin 500ms linear infinite;
}
30 changes: 27 additions & 3 deletions packages/frosted-ui/src/components/base-button.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -21,11 +21,15 @@ interface BaseButtonProps
MarginProps,
BaseButtonOwnProps {
asChild?: boolean;
loading?: boolean;
}
const BaseButton = React.forwardRef<BaseButtonElement, BaseButtonProps>(
(props, forwardedRef) => {
const { rest: marginRest, ...marginProps } = extractMarginProps(props);
const {
children,
loading,
disabled,
className,
asChild = false,
size = baseButtonPropDefs.size.default,
Expand All @@ -37,8 +41,6 @@ const BaseButton = React.forwardRef<BaseButtonElement, BaseButtonProps>(
const Comp = asChild ? Slot : 'button';
return (
<Comp
// The `data-disabled` attribute enables correct styles when doing `<Button asChild disabled>`
data-disabled={baseButtonProps.disabled || undefined}
data-accent-color={color || (variant === 'surface' ? 'gray' : color)}
{...baseButtonProps}
ref={forwardedRef}
Expand All @@ -51,7 +53,29 @@ const BaseButton = React.forwardRef<BaseButtonElement, BaseButtonProps>(
{ 'fui-high-contrast': highContrast },
withMarginProps(marginProps),
)}
/>
aria-busy={loading || undefined}
// The `data-disabled` attribute enables correct styles when doing `<Button asChild disabled>`
data-disabled={disabled || undefined}
aria-disabled={disabled || loading || undefined}
disabled={disabled || loading || undefined}
>
<>
{children}

{loading && (
<svg
viewBox="0 0 20 20"
xmlns="http://www.w3.org/2000/svg"
className="fui-BaseButtonSpinner"
>
<path
fill="currentColor"
d="M7.229 1.173a9.25 9.25 0 1011.655 11.412 1.25 1.25 0 10-2.4-.698 6.75 6.75 0 11-8.506-8.329 1.25 1.25 0 10-.75-2.385z"
></path>
</svg>
)}
</>
</Comp>
);
},
);
Expand Down

0 comments on commit cf1af6d

Please sign in to comment.