Skip to content

Commit

Permalink
Merge pull request #71 from kodai3/feat/button-loading-status
Browse files Browse the repository at this point in the history
feat(Button): 🎸 Add loading status
  • Loading branch information
takanorip authored May 3, 2024
2 parents e6ab470 + f6a380d commit 607eb3c
Show file tree
Hide file tree
Showing 6 changed files with 112 additions and 3 deletions.
5 changes: 5 additions & 0 deletions src/components/Button/Button.module.css
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,11 @@
border-color: var(--border-color-disabled);
}

.loading {
pointer-events: none;
cursor: wait;
}

/* Variant */

.primary {
Expand Down
29 changes: 26 additions & 3 deletions src/components/Button/Button.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
import { clsx } from 'clsx';
import { forwardRef } from 'react';
import styles from './Button.module.css';
import { CircularProgress } from './CircularProgress';
import { VariantIcon } from './VariantIcon';
import type { ButtonProps } from './ButtonTypes';

Expand All @@ -17,22 +18,44 @@ export const Button = forwardRef<HTMLButtonElement, ButtonProps>(
fixedIcon: _fixedIcon,
suffixIcon: _suffixIcon,
type = 'button',
disabled = false,
loading = false,
onClick,
...props
},
ref,
) => {
const icon = _icon === 'default' ? <VariantIcon variant={variant} /> : _icon;
const icon = loading ? <CircularProgress /> : _icon === 'default' ? <VariantIcon variant={variant} /> : _icon;
const fixedIcon = _fixedIcon === 'default' ? <VariantIcon variant={variant} /> : _fixedIcon;
const suffixIcon = _suffixIcon === 'default' ? <VariantIcon variant={variant} /> : _suffixIcon;
const cls = clsx({
[styles.button]: true,
[styles[variant]]: true,
[styles[size]]: true,
[styles.block]: block,
[styles.disabled]: !!props.disabled,
[styles.disabled]: disabled,
[styles.loading]: loading,
});

const handleClick = (e: React.MouseEvent<HTMLButtonElement>) => {
if (loading) {
e.preventDefault();
return;
}

onClick?.(e);
};

return (
<button type={type} className={cls} ref={ref} {...props}>
<button
type={type}
className={cls}
ref={ref}
disabled={disabled}
aria-disabled={loading}
onClick={handleClick}
{...props}
>
{fixedIcon && <span className={styles.fixedIcon}>{fixedIcon}</span>}
<span className={styles.label}>
{icon && <span className={styles.icon}>{icon}</span>}
Expand Down
4 changes: 4 additions & 0 deletions src/components/Button/ButtonTypes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,10 @@ export type OnlyButtonProps = {
* @default false
*/
disabled?: boolean;
/**
* ローディング状態を示す
*/
loading?: boolean;
};

export type OnlyLinkButtonProps = {
Expand Down
38 changes: 38 additions & 0 deletions src/components/Button/CircularProgress.module.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
.root {
display: flex;
animation: spin 1.4s linear infinite;
transform-origin: center;
}

.circle {
stroke: currentcolor;
animation: circular-progress 1.4s ease-in-out infinite;
transform-origin: center;
}

@keyframes spin {
0% {
transform: rotate(0deg);
}

100% {
transform: rotate(360deg);
}
}

@keyframes circular-progress {
0% {
stroke-dasharray: 1px, 200px;
stroke-dashoffset: 0;
}

50% {
stroke-dasharray: 100px, 200px;
stroke-dashoffset: -16px;
}

100% {
stroke-dasharray: 100px, 200px;
stroke-dashoffset: -124px;
}
}
26 changes: 26 additions & 0 deletions src/components/Button/CircularProgress.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import { FC } from 'react';
import styles from './CircularProgress.module.css';

const CIRCLE_SIZE = 44;
const THICKNESS = 3.6;

export const CircularProgress: FC = ({ ...props }) => {
return (
<span role="progressbar" className={styles.root} {...props}>
<svg
role="img"
aria-label="読み込み中"
viewBox={`${CIRCLE_SIZE / 2} ${CIRCLE_SIZE / 2} ${CIRCLE_SIZE} ${CIRCLE_SIZE}`}
>
<circle
className={styles.circle}
cx={CIRCLE_SIZE}
cy={CIRCLE_SIZE}
r={(CIRCLE_SIZE - THICKNESS) / 2}
fill="none"
strokeWidth={THICKNESS}
/>
</svg>
</span>
);
};
13 changes: 13 additions & 0 deletions src/stories/Button.stories.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -130,3 +130,16 @@ export const Disabled: Story = {
),
args: defaultArgs,
};

export const Loading: Story = {
render: () => (
<div style={{ display: 'flex', flexDirection: 'row', alignItems: 'flex-start', gap: '32px' }}>
<Button {...defaultArgs} loading />
<Button {...defaultArgs} variant="secondary" loading />
<Button {...defaultArgs} variant="accent" loading />
<Button {...defaultArgs} variant="alert" loading />
<Button {...defaultArgs} variant="text" loading />
<Button {...defaultArgs} variant="textAlert" loading />
</div>
),
};

0 comments on commit 607eb3c

Please sign in to comment.