You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
session-desktop/ts/components/basic/SessionRadio.tsx

193 lines
5.1 KiB
TypeScript

import React, { ChangeEvent } from 'react';
import styled, { CSSProperties } from 'styled-components';
import { Flex } from './Flex';
const StyledInput = styled.input<{
filledSize: number;
outlineOffset: number;
selectedColor?: string;
}>`
opacity: 0;
position: absolute;
cursor: ${props => (props.disabled ? 'not-allowed' : 'pointer')};
width: ${props => props.filledSize + props.outlineOffset}px;
height: ${props => props.filledSize + props.outlineOffset}px;
:checked + label:before {
background: ${props =>
props.disabled
? 'var(--disabled-color)'
: props.selectedColor
? props.selectedColor
: 'var(--primary-color)'};
}
`;
// NOTE (Will): We don't use a transition because it's too slow and creates flickering when changing buttons.
const StyledLabel = styled.label<{
disabled: boolean;
filledSize: number;
outlineOffset: number;
beforeMargins?: string;
}>`
cursor: pointer;
color: ${props => (props.disabled ? 'var(--disabled-color)' : 'var(--text-primary-color)')};
:before {
content: '';
display: inline-block;
border-radius: 100%;
padding: ${props => props.filledSize}px;
border: none;
outline: 1px solid currentColor; /* CSS variables don't work here */
outline-offset: ${props => props.outlineOffset}px;
${props => props.beforeMargins && `margin: ${props.beforeMargins};`};
}
`;
type SessionRadioProps = {
label: string;
value: string;
active: boolean;
inputName?: string;
beforeMargins?: string;
onClick?: (value: string) => void;
disabled?: boolean;
radioPosition?: 'left' | 'right';
style?: CSSProperties;
};
export const SessionRadio = (props: SessionRadioProps) => {
const {
label,
inputName,
value,
active,
onClick,
beforeMargins,
disabled = false,
radioPosition = 'left',
style,
} = props;
const clickHandler = (e: ChangeEvent<any>) => {
if (!disabled && onClick) {
// let something else catch the event if our click handler is not set
e.stopPropagation();
onClick(value);
}
};
const filledSize = 15 / 2;
const outlineOffset = 2;
return (
<Flex
container={true}
flexDirection={radioPosition === 'left' ? 'row' : 'row-reverse'}
justifyContent={radioPosition === 'left' ? 'flex-start' : 'flex-end'}
style={{ ...style, position: 'relative' }}
>
<StyledInput
type="radio"
name={inputName || ''}
value={value}
aria-checked={active}
checked={active}
onChange={clickHandler}
filledSize={filledSize * 2}
outlineOffset={outlineOffset}
disabled={disabled}
data-testid={`input-${value.replaceAll(' ', '-')}`} // data-testid cannot have spaces
/>
<StyledLabel
role="button"
onClick={clickHandler}
filledSize={filledSize - 1}
outlineOffset={outlineOffset}
beforeMargins={beforeMargins}
aria-label={label}
disabled={disabled}
data-testid={`label-${value}`}
>
{label}
</StyledLabel>
</Flex>
);
};
const StyledInputOutlineSelected = styled(StyledInput)`
color: ${props => (props.disabled ? 'var(--disabled-color)' : 'var(--text-primary-color)')};
label:before,
label:before {
outline: none;
}
:checked + label:before {
outline: 1px solid currentColor;
}
`;
const StyledLabelOutlineSelected = styled(StyledLabel)<{ selectedColor: string }>`
:before {
background: ${props =>
props.disabled
? 'var(--disabled-color)'
: props.selectedColor
? props.selectedColor
: 'var(--primary-color)'};
outline: 1px solid transparent; /* CSS variables don't work here */
}
`;
/**
* Keeping this component here so we can reuse the `StyledInput` and `StyledLabel` defined locally rather than exporting them
*/
export const SessionRadioPrimaryColors = (props: {
value: string;
active: boolean;
inputName?: string;
onClick: (value: string) => void;
ariaLabel: string;
color: string; // by default, we use the theme accent color but for the settings screen we need to be able to force it
disabled?: boolean;
}) => {
const { inputName, value, active, onClick, color, ariaLabel, disabled = false } = props;
function clickHandler(e: ChangeEvent<any>) {
e.stopPropagation();
onClick(value);
}
const filledSize = 31 / 2;
const outlineOffset = 5;
return (
<Flex container={true} padding="0 0 5px 0">
<StyledInputOutlineSelected
type="radio"
name={inputName || ''}
value={value}
aria-checked={active}
checked={active}
onChange={clickHandler}
filledSize={filledSize}
outlineOffset={outlineOffset}
selectedColor={color}
aria-label={ariaLabel}
disabled={disabled}
/>
<StyledLabelOutlineSelected
role="button"
onClick={clickHandler}
selectedColor={color}
filledSize={filledSize}
outlineOffset={outlineOffset}
disabled={disabled}
>
{''}
</StyledLabelOutlineSelected>
</Flex>
);
};