addons/components/Radio.tsxblame
View source
b69ab311/**
b69ab312 * Copyright (c) Meta Platforms, Inc. and affiliates.
b69ab313 *
b69ab314 * This source code is licensed under the MIT license found in the
b69ab315 * LICENSE file in the root directory of this source tree.
b69ab316 */
b69ab317
b69ab318import type react from 'react';
b69ab319
b69ab3110import * as stylex from '@stylexjs/stylex';
b69ab3111import {useId} from 'react';
b69ab3112import {Column} from './Flex';
b69ab3113import {Tooltip} from './Tooltip';
b69ab3114import {layout} from './theme/layout';
b69ab3115import {spacing} from './theme/tokens.stylex';
b69ab3116
b69ab3117// stylex doesn't support :checked and :before simultaneously very well
b69ab3118import './Radio.css';
b69ab3119
b69ab3120const styles = stylex.create({
b69ab3121 group: {
b69ab3122 appearance: 'none',
b69ab3123 border: 'none',
b69ab3124 boxSizing: 'border-box',
b69ab3125 alignItems: 'flex-start',
b69ab3126 marginInline: 0,
b69ab3127 marginBlock: spacing.pad,
b69ab3128 padding: 0,
b69ab3129 },
b69ab3130 label: {
b69ab3131 cursor: 'pointer',
b69ab3132 },
b69ab3133 horizontal: {
b69ab3134 flexDirection: 'row',
b69ab3135 flexWrap: 'wrap',
b69ab3136 },
b69ab3137 disabled: {
b69ab3138 opacity: 0.5,
b69ab3139 cursor: 'not-allowed',
b69ab3140 },
b69ab3141});
b69ab3142
b69ab3143export function RadioGroup<T extends string>({
b69ab3144 title,
b69ab3145 choices,
b69ab3146 current,
b69ab3147 onChange,
b69ab3148 horizontal,
b69ab3149}: {
b69ab3150 title?: string;
b69ab3151 choices: Array<{value: T; title: react.ReactNode; tooltip?: string; disabled?: boolean}>;
b69ab3152 current: T;
b69ab3153 onChange: (t: T) => unknown;
b69ab3154 horizontal?: boolean;
b69ab3155}) {
b69ab3156 const inner = (
b69ab3157 <fieldset
b69ab3158 {...stylex.props(layout.flexCol, styles.group, horizontal === true && styles.horizontal)}>
b69ab3159 {choices.map(({value, title, tooltip, disabled}) => (
b69ab3160 <Radio
b69ab3161 key={value}
b69ab3162 value={value}
b69ab3163 title={title}
b69ab3164 tooltip={tooltip}
b69ab3165 disabled={disabled}
b69ab3166 checked={current === value}
b69ab3167 onChange={() => onChange(value)}
b69ab3168 />
b69ab3169 ))}
b69ab3170 </fieldset>
b69ab3171 );
b69ab3172 return title == null ? (
b69ab3173 inner
b69ab3174 ) : (
b69ab3175 <Column alignStart>
b69ab3176 <strong>{title}</strong>
b69ab3177 {inner}
b69ab3178 </Column>
b69ab3179 );
b69ab3180}
b69ab3181
b69ab3182function Radio({
b69ab3183 title,
b69ab3184 value,
b69ab3185 tooltip,
b69ab3186 checked,
b69ab3187 onChange,
b69ab3188 disabled,
b69ab3189}: {
b69ab3190 title: react.ReactNode;
b69ab3191 value: string;
b69ab3192 tooltip?: string;
b69ab3193 checked: boolean;
b69ab3194 onChange: () => unknown;
b69ab3195 disabled?: boolean;
b69ab3196}) {
b69ab3197 const id = useId();
b69ab3198 const inner = (
b69ab3199 <label
b69ab31100 htmlFor={id}
b69ab31101 {...stylex.props(layout.flexRow, styles.label, disabled && styles.disabled)}>
b69ab31102 <input
b69ab31103 type="radio"
b69ab31104 id={id}
b69ab31105 name={value}
b69ab31106 value={value}
b69ab31107 checked={checked}
b69ab31108 onChange={onChange}
b69ab31109 className="isl-radio"
b69ab31110 disabled={disabled}
b69ab31111 />
b69ab31112 {title}
b69ab31113 </label>
b69ab31114 );
b69ab31115 return tooltip ? <Tooltip title={tooltip}>{inner}</Tooltip> : inner;
b69ab31116}