mastodon/app/javascript/mastodon/components/button/index.tsx
diondiondion bc952ebde9
Some checks failed
Check i18n / check-i18n (push) Waiting to run
CodeQL / Analyze (javascript) (push) Waiting to run
CodeQL / Analyze (ruby) (push) Waiting to run
Check formatting / lint (push) Waiting to run
Ruby Linting / lint (push) Waiting to run
Historical data migration test / test (14-alpine) (push) Waiting to run
Historical data migration test / test (15-alpine) (push) Waiting to run
Historical data migration test / test (16-alpine) (push) Waiting to run
Historical data migration test / test (17-alpine) (push) Waiting to run
Ruby Testing / build (production) (push) Waiting to run
Ruby Testing / build (test) (push) Waiting to run
Ruby Testing / test (.ruby-version) (push) Blocked by required conditions
Ruby Testing / test (3.2) (push) Blocked by required conditions
Ruby Testing / test (3.3) (push) Blocked by required conditions
Ruby Testing / ImageMagick tests (.ruby-version) (push) Blocked by required conditions
Ruby Testing / ImageMagick tests (3.2) (push) Blocked by required conditions
Ruby Testing / ImageMagick tests (3.3) (push) Blocked by required conditions
Ruby Testing / End to End testing (.ruby-version) (push) Blocked by required conditions
Ruby Testing / End to End testing (3.2) (push) Blocked by required conditions
Ruby Testing / End to End testing (3.3) (push) Blocked by required conditions
Ruby Testing / Elastic Search integration testing (.ruby-version, docker.elastic.co/elasticsearch/elasticsearch:7.17.13) (push) Blocked by required conditions
Ruby Testing / Elastic Search integration testing (.ruby-version, docker.elastic.co/elasticsearch/elasticsearch:8.10.2) (push) Blocked by required conditions
Ruby Testing / Elastic Search integration testing (.ruby-version, opensearchproject/opensearch:2) (push) Blocked by required conditions
Ruby Testing / Elastic Search integration testing (3.2, docker.elastic.co/elasticsearch/elasticsearch:7.17.13) (push) Blocked by required conditions
Ruby Testing / Elastic Search integration testing (3.3, docker.elastic.co/elasticsearch/elasticsearch:7.17.13) (push) Blocked by required conditions
Chromatic / Run Chromatic (push) Has been cancelled
CSS Linting / lint (push) Has been cancelled
JavaScript Linting / lint (push) Has been cancelled
JavaScript Testing / test (push) Has been cancelled
Add new plain (text-only) button variant (#36002)
2025-09-03 12:34:29 +00:00

95 lines
2.1 KiB
TypeScript

import type { PropsWithChildren, JSX } from 'react';
import { useCallback } from 'react';
import classNames from 'classnames';
import { LoadingIndicator } from 'mastodon/components/loading_indicator';
interface BaseProps
extends Omit<React.ButtonHTMLAttributes<HTMLButtonElement>, 'children'> {
block?: boolean;
secondary?: boolean;
plain?: boolean;
compact?: boolean;
dangerous?: boolean;
loading?: boolean;
}
interface PropsChildren extends PropsWithChildren<BaseProps> {
text?: undefined;
}
interface PropsWithText extends BaseProps {
text: JSX.Element | string;
children?: undefined;
}
type Props = PropsWithText | PropsChildren;
/**
* Primary UI component for user interaction that doesn't result in navigation.
*/
export const Button: React.FC<Props> = ({
type = 'button',
onClick,
disabled,
block,
secondary,
plain,
compact,
dangerous,
loading,
className,
title,
text,
children,
...props
}) => {
const handleClick = useCallback<React.MouseEventHandler<HTMLButtonElement>>(
(e) => {
if (disabled || loading) {
e.stopPropagation();
e.preventDefault();
} else if (onClick) {
onClick(e);
}
},
[disabled, loading, onClick],
);
const label = text ?? children;
return (
<button
className={classNames('button', className, {
'button-secondary': secondary,
'button--plain': plain,
'button--compact': compact,
'button--block': block,
'button--dangerous': dangerous,
loading,
})}
// Disabled buttons can't have focus, so we don't really
// disable the button during loading
disabled={disabled && !loading}
aria-disabled={loading}
// If the loading prop is used, announce label changes
aria-live={loading !== undefined ? 'polite' : undefined}
onClick={handleClick}
title={title}
type={type}
{...props}
>
{loading ? (
<>
<span className='button__label-wrapper'>{label}</span>
<LoadingIndicator role='none' />
</>
) : (
label
)}
</button>
);
};