2.5 KB117 lines
Blame
1/**
2 * Copyright (c) Meta Platforms, Inc. and affiliates.
3 *
4 * This source code is licensed under the MIT license found in the
5 * LICENSE file in the root directory of this source tree.
6 */
7
8import type react from 'react';
9
10import * as stylex from '@stylexjs/stylex';
11import {useId} from 'react';
12import {Column} from './Flex';
13import {Tooltip} from './Tooltip';
14import {layout} from './theme/layout';
15import {spacing} from './theme/tokens.stylex';
16
17// stylex doesn't support :checked and :before simultaneously very well
18import './Radio.css';
19
20const styles = stylex.create({
21 group: {
22 appearance: 'none',
23 border: 'none',
24 boxSizing: 'border-box',
25 alignItems: 'flex-start',
26 marginInline: 0,
27 marginBlock: spacing.pad,
28 padding: 0,
29 },
30 label: {
31 cursor: 'pointer',
32 },
33 horizontal: {
34 flexDirection: 'row',
35 flexWrap: 'wrap',
36 },
37 disabled: {
38 opacity: 0.5,
39 cursor: 'not-allowed',
40 },
41});
42
43export function RadioGroup<T extends string>({
44 title,
45 choices,
46 current,
47 onChange,
48 horizontal,
49}: {
50 title?: string;
51 choices: Array<{value: T; title: react.ReactNode; tooltip?: string; disabled?: boolean}>;
52 current: T;
53 onChange: (t: T) => unknown;
54 horizontal?: boolean;
55}) {
56 const inner = (
57 <fieldset
58 {...stylex.props(layout.flexCol, styles.group, horizontal === true && styles.horizontal)}>
59 {choices.map(({value, title, tooltip, disabled}) => (
60 <Radio
61 key={value}
62 value={value}
63 title={title}
64 tooltip={tooltip}
65 disabled={disabled}
66 checked={current === value}
67 onChange={() => onChange(value)}
68 />
69 ))}
70 </fieldset>
71 );
72 return title == null ? (
73 inner
74 ) : (
75 <Column alignStart>
76 <strong>{title}</strong>
77 {inner}
78 </Column>
79 );
80}
81
82function Radio({
83 title,
84 value,
85 tooltip,
86 checked,
87 onChange,
88 disabled,
89}: {
90 title: react.ReactNode;
91 value: string;
92 tooltip?: string;
93 checked: boolean;
94 onChange: () => unknown;
95 disabled?: boolean;
96}) {
97 const id = useId();
98 const inner = (
99 <label
100 htmlFor={id}
101 {...stylex.props(layout.flexRow, styles.label, disabled && styles.disabled)}>
102 <input
103 type="radio"
104 id={id}
105 name={value}
106 value={value}
107 checked={checked}
108 onChange={onChange}
109 className="isl-radio"
110 disabled={disabled}
111 />
112 {title}
113 </label>
114 );
115 return tooltip ? <Tooltip title={tooltip}>{inner}</Tooltip> : inner;
116}
117