Skip to content

feat(button): add loader minimal display interval #634

Merged
merged 3 commits into from
May 7, 2021
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 16 additions & 6 deletions packages/button/src/Component.stories.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -270,29 +270,39 @@ import { Button } from '@alfalab/core-components-button';

С помощью свойства `loading` можно отобразить состояние загрузки.

Минимальное время отображения лоадера - 500мс, чтобы при быстрых ответах от сервера кнопка не «моргала».

<Preview>
{React.createElement(() => {
const [loading, setLoading] = React.useState({
primary: false,
secondary: false
});
const handleClick = (buttonName) => {
const handleClick = (buttonName, timeout) => {
setLoading({...loading, [buttonName]: true});
setTimeout(() => {
setLoading({...loading, [buttonName]: false});
}, 500)
}, timeout)
}
return (
<Container>
<Row align="middle">
<Col>
<Button view="primary" loading={loading.primary} onClick={() => handleClick('primary')}>
Отправить запрос
<Button
view="primary"
loading={loading.primary}
onClick={() => handleClick('primary', 30)}
>
Отправить быстрый запрос (30мс)
</Button>
</Col>
<Col>
<Button view="secondary" loading={loading.secondary} onClick={() => handleClick('secondary')}>
Отправить запрос
<Button
view="secondary"
loading={loading.secondary}
onClick={() => handleClick('secondary', 1500)}
>
Отправить долгий запрос (1500мс)
</Button>
</Col>
</Row>
Expand Down
45 changes: 41 additions & 4 deletions packages/button/src/Component.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,10 @@
import React, { AnchorHTMLAttributes, ButtonHTMLAttributes, useRef } from 'react';
import React, {
AnchorHTMLAttributes,
ButtonHTMLAttributes,
useEffect,
useRef,
useState,
} from 'react';
import cn from 'classnames';
import mergeRefs from 'react-merge-refs';

Expand Down Expand Up @@ -61,8 +67,15 @@ export type ComponentProps = {

type AnchorButtonProps = ComponentProps & AnchorHTMLAttributes<HTMLAnchorElement>;
type NativeButtonProps = ComponentProps & ButtonHTMLAttributes<HTMLButtonElement>;

export type ButtonProps = Partial<AnchorButtonProps | NativeButtonProps>;

/**
* Минимальное время отображения лоадера - 500мс,
* чтобы при быстрых ответах от сервера кнопка не «моргала».
*/
const LOADER_MIN_DISPLAY_INTERVAL = 500;

export const Button = React.forwardRef<HTMLAnchorElement | HTMLButtonElement, ButtonProps>(
(
{
Expand All @@ -85,6 +98,12 @@ export const Button = React.forwardRef<HTMLAnchorElement | HTMLButtonElement, Bu

const [focused] = useFocus(buttonRef, 'keyboard');

const [loaderTimePassed, setLoaderTimePassed] = useState(true);

const timerId = useRef(0);

const showLoader = loading || !loaderTimePassed;

const componentProps = {
className: cn(
styles.component,
Expand All @@ -95,7 +114,7 @@ export const Button = React.forwardRef<HTMLAnchorElement | HTMLButtonElement, Bu
[styles.block]: block,
[styles.iconOnly]: !children,
[styles.nowrap]: nowrap,
[styles.loading]: loading,
[styles.loading]: showLoader,
},
className,
),
Expand All @@ -114,11 +133,29 @@ export const Button = React.forwardRef<HTMLAnchorElement | HTMLButtonElement, Bu
{children}
</span>
)}
{loading && <Loader className={styles.loader} />}

{showLoader && <Loader className={styles.loader} />}

{rightAddons && <span className={styles.addons}>{rightAddons}</span>}
</React.Fragment>
);

useEffect(() => {
if (loading) {
setLoaderTimePassed(false);

timerId.current = window.setTimeout(() => {
setLoaderTimePassed(true);
}, LOADER_MIN_DISPLAY_INTERVAL);
}
}, [loading]);

useEffect(() => {
return () => {
window.clearTimeout(timerId.current);
};
}, []);

if (href) {
const { target } = restProps as AnchorHTMLAttributes<HTMLAnchorElement>;

Expand Down Expand Up @@ -146,7 +183,7 @@ export const Button = React.forwardRef<HTMLAnchorElement | HTMLButtonElement, Bu
{...restButtonProps}
// eslint-disable-next-line react/button-has-type
type={type}
disabled={disabled || loading}
disabled={disabled || showLoader}
ref={mergeRefs([buttonRef, ref])}
>
{buttonChildren}
Expand Down