@synerise/ds-radio 1.1.16 → 1.1.18

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -3,6 +3,14 @@
3
3
  All notable changes to this project will be documented in this file.
4
4
  See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
5
5
 
6
+ ## [1.1.18](https://github.com/synerise/synerise-design/compare/@synerise/ds-radio@1.1.17...@synerise/ds-radio@1.1.18) (2026-07-23)
7
+
8
+ **Note:** Version bump only for package @synerise/ds-radio
9
+
10
+ ## [1.1.17](https://github.com/synerise/synerise-design/compare/@synerise/ds-radio@1.1.16...@synerise/ds-radio@1.1.17) (2026-06-27)
11
+
12
+ **Note:** Version bump only for package @synerise/ds-radio
13
+
6
14
  ## [1.1.16](https://github.com/synerise/synerise-design/compare/@synerise/ds-radio@1.1.15...@synerise/ds-radio@1.1.16) (2026-06-17)
7
15
 
8
16
  **Note:** Version bump only for package @synerise/ds-radio
package/CLAUDE.md ADDED
@@ -0,0 +1,109 @@
1
+ # Radio (`@synerise/ds-radio`)
2
+
3
+ > DS-native, antd-free radio: a `label` prop (renders via `FormFieldLabel`), a `description` below the radio, a context-based `Radio.Group`, a segmented `Radio.Button`, and group-level `fullWidth`/`big` layout variants.
4
+
5
+ ## Package structure
6
+
7
+ ```
8
+ src/
9
+ Radio.tsx — assembles Radio (= RadioBase) + .Group + .Button
10
+ Radio.types.ts — RadioProps, RadioGroupProps, RadioButtonProps, RadioChangeEvent, RadioValueType
11
+ Radio.styles.tsx — styled-components (RadioLabel = radio-dot visual; RadioGroupWrapper = layout + segmented buttons)
12
+ RadioGroup.tsx — DS-native Radio.Group (single-select context provider)
13
+ RadioContext.ts — group context (value + onChange)
14
+ components/
15
+ RadioBase.tsx — the radio input (DOM + group consumption + event synthesis)
16
+ RadioButton.tsx — segmented button-style radio
17
+ index.ts — public exports
18
+ __specs__/Radio.spec.tsx — Vitest tests
19
+ ```
20
+
21
+ No LESS, no antd — antd's base + `radio.mixin.less` were inlined into `Radio.styles.tsx`.
22
+
23
+ ## Public exports
24
+
25
+ ### `Radio` (default export)
26
+
27
+ Compound component: `Radio` + `Radio.Group` + `Radio.Button`.
28
+
29
+ | Prop | Type | Default | Description |
30
+ |------|------|---------|-------------|
31
+ | `label` | `ReactNode` | `undefined` | Renders label text via `FormFieldLabel`. Takes precedence over `children` |
32
+ | `description` | `ReactNode` | `undefined` | Additional text rendered below the radio in small grey text |
33
+ | `children` | `ReactNode` | `undefined` | **@deprecated** — use `label` instead. Only rendered when `label` is not provided |
34
+ | `checked` | `boolean` | `undefined` | Controlled checked state |
35
+ | `defaultChecked` | `boolean` | `undefined` | Initial uncontrolled checked state |
36
+ | `disabled` | `boolean` | `undefined` | Disables the radio; also dims `label` and `description` to 40% opacity |
37
+ | `value` | `any` | `undefined` | Value used by `Radio.Group` for comparison |
38
+ | `autoFocus` | `boolean` | `undefined` | Focuses the input on mount |
39
+ | `value`, `name`, `id`, `autoFocus`, `tabIndex`, `onChange` | — | — | DS-native props (no antd inheritance). `onChange` receives a `RadioChangeEvent` (`target.value`, `target.checked`). |
40
+
41
+ ### `Radio.Group`
42
+
43
+ Wraps `AntdRadio.Group` with two additional layout props:
44
+
45
+ | Prop | Type | Default | Description |
46
+ |------|------|---------|-------------|
47
+ | `fullWidth` | `boolean` | `undefined` | Makes the group `display: flex; width: 100%` — each radio button gets `flex: 1` |
48
+ | `big` | `boolean` | `undefined` | When combined with `fullWidth`, sets button height to `48px` instead of `32px` |
49
+ | `size` | `'small' \| 'middle' \| 'large'` | `'middle'` | Segmented `Radio.Button` height: `small` 24px · `middle` 32px · `large` 40px |
50
+ | + DS `RadioGroupProps` | — | — | `value`, `defaultValue`, `onChange`, `options`, `optionType`, `buttonStyle`, `disabled`, `name` (DS-native, no antd inheritance) |
51
+
52
+ ### `Radio.Button`
53
+
54
+ DS-native segmented button-style radio (replaces the former `AntdRadio.Button` re-export). Reads the `Radio.Group` context; styled by the group as a connected segment. Use for segmented/toggle-button radio groups (`optionType="button"` or explicit `<Radio.Button>` children).
55
+
56
+ ### Types exported
57
+
58
+ | Type | Description |
59
+ |------|-------------|
60
+ | `RadioProps` | Props for `Radio` |
61
+ | `RadioGroupProps` | Props for `Radio.Group` |
62
+ | `Props` | **@deprecated** alias for `RadioProps` |
63
+
64
+ ## Usage patterns
65
+
66
+ ```tsx
67
+ import Radio from '@synerise/ds-radio';
68
+
69
+ // Basic radio with label and description
70
+ <Radio.Group value={selected} onChange={(e) => setSelected(e.target.value)}>
71
+ <Radio value="a" label="Option A" description="Some extra context" />
72
+ <Radio value="b" label="Option B" />
73
+ </Radio.Group>
74
+
75
+ // Full-width button group
76
+ <Radio.Group value={tab} onChange={(e) => setTab(e.target.value)} fullWidth big>
77
+ <Radio.Button value="list">List</Radio.Button>
78
+ <Radio.Button value="grid">Grid</Radio.Button>
79
+ </Radio.Group>
80
+
81
+ // Shorthand options
82
+ <Radio.Group options={['A', 'B', 'C']} value={val} onChange={handleChange} />
83
+ ```
84
+
85
+ ## Styling
86
+
87
+ Styles live in `Radio.styles.tsx`. **Each element is its own styled-component and owns its styles** — the `.ant-radio-*` / `ds-radio-*` class names are kept on the elements purely as hooks (ui-tests / interim external CSS), never used as styling selectors; state is passed in as transient `$`-props. Uses `@synerise/ds-core` theme tokens:
88
+ - `grey-600` — `Description` text colour; `opacity: 0.4` applied on `disabled` to `Label`/`Description`/`RadioText`
89
+ - `Label` extends `FormFieldLabel` from `@synerise/ds-form-field`
90
+ - radio dot: `RadioBox` / `RadioInput` / `RadioInner` / `RadioText`. `RadioInner` is the 16px circle (`grey-300` border, blue-600 dot scaled in on `$checked`, greyed on `$disabled`); `RadioLabel` holds the cross-element hover-preview + focus-ring rules (`${RadioInput}:focus + ${RadioInner}`, white centre once `$checked`).
91
+ - segmented button: `RadioButtonLabel` (+ `RadioButtonInput`) owns the full segment visual including the connected-border `:first-child`/`:last-child` rules and the `$solid`/`$checked`/`$disabled` variants — driven by `$`-props read from the group context, not by group-level `.ant-radio-button-*` selectors.
92
+ - `RadioGroupWrapper` is `inline-block` (vertical stacking) and, with `fullWidth`, switches to flex and stretches `${RadioLabel}`/`${RadioButtonLabel}` (`big` → 48px height).
93
+ - `AdditionalData` is the margin wrapper for `Description`
94
+
95
+ All styling is in `Radio.styles.tsx` (no LESS).
96
+
97
+ ## Key dependencies
98
+
99
+ - DS-native (no antd) — radio + `Radio.Group` (context) + `Radio.Button` reimplemented in `src/`
100
+ - `@synerise/ds-form-field` — `FormFieldLabel` used for the `label` prop rendering
101
+ - `@synerise/ds-typography` — `macro.small` applied to `Description` font size
102
+
103
+ ## Implementation notes
104
+
105
+ - **`label` vs `children`**: `label` takes precedence — if `label` is set, `children` is ignored entirely. `children` is marked `@deprecated` in the type file.
106
+ - **`Radio.Button` is DS-native**: reads `RadioContext` for checked state; the segment visual (connected borders, checked, focus, `buttonStyle` solid/outline) is styled by `RadioGroupWrapper`.
107
+ - **`RadioChangeEvent`** is synthesised from the native input change (`{ target: { value, checked, name }, stopPropagation, preventDefault, nativeEvent }`); `target.value` is `RadioValueType` (consumers needing a narrower type cast it — antd typed it `any`).
108
+ - **`AdditionalData` styled-component** is marked `@deprecated` in its source comment but is still the live wrapper div for `Description` — the comment is misleading.
109
+ - **Uses Vitest** for testing.
package/dist/Radio.d.ts CHANGED
@@ -1,7 +1,9 @@
1
- import { default as React } from 'react';
2
- import { RadioGroupProps, RadioProps } from './Radio.types';
3
- declare const Radio: (({ description, label, children, ...antdRadioButtonProps }: RadioProps) => React.JSX.Element) & {
4
- Group: ({ children, fullWidth, big, ...props }: RadioGroupProps) => React.JSX.Element;
5
- Button: React.ForwardRefExoticComponent<import('antd/lib/radio/radioButton').RadioButtonProps & React.RefAttributes<any>>;
1
+ /**
2
+ * DS-native, antd-free radio. `Radio` (radio + label + description) with `Radio.Group` (single-select
3
+ * context) and `Radio.Button` (segmented control) attached as static members.
4
+ */
5
+ declare const Radio: (({ value, checked, defaultChecked, disabled, name, id, autoFocus, tabIndex, onChange, onClick, label, description, children, className, style, ...rest }: import('./Radio.types').RadioProps) => import("react").JSX.Element) & {
6
+ Group: ({ value, defaultValue, onChange, options, optionType, buttonStyle, size, disabled, name, fullWidth, big, children, className, style, }: import('./Radio.types').RadioGroupProps) => import("react").JSX.Element;
7
+ Button: ({ value, disabled, children, className, checked, onChange, onClick, style, ...rest }: import('./Radio.types').RadioButtonProps) => import("react").JSX.Element;
6
8
  };
7
9
  export default Radio;
package/dist/Radio.js CHANGED
@@ -1,29 +1,9 @@
1
- import { jsxs, jsx } from "react/jsx-runtime";
2
- import { Radio as Radio$1 } from "antd";
3
- import { RadioWrapper, AntRadio, Label, AdditionalData, Description, AntRadioGroup } from "./Radio.styles.js";
4
- import "./style/index.css";
5
- const Group = ({
6
- children,
7
- fullWidth,
8
- big,
9
- ...props
10
- }) => {
11
- return /* @__PURE__ */ jsx(AntRadioGroup, { fullWidth, big, ...props, children });
12
- };
13
- const RadioComponent = ({
14
- description,
15
- label,
16
- children,
17
- ...antdRadioButtonProps
18
- }) => {
19
- return /* @__PURE__ */ jsxs(RadioWrapper, { children: [
20
- /* @__PURE__ */ jsx(AntRadio, { ...antdRadioButtonProps, children: label ? /* @__PURE__ */ jsx(Label, { disabled: antdRadioButtonProps.disabled, children: label }) : children }),
21
- description && /* @__PURE__ */ jsx(AdditionalData, { children: /* @__PURE__ */ jsx(Description, { disabled: antdRadioButtonProps.disabled, children: description }) })
22
- ] });
23
- };
24
- const Radio = Object.assign(RadioComponent, {
25
- Group,
26
- Button: Radio$1.Button
1
+ import RadioGroup from "./RadioGroup.js";
2
+ import { RadioBase } from "./components/RadioBase.js";
3
+ import { RadioButton } from "./components/RadioButton.js";
4
+ const Radio = Object.assign(RadioBase, {
5
+ Group: RadioGroup,
6
+ Button: RadioButton
27
7
  });
28
8
  export {
29
9
  Radio as default
@@ -1,4 +1,3 @@
1
- import { default as React } from 'react';
2
1
  export declare const RadioWrapper: import('styled-components').StyledComponent<"div", any, {}, never>;
3
2
  export declare const Description: import('styled-components').StyledComponent<"div", any, {
4
3
  disabled?: boolean;
@@ -7,8 +6,31 @@ export declare const Label: import('styled-components').StyledComponent<({ id, l
7
6
  disabled?: boolean;
8
7
  }, never>;
9
8
  export declare const AdditionalData: import('styled-components').StyledComponent<"div", any, {}, never>;
10
- export declare const AntRadio: import('styled-components').StyledComponent<({ ...rest }: any) => React.JSX.Element, any, {}, never>;
11
- export declare const AntRadioGroup: import('styled-components').StyledComponent<React.MemoExoticComponent<React.ForwardRefExoticComponent<import('antd').RadioGroupProps & React.RefAttributes<HTMLDivElement>>>, any, {
9
+ export declare const RadioInput: import('styled-components').StyledComponent<"input", any, {}, never>;
10
+ export declare const RadioInner: import('styled-components').StyledComponent<"span", any, {
11
+ $checked?: boolean;
12
+ $disabled?: boolean;
13
+ }, never>;
14
+ export declare const RadioText: import('styled-components').StyledComponent<"span", any, {
15
+ $checked?: boolean;
16
+ $disabled?: boolean;
17
+ }, never>;
18
+ export declare const RadioBox: import('styled-components').StyledComponent<"span", any, {}, never>;
19
+ export declare const RadioLabel: import('styled-components').StyledComponent<"label", any, {
20
+ $checked?: boolean;
21
+ $disabled?: boolean;
22
+ }, never>;
23
+ export declare const RadioButtonInput: import('styled-components').StyledComponent<"input", any, {}, never>;
24
+ export declare const RadioButtonLabel: import('styled-components').StyledComponent<"label", any, {
25
+ $checked?: boolean;
26
+ $disabled?: boolean;
27
+ $solid?: boolean;
28
+ $size?: "small" | "middle" | "large";
29
+ }, never>;
30
+ /**
31
+ * The `Radio.Group` container. Lays out the children and owns the `fullWidth`/`big` layout.
32
+ */
33
+ export declare const RadioGroupWrapper: import('styled-components').StyledComponent<"div", any, {
12
34
  fullWidth?: boolean;
13
35
  big?: boolean;
14
36
  }, never>;
@@ -1,39 +1,70 @@
1
- import { jsx } from "react/jsx-runtime";
2
- import { Radio } from "antd";
3
1
  import styled, { css } from "styled-components";
4
2
  import { FormFieldLabel } from "@synerise/ds-form-field";
5
3
  import { macro } from "@synerise/ds-typography";
6
4
  const RadioWrapper = /* @__PURE__ */ styled.div.withConfig({
7
5
  displayName: "Radiostyles__RadioWrapper",
8
6
  componentId: "sc-137n5jf-0"
9
- })(["&{display:block;margin-bottom:15px;}"]);
7
+ })(["display:block;margin-bottom:15px;"]);
10
8
  const Description = /* @__PURE__ */ styled.div.withConfig({
11
9
  displayName: "Radiostyles__Description",
12
10
  componentId: "sc-137n5jf-1"
13
- })(["color:", ";", " ", ""], (props) => props.theme.palette["grey-600"], (props) => props.disabled ? `opacity: 0.4;` : "", macro.small);
11
+ })(["color:", ";", " ", ""], (props) => props.theme.palette["grey-600"], (props) => props.disabled ? "opacity: 0.4;" : "", macro.small);
14
12
  const Label = /* @__PURE__ */ styled(FormFieldLabel).withConfig({
15
13
  displayName: "Radiostyles__Label",
16
14
  componentId: "sc-137n5jf-2"
17
- })(["", ""], (props) => props.disabled ? `opacity: 0.4;` : "");
15
+ })(["", ""], (props) => props.disabled ? "opacity: 0.4;" : "");
18
16
  const AdditionalData = /* @__PURE__ */ styled.div.withConfig({
19
17
  displayName: "Radiostyles__AdditionalData",
20
18
  componentId: "sc-137n5jf-3"
21
19
  })(["margin:4px 8px 15px 28px;"]);
22
- const AntRadio = /* @__PURE__ */ styled(({
23
- ...rest
24
- }) => /* @__PURE__ */ jsx(Radio, { ...rest })).withConfig({
25
- displayName: "Radiostyles__AntRadio",
20
+ const RadioInput = /* @__PURE__ */ styled.input.withConfig({
21
+ displayName: "Radiostyles__RadioInput",
26
22
  componentId: "sc-137n5jf-4"
27
- })([".ant-radio{height:16px;top:0;}.ant-radio + span{flex-grow:1;}"]);
28
- const AntRadioGroup = /* @__PURE__ */ styled(Radio.Group).withConfig({
29
- displayName: "Radiostyles__AntRadioGroup",
23
+ })(["position:absolute;inset:0;width:16px;height:16px;margin:0;padding:0;opacity:0;cursor:pointer;z-index:1;"]);
24
+ const RadioInner = /* @__PURE__ */ styled.span.withConfig({
25
+ displayName: "Radiostyles__RadioInner",
30
26
  componentId: "sc-137n5jf-5"
31
- })(["", ""], (props) => props.fullWidth && css(["&&{display:flex;width:100%;label{flex:1;height:", ";display:flex;align-items:center;justify-content:center;font-weight:500;}}"], props.big ? "48px" : "32px"));
27
+ })(["position:relative;display:block;width:16px;height:16px;box-sizing:border-box;background-color:", ";border:1px solid ", ";border-radius:50%;&::after{content:'';position:absolute;inset:0;margin:auto;width:8px;height:8px;border-radius:50%;background-color:", ";transform:scale(0);transition:transform 0.2s ease;}", " ", ""], (props) => props.theme.palette.white, (props) => props.theme.palette["grey-300"], (props) => props.theme.palette["blue-600"], (props) => props.$checked && css(["border-color:", ";border-width:2px;&::after{transform:scale(1);}"], props.theme.palette["blue-600"]), (props) => props.$disabled && css(["border-color:", " !important;background-color:", " !important;&::after{background-color:", ";}"], props.theme.palette["grey-200"], props.theme.palette["grey-050"], props.theme.palette["grey-400"]));
28
+ const RadioText = /* @__PURE__ */ styled.span.withConfig({
29
+ displayName: "Radiostyles__RadioText",
30
+ componentId: "sc-137n5jf-6"
31
+ })(["flex-grow:1;margin:0 12px;padding:0;font-weight:500;color:", ";", ""], (props) => props.$checked ? props.theme.palette["grey-800"] : props.theme.palette["grey-700"], (props) => props.$disabled && css(["color:", ";opacity:0.4;"], props.theme.palette["grey-600"]));
32
+ const RadioBox = /* @__PURE__ */ styled.span.withConfig({
33
+ displayName: "Radiostyles__RadioBox",
34
+ componentId: "sc-137n5jf-7"
35
+ })(["position:relative;top:0;display:inline-flex;flex:none;height:16px;cursor:pointer;"]);
36
+ const RadioLabel = /* @__PURE__ */ styled.label.withConfig({
37
+ displayName: "Radiostyles__RadioLabel",
38
+ componentId: "sc-137n5jf-8"
39
+ })(["display:flex;align-items:flex-start;margin:0 8px 0 0;cursor:pointer;white-space:normal;", ":hover + ", "{border-color:", ";}", ":focus + ", "{border-color:", ";background-color:", ";border-width:2px;}", ""], RadioInput, RadioInner, (props) => props.theme.palette["grey-400"], RadioInput, RadioInner, (props) => props.theme.palette["blue-600"], (props) => props.$checked ? props.theme.palette.white : props.theme.palette["blue-050"], (props) => props.$disabled && css(["cursor:not-allowed;", ",", "{cursor:not-allowed;}"], RadioBox, RadioInput));
40
+ const RadioButtonInput = /* @__PURE__ */ styled.input.withConfig({
41
+ displayName: "Radiostyles__RadioButtonInput",
42
+ componentId: "sc-137n5jf-9"
43
+ })(["position:absolute;inset:0;opacity:0;cursor:pointer;"]);
44
+ const BUTTON_HEIGHT = {
45
+ small: "24px",
46
+ middle: "32px",
47
+ large: "40px"
48
+ };
49
+ const RadioButtonLabel = /* @__PURE__ */ styled.label.withConfig({
50
+ displayName: "Radiostyles__RadioButtonLabel",
51
+ componentId: "sc-137n5jf-10"
52
+ })(["position:relative;display:inline-flex;align-items:center;justify-content:center;height:", ";margin:0;padding:0 16px;color:", ";background-color:", ";border:1px solid ", ";border-left-width:0;cursor:pointer;&:first-child{border-left:1px solid ", ";border-radius:3px 0 0 3px;}&:last-child{border-radius:0 3px 3px 0;}&:hover{background-color:", ";}&:focus-within{border-color:", ";box-shadow:inset 0 0 0 1px ", ";z-index:2;}", " ", " ", ""], (props) => BUTTON_HEIGHT[props.$size ?? "middle"], (props) => props.theme.palette["grey-700"], (props) => props.theme.palette["grey-050"], (props) => props.theme.palette["grey-300"], (props) => props.theme.palette["grey-300"], (props) => props.theme.palette.white, (props) => props.theme.palette["blue-600"], (props) => props.theme.palette["blue-600"], (props) => props.$checked && css(["&&{color:", ";border-color:", ";box-shadow:-1px 0 0 0 ", ";z-index:1;}"], props.theme.palette["blue-600"], props.theme.palette["blue-600"], props.theme.palette["blue-600"]), (props) => props.$disabled && css(["color:", ";opacity:0.4;cursor:not-allowed;", "{cursor:not-allowed;}"], props.theme.palette["grey-700"], RadioButtonInput), (props) => props.$solid && props.$checked && !props.$disabled && css(["&&{color:", ";background-color:", ";border-color:", ";&:hover{background-color:", ";border-color:", ";box-shadow:-1px 0 0 0 ", ";}}"], props.theme.palette.white, props.theme.palette["blue-600"], props.theme.palette["blue-600"], props.theme.palette["blue-500"], props.theme.palette["blue-500"], props.theme.palette["blue-500"]));
53
+ const RadioGroupWrapper = /* @__PURE__ */ styled.div.withConfig({
54
+ displayName: "Radiostyles__RadioGroupWrapper",
55
+ componentId: "sc-137n5jf-11"
56
+ })(["display:inline-block;", ""], (props) => props.fullWidth && css(["&&{display:flex;width:100%;", ",", "{flex:1;height:", ";display:flex;align-items:center;justify-content:center;font-weight:500;}}"], RadioLabel, RadioButtonLabel, props.big ? "48px" : "32px"));
32
57
  export {
33
58
  AdditionalData,
34
- AntRadio,
35
- AntRadioGroup,
36
59
  Description,
37
60
  Label,
61
+ RadioBox,
62
+ RadioButtonInput,
63
+ RadioButtonLabel,
64
+ RadioGroupWrapper,
65
+ RadioInner,
66
+ RadioInput,
67
+ RadioLabel,
68
+ RadioText,
38
69
  RadioWrapper
39
70
  };
@@ -1,18 +1,74 @@
1
- import { RadioGroupProps as AntdRadioGroupProps, RadioProps as AntdRadioProps } from 'antd';
2
- import { ReactNode } from 'react';
3
- export type RadioProps = AntdRadioProps & {
4
- description?: ReactNode;
1
+ import { CSSProperties, MouseEventHandler, ReactNode } from 'react';
2
+ import { DataAttributes } from '@synerise/ds-utils';
3
+ export type RadioValueType = string | number | boolean;
4
+ /** antd-compatible radio change event. */
5
+ export type RadioChangeEventTarget = {
6
+ value?: RadioValueType;
7
+ checked: boolean;
8
+ name?: string;
9
+ [key: string]: unknown;
10
+ };
11
+ export type RadioChangeEvent = {
12
+ target: RadioChangeEventTarget;
13
+ stopPropagation: () => void;
14
+ preventDefault: () => void;
15
+ nativeEvent: Event;
16
+ };
17
+ export type RadioProps = {
18
+ value?: RadioValueType;
19
+ checked?: boolean;
20
+ defaultChecked?: boolean;
21
+ disabled?: boolean;
22
+ name?: string;
23
+ id?: string;
24
+ autoFocus?: boolean;
25
+ tabIndex?: number;
26
+ onChange?: (event: RadioChangeEvent) => void;
27
+ /** Label text (rendered via `FormFieldLabel`). Takes precedence over `children`. */
5
28
  label?: ReactNode;
6
- /**
7
- * @deprecated, use label instead
8
- */
29
+ /** Helper text rendered below the radio. */
30
+ description?: ReactNode;
31
+ /** @deprecated use `label` instead */
9
32
  children?: ReactNode;
10
- };
11
- /**
12
- * @deprecated, use RadioProps instead
13
- */
33
+ className?: string;
34
+ style?: CSSProperties;
35
+ onClick?: MouseEventHandler<HTMLElement>;
36
+ } & DataAttributes;
37
+ /** @deprecated use `RadioProps` instead */
14
38
  export type Props = RadioProps;
15
- export type RadioGroupProps = AntdRadioGroupProps & {
39
+ export type RadioOptionType = {
40
+ label: ReactNode;
41
+ value: RadioValueType;
42
+ disabled?: boolean;
43
+ };
44
+ export type RadioGroupProps = {
45
+ value?: RadioValueType;
46
+ defaultValue?: RadioValueType;
47
+ onChange?: (event: RadioChangeEvent) => void;
48
+ options?: (RadioValueType | RadioOptionType)[];
49
+ optionType?: 'default' | 'button';
50
+ buttonStyle?: 'outline' | 'solid';
51
+ /** Segmented-button height: `small` 24px · `middle` (default) 32px · `large` 40px. */
52
+ size?: 'small' | 'middle' | 'large';
53
+ disabled?: boolean;
54
+ name?: string;
55
+ /** Group laid out full-width with equal-flex buttons. */
16
56
  fullWidth?: boolean;
57
+ /** With `fullWidth`, taller (48px) buttons. */
17
58
  big?: boolean;
18
- };
59
+ children?: ReactNode;
60
+ className?: string;
61
+ style?: CSSProperties;
62
+ } & DataAttributes;
63
+ export type RadioButtonProps = {
64
+ value?: RadioValueType;
65
+ disabled?: boolean;
66
+ children?: ReactNode;
67
+ className?: string;
68
+ /** Override the group-derived checked state (e.g. when used standalone). */
69
+ checked?: boolean;
70
+ /** Fired on selection, in addition to the group's `onChange`. */
71
+ onChange?: (event: RadioChangeEvent) => void;
72
+ onClick?: MouseEventHandler<HTMLElement>;
73
+ style?: CSSProperties;
74
+ } & DataAttributes;
@@ -0,0 +1,17 @@
1
+ import { RadioValueType } from './Radio.types';
2
+ export type RadioGroupContextValue = {
3
+ /** Currently selected value (single). */
4
+ value?: RadioValueType;
5
+ /** Select a value; the group builds the change event + calls the consumer onChange. */
6
+ onChange: (value: RadioValueType, nativeEvent: Event) => void;
7
+ disabled?: boolean;
8
+ name?: string;
9
+ /** `'button'` makes the group render segmented `Radio.Button`s. */
10
+ optionType?: 'default' | 'button';
11
+ /** Button visual when `optionType === 'button'`. */
12
+ buttonStyle?: 'outline' | 'solid';
13
+ /** Segmented-button height (`small`/`middle`/`large`). */
14
+ size?: 'small' | 'middle' | 'large';
15
+ };
16
+ /** Provided by `Radio.Group`; consumed by child `Radio`/`Radio.Button`. `null` when standalone. */
17
+ export declare const RadioGroupContext: import('react').Context<RadioGroupContextValue | null>;
@@ -0,0 +1,5 @@
1
+ import { createContext } from "react";
2
+ const RadioGroupContext = createContext(null);
3
+ export {
4
+ RadioGroupContext
5
+ };
@@ -0,0 +1,9 @@
1
+ import { default as React } from 'react';
2
+ import { RadioGroupProps } from './Radio.types';
3
+ /**
4
+ * DS-native `Radio.Group`. Provides a single-value selection context that child `Radio`/`Radio.Button`s
5
+ * read, and emits `onChange(RadioChangeEvent)` (`target.value` = the selected value). Controlled via
6
+ * `value`, uncontrolled via `defaultValue`. `optionType="button"` renders segmented `Radio.Button`s.
7
+ */
8
+ declare const RadioGroup: ({ value, defaultValue, onChange, options, optionType, buttonStyle, size, disabled, name, fullWidth, big, children, className, style, }: RadioGroupProps) => React.JSX.Element;
9
+ export default RadioGroup;
@@ -0,0 +1,61 @@
1
+ import { jsx } from "react/jsx-runtime";
2
+ import { useState, useCallback, useMemo } from "react";
3
+ import { RadioGroupWrapper } from "./Radio.styles.js";
4
+ import { RadioGroupContext } from "./RadioContext.js";
5
+ import { RadioBase } from "./components/RadioBase.js";
6
+ import { RadioButton } from "./components/RadioButton.js";
7
+ const cx = (...classes) => classes.filter(Boolean).join(" ");
8
+ const RadioGroup = ({
9
+ value,
10
+ defaultValue,
11
+ onChange,
12
+ options,
13
+ optionType = "default",
14
+ buttonStyle = "outline",
15
+ size,
16
+ disabled,
17
+ name,
18
+ fullWidth,
19
+ big,
20
+ children,
21
+ className,
22
+ style
23
+ }) => {
24
+ const isControlled = value !== void 0;
25
+ const [internalValue, setInternalValue] = useState(defaultValue);
26
+ const resolvedValue = isControlled ? value : internalValue;
27
+ const handleChange = useCallback((newValue, nativeEvent) => {
28
+ if (!isControlled) {
29
+ setInternalValue(newValue);
30
+ }
31
+ const event = {
32
+ target: {
33
+ value: newValue,
34
+ checked: true,
35
+ name
36
+ },
37
+ stopPropagation: () => nativeEvent.stopPropagation?.(),
38
+ preventDefault: () => nativeEvent.preventDefault?.(),
39
+ nativeEvent
40
+ };
41
+ onChange?.(event);
42
+ }, [isControlled, name, onChange]);
43
+ const contextValue = useMemo(() => ({
44
+ value: resolvedValue,
45
+ onChange: handleChange,
46
+ disabled,
47
+ name,
48
+ optionType,
49
+ buttonStyle,
50
+ size
51
+ }), [resolvedValue, handleChange, disabled, name, optionType, buttonStyle, size]);
52
+ const normalizedOptions = options?.map((option) => typeof option === "object" ? option : {
53
+ label: String(option),
54
+ value: option
55
+ });
56
+ const renderOptions = () => normalizedOptions?.map((option) => optionType === "button" ? /* @__PURE__ */ jsx(RadioButton, { value: option.value, disabled: option.disabled, children: option.label }, String(option.value)) : /* @__PURE__ */ jsx(RadioBase, { value: option.value, disabled: option.disabled, label: option.label }, String(option.value)));
57
+ return /* @__PURE__ */ jsx(RadioGroupContext.Provider, { value: contextValue, children: /* @__PURE__ */ jsx(RadioGroupWrapper, { className: cx("ant-radio-group", `ant-radio-group-${buttonStyle}`, "ds-radio-group", className), style, fullWidth, big, children: normalizedOptions ? renderOptions() : children }) });
58
+ };
59
+ export {
60
+ RadioGroup as default
61
+ };
@@ -0,0 +1,3 @@
1
+ import { default as React } from 'react';
2
+ import { RadioProps } from '../Radio.types';
3
+ export declare const RadioBase: ({ value, checked, defaultChecked, disabled, name, id, autoFocus, tabIndex, onChange, onClick, label, description, children, className, style, ...rest }: RadioProps) => React.JSX.Element;
@@ -0,0 +1,66 @@
1
+ import { jsxs, jsx } from "react/jsx-runtime";
2
+ import { useContext, useState } from "react";
3
+ import { RadioWrapper, RadioLabel, RadioBox, RadioInput, RadioInner, RadioText, Label, AdditionalData, Description } from "../Radio.styles.js";
4
+ import { RadioGroupContext } from "../RadioContext.js";
5
+ const cx = (...classes) => classes.filter(Boolean).join(" ");
6
+ const RadioBase = ({
7
+ value,
8
+ checked,
9
+ defaultChecked,
10
+ disabled,
11
+ name,
12
+ id,
13
+ autoFocus,
14
+ tabIndex,
15
+ onChange,
16
+ onClick,
17
+ label,
18
+ description,
19
+ children,
20
+ className,
21
+ style,
22
+ // forward data-*/aria-* (e.g. data-testid) to the input, as antd's rc-checkbox did
23
+ ...rest
24
+ }) => {
25
+ const group = useContext(RadioGroupContext);
26
+ const isControlled = checked !== void 0;
27
+ const [internalChecked, setInternalChecked] = useState(Boolean(defaultChecked));
28
+ const isChecked = group ? group.value === value : isControlled ? Boolean(checked) : internalChecked;
29
+ const isDisabled = Boolean(disabled ?? group?.disabled);
30
+ const handleChange = (event) => {
31
+ if (isDisabled) {
32
+ return;
33
+ }
34
+ if (group) {
35
+ group.onChange(value, event.nativeEvent);
36
+ return;
37
+ }
38
+ if (!isControlled) {
39
+ setInternalChecked(true);
40
+ }
41
+ const changeEvent = {
42
+ target: {
43
+ value,
44
+ checked: true,
45
+ name
46
+ },
47
+ stopPropagation: () => event.stopPropagation(),
48
+ preventDefault: () => event.preventDefault(),
49
+ nativeEvent: event.nativeEvent
50
+ };
51
+ onChange?.(changeEvent);
52
+ };
53
+ return /* @__PURE__ */ jsxs(RadioWrapper, { className: cx("ds-radio-field", className), style, children: [
54
+ /* @__PURE__ */ jsxs(RadioLabel, { className: cx("ant-radio-wrapper", "ds-radio", isChecked && "ant-radio-wrapper-checked", isDisabled && "ant-radio-wrapper-disabled"), $checked: isChecked, $disabled: isDisabled, onClick, children: [
55
+ /* @__PURE__ */ jsxs(RadioBox, { className: cx("ant-radio", "ds-radio-box", isChecked && "ant-radio-checked", isDisabled && "ant-radio-disabled"), children: [
56
+ /* @__PURE__ */ jsx(RadioInput, { type: "radio", className: "ant-radio-input ds-radio-input", checked: isChecked, disabled: isDisabled, name: name ?? group?.name, id, autoFocus, tabIndex, value: value === void 0 ? void 0 : String(value), onChange: handleChange, ...rest }),
57
+ /* @__PURE__ */ jsx(RadioInner, { className: "ant-radio-inner ds-radio-inner", $checked: isChecked, $disabled: isDisabled })
58
+ ] }),
59
+ /* @__PURE__ */ jsx(RadioText, { $checked: isChecked, $disabled: isDisabled, children: label ? /* @__PURE__ */ jsx(Label, { disabled: isDisabled, children: label }) : children })
60
+ ] }),
61
+ description && /* @__PURE__ */ jsx(AdditionalData, { children: /* @__PURE__ */ jsx(Description, { disabled: isDisabled, children: description }) })
62
+ ] });
63
+ };
64
+ export {
65
+ RadioBase
66
+ };
@@ -0,0 +1,10 @@
1
+ import { default as React } from 'react';
2
+ import { RadioButtonProps } from '../Radio.types';
3
+ /**
4
+ * Segmented button-style radio (`Radio.Button`). Reproduces antd's `ant-radio-button-wrapper` DOM
5
+ * (class names kept as hooks only); the segment visual lives in the `RadioButtonLabel`
6
+ * styled-component, driven by checked/disabled/solid `$`-props read from the group context. When
7
+ * standalone (no group) `checked` applies directly; inside a group the group's value wins (antd
8
+ * parity). `onChange`/`onClick`/`style` always pass through.
9
+ */
10
+ export declare const RadioButton: ({ value, disabled, children, className, checked, onChange, onClick, style, ...rest }: RadioButtonProps) => React.JSX.Element;
@@ -0,0 +1,49 @@
1
+ import { jsxs, jsx } from "react/jsx-runtime";
2
+ import { useContext } from "react";
3
+ import { RadioButtonLabel, RadioButtonInput } from "../Radio.styles.js";
4
+ import { RadioGroupContext } from "../RadioContext.js";
5
+ const cx = (...classes) => classes.filter(Boolean).join(" ");
6
+ const RadioButton = ({
7
+ value,
8
+ disabled,
9
+ children,
10
+ className,
11
+ checked,
12
+ onChange,
13
+ onClick,
14
+ style,
15
+ // forward data-*/aria-* (e.g. data-testid) to the input, as antd's rc-checkbox did
16
+ ...rest
17
+ }) => {
18
+ const group = useContext(RadioGroupContext);
19
+ const isChecked = group ? group.value === value : Boolean(checked);
20
+ const isDisabled = Boolean(disabled ?? group?.disabled);
21
+ const handleChange = (event) => {
22
+ if (isDisabled) {
23
+ return;
24
+ }
25
+ if (group) {
26
+ group.onChange(value, event.nativeEvent);
27
+ }
28
+ if (onChange) {
29
+ const changeEvent = {
30
+ target: {
31
+ value,
32
+ checked: true,
33
+ name: group?.name
34
+ },
35
+ stopPropagation: () => event.stopPropagation(),
36
+ preventDefault: () => event.preventDefault(),
37
+ nativeEvent: event.nativeEvent
38
+ };
39
+ onChange(changeEvent);
40
+ }
41
+ };
42
+ return /* @__PURE__ */ jsxs(RadioButtonLabel, { className: cx("ant-radio-button-wrapper", "ds-radio-button", isChecked && "ant-radio-button-wrapper-checked", isDisabled && "ant-radio-button-wrapper-disabled", className), style, onClick, $checked: isChecked, $disabled: isDisabled, $solid: group?.buttonStyle === "solid", $size: group?.size, children: [
43
+ /* @__PURE__ */ jsx("span", { className: cx("ant-radio-button", isChecked && "ant-radio-button-checked", isDisabled && "ant-radio-button-disabled"), children: /* @__PURE__ */ jsx(RadioButtonInput, { type: "radio", className: "ant-radio-button-input ds-radio-button-input", checked: isChecked, disabled: isDisabled, name: group?.name, value: value === void 0 ? void 0 : String(value), onChange: handleChange, ...rest }) }),
44
+ /* @__PURE__ */ jsx("span", { children })
45
+ ] });
46
+ };
47
+ export {
48
+ RadioButton
49
+ };
@@ -0,0 +1,2 @@
1
+ export { RadioBase } from './RadioBase';
2
+ export { RadioButton } from './RadioButton';
@@ -0,0 +1,6 @@
1
+ import { RadioBase } from "./RadioBase.js";
2
+ import { RadioButton } from "./RadioButton.js";
3
+ export {
4
+ RadioBase,
5
+ RadioButton
6
+ };
package/dist/index.d.ts CHANGED
@@ -1,2 +1,2 @@
1
1
  export { default } from './Radio';
2
- export type { RadioProps, RadioGroupProps } from './Radio.types';
2
+ export type { RadioProps, RadioGroupProps, RadioButtonProps, RadioChangeEvent, RadioValueType, Props, } from './Radio.types';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@synerise/ds-radio",
3
- "version": "1.1.16",
3
+ "version": "1.1.18",
4
4
  "description": "Radio UI Component for the Synerise Design System",
5
5
  "license": "ISC",
6
6
  "repository": "synerise/synerise-design",
@@ -17,6 +17,7 @@
17
17
  "files": [
18
18
  "/dist",
19
19
  "CHANGELOG.md",
20
+ "CLAUDE.md",
20
21
  "README.md",
21
22
  "package.json",
22
23
  "LICENSE.md"
@@ -41,15 +42,15 @@
41
42
  ],
42
43
  "types": "dist/index.d.ts",
43
44
  "dependencies": {
44
- "@synerise/ds-form-field": "^1.3.23",
45
- "@synerise/ds-typography": "^1.1.26"
45
+ "@synerise/ds-form-field": "^1.3.24",
46
+ "@synerise/ds-typography": "^1.1.27",
47
+ "@synerise/ds-utils": "^1.10.2"
46
48
  },
47
49
  "peerDependencies": {
48
50
  "@synerise/ds-core": "*",
49
- "antd": "4.24.16",
50
51
  "react": ">=16.9.0 <= 18.3.1",
51
52
  "styled-components": "^5.3.3",
52
53
  "vitest": "4"
53
54
  },
54
- "gitHead": "d8c64070f58f14e3fb1bfbcbf00d1e3b8fd51eb8"
55
+ "gitHead": "d0a43cc43d8528a36f105aceea52ab470edb71d9"
55
56
  }
