mirror of
https://github.com/hanzoai/sentinel.git
synced 2026-09-29 22:34:33 +00:00
140 lines
3.9 KiB
TypeScript
140 lines
3.9 KiB
TypeScript
import {Fragment} from 'react';
|
|
import isPropValid from '@emotion/is-prop-valid';
|
|
import styled from '@emotion/styled';
|
|
|
|
import Radio from 'sentry/components/radio';
|
|
import {Tooltip} from 'sentry/components/tooltip';
|
|
import {space} from 'sentry/styles/space';
|
|
|
|
interface ContainerProps extends React.HTMLAttributes<HTMLDivElement> {
|
|
orientInline?: boolean;
|
|
}
|
|
|
|
interface BaseRadioGroupProps<C extends string> {
|
|
/**
|
|
* The choices availiable in the group
|
|
*/
|
|
choices: RadioOption<C>[];
|
|
/**
|
|
* Labels the radio group.
|
|
*/
|
|
label: string;
|
|
onChange: (id: C, e: React.FormEvent<HTMLInputElement>) => void;
|
|
value: string | number | null;
|
|
disabled?: boolean;
|
|
/**
|
|
* An array of [choice id, disabled reason]
|
|
*/
|
|
disabledChoices?: [C, React.ReactNode?][];
|
|
/**
|
|
* Switch the radio items to flow left to right, instead of vertically.
|
|
*/
|
|
orientInline?: boolean;
|
|
}
|
|
|
|
/**
|
|
* A single option in a radio group
|
|
*/
|
|
export type RadioOption<C extends string = string> = [
|
|
id: C,
|
|
label: React.ReactNode,
|
|
description?: React.ReactNode,
|
|
];
|
|
|
|
export interface RadioGroupProps<C extends string = string>
|
|
extends BaseRadioGroupProps<C>,
|
|
Omit<ContainerProps, 'onChange'> {}
|
|
|
|
function RadioGroup<C extends string>({
|
|
value,
|
|
disabled: groupDisabled,
|
|
disabledChoices = [],
|
|
choices = [],
|
|
label,
|
|
onChange,
|
|
orientInline,
|
|
...props
|
|
}: RadioGroupProps<C>) {
|
|
return (
|
|
<Container
|
|
orientInline={orientInline}
|
|
{...props}
|
|
role="radiogroup"
|
|
aria-label={label}
|
|
>
|
|
{choices.map(([id, name, description], index) => {
|
|
const disabledChoice = disabledChoices.find(([choiceId]) => choiceId === id);
|
|
const disabledChoiceReason = disabledChoice?.[1];
|
|
const disabled = !!disabledChoice || groupDisabled;
|
|
|
|
// TODO(epurkhiser): There should be a `name` and `label` attribute in
|
|
// the options type to allow for the aria label to work correctly. For
|
|
// now we slap a `toString` on there, but it may sometimes return
|
|
// [object Object] if the name is a react node.
|
|
|
|
return (
|
|
<Tooltip
|
|
key={index}
|
|
disabled={!disabledChoiceReason}
|
|
title={disabledChoiceReason}
|
|
>
|
|
<RadioLineItem index={index} aria-checked={value === id} disabled={disabled}>
|
|
<Radio
|
|
aria-label={name?.toString()}
|
|
disabled={disabled}
|
|
checked={value === id}
|
|
onChange={(e: React.FormEvent<HTMLInputElement>) =>
|
|
!disabled && onChange(id, e)
|
|
}
|
|
/>
|
|
<RadioLineText disabled={disabled}>{name}</RadioLineText>
|
|
{description && (
|
|
<Fragment>
|
|
{/* If there is a description then we want to have a 2x2 grid so the first column width aligns with Radio Button */}
|
|
<div />
|
|
<Description>{description}</Description>
|
|
</Fragment>
|
|
)}
|
|
</RadioLineItem>
|
|
</Tooltip>
|
|
);
|
|
})}
|
|
</Container>
|
|
);
|
|
}
|
|
|
|
const Container = styled('div')<ContainerProps>`
|
|
display: flex;
|
|
gap: ${p => space(p.orientInline ? 3 : 1)};
|
|
flex-direction: ${p => (p.orientInline ? 'row' : 'column')};
|
|
`;
|
|
|
|
const shouldForwardProp = (p: PropertyKey) =>
|
|
typeof p === 'string' && !['disabled', 'animate'].includes(p) && isPropValid(p);
|
|
|
|
export const RadioLineItem = styled('label', {shouldForwardProp})<{
|
|
index: number;
|
|
disabled?: boolean;
|
|
}>`
|
|
display: grid;
|
|
gap: 0.25em 0.5em;
|
|
grid-template-columns: max-content auto;
|
|
align-items: center;
|
|
cursor: ${p => (p.disabled ? 'default' : 'pointer')};
|
|
outline: none;
|
|
font-weight: normal;
|
|
margin: 0;
|
|
`;
|
|
|
|
const RadioLineText = styled('div', {shouldForwardProp})<{disabled?: boolean}>`
|
|
opacity: ${p => (p.disabled ? 0.4 : null)};
|
|
`;
|
|
|
|
const Description = styled('div')`
|
|
color: ${p => p.theme.gray300};
|
|
font-size: ${p => p.theme.fontSizeRelativeSmall};
|
|
line-height: 1.4em;
|
|
`;
|
|
|
|
export default RadioGroup;
|