File without changes
@@ -1,3 +0,0 @@
1
- .ant-radio-group{box-sizing:border-box;margin:0;padding:0;color:#6a7580;font-size:13px;font-variant:tabular-nums;line-height:1.38;list-style:none;font-feature-settings:'tnum';display:inline-block;font-size:0}.ant-radio-group .ant-badge-count{z-index:1}.ant-radio-group>.ant-badge:not(:first-child)>.ant-radio-button-wrapper{border-left:none}.ant-radio-wrapper{box-sizing:border-box;margin:0;padding:0;color:#6a7580;font-size:13px;font-variant:tabular-nums;line-height:1.38;list-style:none;font-feature-settings:'tnum';position:relative;display:inline-flex;align-items:baseline;margin-right:8px;cursor:pointer}.ant-radio-wrapper-disabled{cursor:not-allowed}.ant-radio-wrapper::after{display:inline-block;width:0;overflow:hidden;content:'\a0'}.ant-radio-wrapper.ant-radio-wrapper-in-form-item input[type=radio]{width:14px;height:14px}.ant-radio{box-sizing:border-box;margin:0;padding:0;color:#6a7580;font-size:13px;font-variant:tabular-nums;line-height:1.38;list-style:none;font-feature-settings:'tnum';position:relative;top:.2em;display:inline-block;outline:0;cursor:pointer}.ant-radio-input:focus+.ant-radio-inner,.ant-radio-wrapper:hover .ant-radio,.ant-radio:hover .ant-radio-inner{border-color:#0b68ff}.ant-radio-input:focus+.ant-radio-inner{box-shadow:0 0 0 3px #85b4ff}.ant-radio-checked::after{position:absolute;top:0;left:0;width:100%;height:100%;border:1px solid #0b68ff;border-radius:50%;visibility:hidden;animation:antRadioEffect .36s ease-in-out;animation-fill-mode:both;content:''}.ant-radio-wrapper:hover .ant-radio::after,.ant-radio:hover::after{visibility:visible}.ant-radio-inner{position:relative;top:0;left:0;display:block;width:16px;height:16px;background-color:#fff;border-color:#dbe0e3;border-style:solid;border-width:1px;border-radius:50%;transition:all .3s}.ant-radio-inner::after{position:absolute;top:50%;left:50%;display:block;width:16px;height:16px;margin-top:-8px;margin-left:-8px;background-color:#0b68ff;border-top:0;border-left:0;border-radius:16px;transform:scale(0);opacity:0;transition:all .3s cubic-bezier(.78, .14, .15, .86);content:' '}.ant-radio-input{position:absolute;top:0;right:0;bottom:0;left:0;z-index:1;cursor:pointer;opacity:0}.ant-radio.ant-radio-disabled .ant-radio-inner{border-color:#dbe0e3}.ant-radio-checked .ant-radio-inner{border-color:#0b68ff}.ant-radio-checked .ant-radio-inner::after{transform:scale(.5);opacity:1;transition:all .3s cubic-bezier(.78, .14, .15, .86)}.ant-radio-disabled{cursor:not-allowed}.ant-radio-disabled .ant-radio-inner{background-color:#f9fafb;cursor:not-allowed}.ant-radio-disabled .ant-radio-inner::after{background-color:rgba(0,0,0,.2)}.ant-radio-disabled .ant-radio-input{cursor:not-allowed}.ant-radio-disabled+span{color:#b5bdc3;cursor:not-allowed}span.ant-radio+*{padding-right:8px;padding-left:8px}.ant-radio-button-wrapper{position:relative;display:inline-block;height:32px;margin:0;padding:0 15px;color:#6a7580;font-size:13px;line-height:30px;background:#fff;border:1px solid #dbe0e3;border-top-width:1.02px;border-left-width:0;cursor:pointer;transition:color .3s,background .3s,border-color .3s,box-shadow .3s}.ant-radio-button-wrapper a{color:#6a7580}.ant-radio-button-wrapper>.ant-radio-button{position:absolute;top:0;left:0;z-index:-1;width:100%;height:100%}.ant-radio-group-large .ant-radio-button-wrapper{height:48px;font-size:13px;line-height:46px}.ant-radio-group-small .ant-radio-button-wrapper{height:24px;padding:0 7px;line-height:22px}.ant-radio-button-wrapper:not(:first-child)::before{position:absolute;top:-1px;left:-1px;display:block;box-sizing:content-box;width:1px;height:100%;padding:1px 0;background-color:#dbe0e3;transition:background-color .3s;content:''}.ant-radio-button-wrapper:first-child{border-left:1px solid #dbe0e3;border-radius:3px 0 0 3px}.ant-radio-button-wrapper:last-child{border-radius:0 3px 3px 0}.ant-radio-button-wrapper:first-child:last-child{border-radius:3px}.ant-radio-button-wrapper:hover{position:relative;color:#0b68ff}.ant-radio-button-wrapper:focus-within{box-shadow:0 0 0 3px #85b4ff}.ant-radio-button-wrapper .ant-radio-inner,.ant-radio-button-wrapper input[type=checkbox],.ant-radio-button-wrapper input[type=radio]{width:0;height:0;opacity:0;pointer-events:none}.ant-radio-button-wrapper-checked:not(.ant-radio-button-wrapper-disabled){z-index:1;color:#0b68ff;background:#f9fafb;border-color:#0b68ff}.ant-radio-button-wrapper-checked:not(.ant-radio-button-wrapper-disabled)::before{background-color:#0b68ff}.ant-radio-button-wrapper-checked:not(.ant-radio-button-wrapper-disabled):first-child{border-color:#0b68ff}.ant-radio-button-wrapper-checked:not(.ant-radio-button-wrapper-disabled):hover{color:#38f;border-color:#38f}.ant-radio-button-wrapper-checked:not(.ant-radio-button-wrapper-disabled):hover::before{background-color:#38f}.ant-radio-button-wrapper-checked:not(.ant-radio-button-wrapper-disabled):active{color:#004cd9;border-color:#004cd9}.ant-radio-button-wrapper-checked:not(.ant-radio-button-wrapper-disabled):active::before{background-color:#004cd9}.ant-radio-button-wrapper-checked:not(.ant-radio-button-wrapper-disabled):focus-within{box-shadow:0 0 0 3px #85b4ff}.ant-radio-group-solid .ant-radio-button-wrapper-checked:not(.ant-radio-button-wrapper-disabled){color:#fff;background:#0b68ff;border-color:#0b68ff}.ant-radio-group-solid .ant-radio-button-wrapper-checked:not(.ant-radio-button-wrapper-disabled):hover{color:#fff;background:#38f;border-color:#38f}.ant-radio-group-solid .ant-radio-button-wrapper-checked:not(.ant-radio-button-wrapper-disabled):active{color:#fff;background:#004cd9;border-color:#004cd9}.ant-radio-group-solid .ant-radio-button-wrapper-checked:not(.ant-radio-button-wrapper-disabled):focus-within{box-shadow:0 0 0 3px #85b4ff}.ant-radio-button-wrapper-disabled{color:#b5bdc3;background-color:#f9fafb;border-color:#dbe0e3;cursor:not-allowed}.ant-radio-button-wrapper-disabled:first-child,.ant-radio-button-wrapper-disabled:hover{color:#b5bdc3;background-color:#f9fafb;border-color:#dbe0e3}.ant-radio-button-wrapper-disabled:first-child{border-left-color:#dbe0e3}.ant-radio-button-wrapper-disabled.ant-radio-button-wrapper-checked{color:#b5bdc3;background-color:#e6e6e6;border-color:#dbe0e3;box-shadow:none}@keyframes antRadioEffect{0%{transform:scale(1);opacity:.5}100%{transform:scale(1.6);opacity:0}}.ant-radio-group.ant-radio-group-rtl{direction:rtl}.ant-radio-wrapper.ant-radio-wrapper-rtl{margin-right:0;margin-left:8px;direction:rtl}.ant-radio-button-wrapper.ant-radio-button-wrapper-rtl{border-right-width:0;border-left-width:1px}.ant-radio-button-wrapper.ant-radio-button-wrapper-rtl.ant-radio-button-wrapper:not(:first-child)::before{right:-1px;left:0}.ant-radio-button-wrapper.ant-radio-button-wrapper-rtl.ant-radio-button-wrapper:first-child{border-right:1px solid #dbe0e3;border-radius:0 3px 3px 0}.ant-radio-button-wrapper-checked:not([class*=' ant-radio-button-wrapper-disabled']).ant-radio-button-wrapper:first-child{border-right-color:#38f}.ant-radio-button-wrapper.ant-radio-button-wrapper-rtl.ant-radio-button-wrapper:last-child{border-radius:3px 0 0 3px}.ant-radio-button-wrapper.ant-radio-button-wrapper-rtl.ant-radio-button-wrapper-disabled:first-child{border-right-color:#dbe0e3}.ant-radio-wrapper{display:flex;white-space:normal}.ant-radio+span{padding:0;color:#57616d;font-weight:500;margin:0 12px 0 12px;position:relative}.ant-radio-checked+span{color:#384350}.ant-radio-wrapper-disabled .ant-radio+span{color:#6a7580;opacity:.4}.ant-radio-wrapper{align-items:unset}.ant-radio-wrapper::after{display:none}.ant-radio-input:hover+.ant-radio-inner{border-color:#b5bdc3}.ant-radio-input:focus+.ant-radio-inner{border-color:#0b68ff;background-color:#f4faff;border-width:2px}.ant-radio-input:focus+.ant-radio-inner{box-shadow:none}.ant-radio-checked .ant-radio-inner{border-color:#0b68ff;border-width:2px}.ant-radio-button-wrapper{background-color:#f9fafb;color:#57616d}.ant-radio-button-wrapper:hover{background-color:#fff;color:#57616d}.ant-radio-button-wrapper:focus{border:2px solid #0b68ff;z-index:2;margin-left:-1px;top:1px}.ant-radio-button-wrapper:focus span+span{top:-2px}.ant-radio-button-wrapper:not(:focus){top:0}.ant-radio-button-wrapper:not(:first-child)::before{display:none}.ant-radio-button-wrapper:not(:first-child):not(:focus)::before{position:absolute;top:0;left:-1px;display:block;width:1px;height:100%;background-color:#dbe0e3;content:''}.ant-radio-button-wrapper-checked:not( .ant-radio-button-wrapper-disabled){box-shadow:-1px 0 0 0 #0b68ff}.ant-radio-button-wrapper span+span{position:relative;top:-1px}.ant-radio-button-wrapper-disabled{color:#57616d;opacity:.4}.ant-radio-group-solid .ant-radio-button-wrapper-checked:not(
2
- .ant-radio-button-wrapper-disabled
3
- ):hover{color:#fff;background:#238afe;border-color:#238afe;box-shadow:-1px 0 0 0 #238afe}