@negative-space/radio 1.3.0 → 1.4.0

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/dist/index.d.mts CHANGED
@@ -1,10 +1,7 @@
1
1
  import React from 'react';
2
2
  import { FlexProps } from '@negative-space/flex';
3
- import * as react_jsx_runtime from 'react/jsx-runtime';
4
3
 
5
- interface RadioProps extends Omit<FlexProps<'label'>, 'className' | 'style'> {
6
- disabled?: boolean;
7
- value: string | number;
4
+ interface RadioOptionProps extends Omit<FlexProps<'label'>, 'as' | 'className' | 'style' | 'onClick'> {
8
5
  classNames?: {
9
6
  label?: string;
10
7
  radio?: string;
@@ -15,16 +12,35 @@ interface RadioProps extends Omit<FlexProps<'label'>, 'className' | 'style'> {
15
12
  radio?: React.CSSProperties;
16
13
  inner?: React.CSSProperties;
17
14
  };
15
+ disabled?: boolean;
16
+ value: string;
17
+ label: React.ReactNode;
18
18
  isPopDisabled?: boolean;
19
19
  }
20
- declare const Radio: React.ForwardRefExoticComponent<RadioProps & React.RefAttributes<HTMLDivElement>>;
21
20
 
22
- interface RadioGroupProps extends Omit<FlexProps, 'onChange'> {
21
+ interface RadioGroupProps extends Omit<FlexProps, 'children' | 'onChange'> {
22
+ classNames?: {
23
+ root?: string;
24
+ option?: {
25
+ label?: string;
26
+ radio?: string;
27
+ inner?: string;
28
+ };
29
+ };
30
+ styles?: {
31
+ root?: React.CSSProperties;
32
+ option?: {
33
+ label?: React.CSSProperties;
34
+ radio?: React.CSSProperties;
35
+ inner?: React.CSSProperties;
36
+ };
37
+ };
23
38
  name?: string;
24
39
  disabled?: boolean;
25
- value?: string | number;
26
- onChange?: (value: string | number) => void;
40
+ options?: RadioOptionProps[];
41
+ defaultValue?: string;
42
+ onValueChange?: (value: string) => void;
27
43
  }
28
- declare const RadioGroup: ({ direction, className, value: valueProp, onChange, name: nameProp, disabled, children, ...props }: RadioGroupProps) => react_jsx_runtime.JSX.Element;
44
+ declare const RadioGroup: React.ForwardRefExoticComponent<RadioGroupProps & React.RefAttributes<HTMLDivElement>>;
29
45
 
30
- export { Radio, RadioGroup, type RadioGroupProps, type RadioProps };
46
+ export { RadioGroup, type RadioGroupProps };
package/dist/index.d.ts CHANGED
@@ -1,10 +1,7 @@
1
1
  import React from 'react';
2
2
  import { FlexProps } from '@negative-space/flex';
3
- import * as react_jsx_runtime from 'react/jsx-runtime';
4
3
 
5
- interface RadioProps extends Omit<FlexProps<'label'>, 'className' | 'style'> {
6
- disabled?: boolean;
7
- value: string | number;
4
+ interface RadioOptionProps extends Omit<FlexProps<'label'>, 'as' | 'className' | 'style' | 'onClick'> {
8
5
  classNames?: {
9
6
  label?: string;
10
7
  radio?: string;
@@ -15,16 +12,35 @@ interface RadioProps extends Omit<FlexProps<'label'>, 'className' | 'style'> {
15
12
  radio?: React.CSSProperties;
16
13
  inner?: React.CSSProperties;
17
14
  };
15
+ disabled?: boolean;
16
+ value: string;
17
+ label: React.ReactNode;
18
18
  isPopDisabled?: boolean;
19
19
  }
20
- declare const Radio: React.ForwardRefExoticComponent<RadioProps & React.RefAttributes<HTMLDivElement>>;
21
20
 
22
- interface RadioGroupProps extends Omit<FlexProps, 'onChange'> {
21
+ interface RadioGroupProps extends Omit<FlexProps, 'children' | 'onChange'> {
22
+ classNames?: {
23
+ root?: string;
24
+ option?: {
25
+ label?: string;
26
+ radio?: string;
27
+ inner?: string;
28
+ };
29
+ };
30
+ styles?: {
31
+ root?: React.CSSProperties;
32
+ option?: {
33
+ label?: React.CSSProperties;
34
+ radio?: React.CSSProperties;
35
+ inner?: React.CSSProperties;
36
+ };
37
+ };
23
38
  name?: string;
24
39
  disabled?: boolean;
25
- value?: string | number;
26
- onChange?: (value: string | number) => void;
40
+ options?: RadioOptionProps[];
41
+ defaultValue?: string;
42
+ onValueChange?: (value: string) => void;
27
43
  }
28
- declare const RadioGroup: ({ direction, className, value: valueProp, onChange, name: nameProp, disabled, children, ...props }: RadioGroupProps) => react_jsx_runtime.JSX.Element;
44
+ declare const RadioGroup: React.ForwardRefExoticComponent<RadioGroupProps & React.RefAttributes<HTMLDivElement>>;
29
45
 
30
- export { Radio, RadioGroup, type RadioGroupProps, type RadioProps };
46
+ export { RadioGroup, type RadioGroupProps };
package/dist/index.js CHANGED
@@ -1,64 +1,51 @@
1
1
  'use strict';
2
2
 
3
- var React3 = require('react');
3
+ var React = require('react');
4
4
  var system = require('@negative-space/system');
5
5
  var flex = require('@negative-space/flex');
6
+ var rovingFocus = require('@negative-space/roving-focus');
6
7
  var jsxRuntime = require('react/jsx-runtime');
7
8
 
8
9
  function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; }
9
10
 
10
- var React3__default = /*#__PURE__*/_interopDefault(React3);
11
+ var React__default = /*#__PURE__*/_interopDefault(React);
11
12
 
12
- // src/Radio.tsx
13
- var RadioContext = React3__default.default.createContext(null);
14
- var RadioProvider = ({ children, ...value }) => {
15
- return /* @__PURE__ */ jsxRuntime.jsx(RadioContext.Provider, { value, children });
16
- };
13
+ // src/RadioGroup.tsx
14
+ var RadioContext = React.createContext(null);
17
15
 
18
16
  // src/useRadioContext.ts
19
17
  var useRadioContext = () => {
20
- const ctx = React3.useContext(RadioContext);
18
+ const ctx = React.useContext(RadioContext);
21
19
  if (!ctx) {
22
20
  throw new Error("Radio must be used within RadioGroup");
23
21
  }
24
22
  return ctx;
25
23
  };
26
- var Radio = React3__default.default.forwardRef(
27
- ({
28
- classNames,
29
- alignItems = "center",
30
- styles,
31
- value,
32
- children,
33
- disabled,
34
- isPopDisabled,
35
- ...props
36
- }, ref) => {
37
- const { global, components } = system.useNSUI();
38
- const { selectedValue, onChange, disabled: groupDisabled } = useRadioContext();
39
- const Disabled = disabled ?? groupDisabled;
24
+ var RadioOption = React__default.default.forwardRef(
25
+ ({ value, label, disabled, classNames, styles, ...props }) => {
26
+ const { global } = system.useNSUI();
27
+ const { selectedValue, onChange, disabled: groupDisabled, roving } = useRadioContext();
28
+ const ref = React.useRef(null);
29
+ const isDisabled = disabled ?? groupDisabled;
40
30
  const checked = selectedValue === value;
41
- const IsPopDisabled = isPopDisabled ?? components.radio.isPopDisabled;
42
- const handleClick = () => {
43
- if (!Disabled) onChange?.(value);
44
- };
45
- const handleKeyDown = (e) => {
46
- if (Disabled) return;
47
- if (e.key === "Enter" || e.key === " ") {
48
- e.preventDefault();
49
- onChange?.(value);
50
- }
51
- };
31
+ React.useEffect(() => {
32
+ roving.registerItem({
33
+ id: value,
34
+ ref,
35
+ disabled: isDisabled
36
+ });
37
+ return () => roving.unregisterItem(value);
38
+ }, [value, isDisabled, roving]);
52
39
  return /* @__PURE__ */ jsxRuntime.jsxs(
53
40
  flex.Flex,
54
41
  {
55
42
  ...props,
56
43
  as: "label",
57
- alignItems,
58
- "aria-disabled": Disabled,
59
- "data-disabled": Disabled,
60
- onClick: handleClick,
61
- className: system.cn(`${global.prefixCls}-radio-label`, classNames?.label),
44
+ alignItems: "center",
45
+ className: system.cn(
46
+ `${global.prefixCls}-radio-label ${global.prefixCls}-clickable`,
47
+ classNames?.label
48
+ ),
62
49
  style: styles?.label,
63
50
  children: [
64
51
  /* @__PURE__ */ jsxRuntime.jsx(
@@ -68,76 +55,110 @@ var Radio = React3__default.default.forwardRef(
68
55
  role: "radio",
69
56
  "aria-checked": checked,
70
57
  "data-checked": checked,
71
- tabIndex: Disabled ? -1 : 0,
72
- onKeyDown: handleKeyDown,
73
- "data-disabled": Disabled,
58
+ tabIndex: roving.hasInteracted && roving.activeId === value ? 0 : -1,
59
+ "data-disabled": isDisabled,
60
+ onClick: () => {
61
+ if (!isDisabled) {
62
+ roving.focusItem(value);
63
+ onChange?.(value);
64
+ }
65
+ },
66
+ onKeyDown: (e) => roving.handleItemKeyDown(e, value, onChange),
74
67
  className: system.cn(`${global.prefixCls}-radio`, classNames?.radio),
75
- style: { ...styles?.radio },
68
+ style: {
69
+ borderRadius: "50%",
70
+ display: "flex",
71
+ alignItems: "center",
72
+ justifyContent: "center",
73
+ overflow: "hidden",
74
+ ...styles?.radio
75
+ },
76
76
  children: /* @__PURE__ */ jsxRuntime.jsx(
77
77
  "div",
78
78
  {
79
+ "data-visible": checked,
79
80
  className: system.cn(
80
- `${global.prefixCls}-radio-inner`,
81
- checked && !IsPopDisabled && `${global.prefixCls}-pop`,
81
+ `${global.prefixCls}-radio-inner ${global.prefixCls}-fade`,
82
82
  classNames?.inner
83
83
  ),
84
- style: { ...styles?.inner }
84
+ style: {
85
+ borderRadius: "50%",
86
+ ...styles?.inner
87
+ }
85
88
  }
86
89
  )
87
90
  }
88
91
  ),
89
- children
92
+ label
90
93
  ]
91
94
  }
92
95
  );
93
96
  }
94
97
  );
95
- Radio.displayName = "Radio";
96
- var RadioGroup = ({
97
- direction = "column",
98
- className,
99
- value: valueProp,
100
- onChange,
101
- name: nameProp,
102
- disabled = false,
103
- children,
104
- ...props
105
- }) => {
106
- const { global } = system.useNSUI();
107
- const autoName = React3.useId();
108
- const name = nameProp ?? autoName;
109
- const [internalValue, setInternalValue] = React3.useState(valueProp);
110
- const value = valueProp !== void 0 ? valueProp : internalValue;
111
- const handleChange = (newValue) => {
112
- if (valueProp === void 0) setInternalValue(newValue);
113
- onChange?.(newValue);
114
- };
115
- if (process.env.NODE_ENV !== "production") {
116
- const countRadios = (nodes) => {
117
- return React3__default.default.Children.toArray(nodes).reduce((acc, child) => {
118
- if (!React3__default.default.isValidElement(child)) return acc;
119
- if (child.type === Radio) return acc + 1;
120
- const childChildren = child.props?.children;
121
- return acc + countRadios(childChildren);
122
- }, 0);
123
- };
124
- const radiosCount = countRadios(children);
125
- if (radiosCount === 1) {
126
- console.warn("RadioGroup has only one Radio. Consider using multiple options for clarity.");
127
- }
98
+ RadioOption.displayName = "RadioOption";
99
+ var RadioGroup = React__default.default.forwardRef(
100
+ ({
101
+ classNames,
102
+ styles,
103
+ disabled = false,
104
+ options,
105
+ direction = "column",
106
+ name: nameProp,
107
+ defaultValue,
108
+ onValueChange,
109
+ ...props
110
+ }, ref) => {
111
+ const { global } = system.useNSUI();
112
+ const autoName = React.useId();
113
+ const name = nameProp ?? autoName;
114
+ const [selectedValue, setSelectedValue] = React.useState(defaultValue);
115
+ const roving = rovingFocus.useRovingFocus();
116
+ const handleChange = React.useCallback(
117
+ (value) => {
118
+ setSelectedValue(value);
119
+ roving.setActiveId(value);
120
+ onValueChange?.(value);
121
+ },
122
+ [onValueChange, roving]
123
+ );
124
+ const contextValue = React.useMemo(
125
+ () => ({
126
+ name,
127
+ disabled,
128
+ selectedValue,
129
+ onChange: handleChange,
130
+ roving
131
+ }),
132
+ [name, disabled, selectedValue, handleChange, roving]
133
+ );
134
+ return /* @__PURE__ */ jsxRuntime.jsx(RadioContext.Provider, { value: contextValue, children: /* @__PURE__ */ jsxRuntime.jsx(
135
+ flex.Flex,
136
+ {
137
+ ...props,
138
+ as: "fieldset",
139
+ ref,
140
+ direction,
141
+ role: "radiogroup",
142
+ tabIndex: roving.hasInteracted ? -1 : 0,
143
+ onKeyDown: roving.handleGroupKeyDown,
144
+ onBlur: roving.handleGroupBlur,
145
+ className: system.cn(`${global.prefixCls}-radio-group`, classNames?.root),
146
+ style: styles?.root,
147
+ children: options?.map((option, index) => /* @__PURE__ */ jsxRuntime.jsx(
148
+ RadioOption,
149
+ {
150
+ ...option,
151
+ classNames: system.mergeClassNames(classNames?.option, option.classNames),
152
+ styles: { ...styles?.option, ...option.styles }
153
+ },
154
+ option.value ?? index
155
+ ))
156
+ }
157
+ ) });
128
158
  }
129
- return /* @__PURE__ */ jsxRuntime.jsx(RadioProvider, { name, selectedValue: value, onChange: handleChange, disabled, children: /* @__PURE__ */ jsxRuntime.jsx(
130
- flex.Flex,
131
- {
132
- ...props,
133
- direction,
134
- className: system.cn(`${global.prefixCls}-radio-group`, className),
135
- children
136
- }
137
- ) });
138
- };
159
+ );
160
+ RadioGroup.displayName = "RadioGroup";
139
161
 
140
- exports.Radio = Radio;
141
162
  exports.RadioGroup = RadioGroup;
142
163
  //# sourceMappingURL=index.js.map
143
164
  //# sourceMappingURL=index.js.map
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/RadioProvider.tsx","../src/useRadioContext.ts","../src/Radio.tsx","../src/RadioGroup.tsx"],"names":["React","jsx","useContext","useNSUI","jsxs","Flex","cn","useId","useState"],"mappings":"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","file":"index.js","sourcesContent":["import React from 'react'\n\nexport type RadioContextValue = {\n name: string\n disabled?: boolean\n selectedValue?: string | number\n onChange?: (value: string | number) => void\n}\n\nexport const RadioContext = React.createContext<RadioContextValue | null>(null)\n\nexport interface RadioProviderProps extends RadioContextValue {\n children: React.ReactNode\n}\n\nexport const RadioProvider = ({ children, ...value }: RadioProviderProps) => {\n return <RadioContext.Provider value={value}>{children}</RadioContext.Provider>\n}\n","import { useContext } from 'react'\nimport { RadioContext } from './RadioProvider'\n\nexport const useRadioContext = () => {\n const ctx = useContext(RadioContext)\n\n if (!ctx) {\n throw new Error('Radio must be used within RadioGroup')\n }\n\n return ctx\n}\n","import React from 'react'\nimport { cn, useNSUI } from '@negative-space/system'\nimport { Flex, type FlexProps } from '@negative-space/flex'\nimport { useRadioContext } from './useRadioContext'\n\nexport interface RadioProps extends Omit<FlexProps<'label'>, 'className' | 'style'> {\n disabled?: boolean\n value: string | number\n classNames?: {\n label?: string\n radio?: string\n inner?: string\n }\n styles?: {\n label?: React.CSSProperties\n radio?: React.CSSProperties\n inner?: React.CSSProperties\n }\n isPopDisabled?: boolean\n}\n\nexport const Radio = React.forwardRef<HTMLDivElement, RadioProps>(\n (\n {\n classNames,\n alignItems = 'center',\n styles,\n value,\n children,\n disabled,\n isPopDisabled,\n ...props\n },\n ref\n ) => {\n const { global, components } = useNSUI()\n const { selectedValue, onChange, disabled: groupDisabled } = useRadioContext()\n const Disabled = disabled ?? groupDisabled\n const checked = selectedValue === value\n const IsPopDisabled = isPopDisabled ?? components.radio.isPopDisabled\n\n const handleClick = () => {\n if (!Disabled) onChange?.(value)\n }\n\n const handleKeyDown = (e: React.KeyboardEvent) => {\n if (Disabled) return\n if (e.key === 'Enter' || e.key === ' ') {\n e.preventDefault()\n onChange?.(value)\n }\n }\n\n return (\n <Flex\n {...props}\n as=\"label\"\n alignItems={alignItems}\n aria-disabled={Disabled}\n data-disabled={Disabled}\n onClick={handleClick}\n className={cn(`${global.prefixCls}-radio-label`, classNames?.label)}\n style={styles?.label}\n >\n <div\n ref={ref}\n role=\"radio\"\n aria-checked={checked}\n data-checked={checked}\n tabIndex={Disabled ? -1 : 0}\n onKeyDown={handleKeyDown}\n data-disabled={Disabled}\n className={cn(`${global.prefixCls}-radio`, classNames?.radio)}\n style={{ ...styles?.radio }}\n >\n <div\n className={cn(\n `${global.prefixCls}-radio-inner`,\n checked && !IsPopDisabled && `${global.prefixCls}-pop`,\n classNames?.inner\n )}\n style={{ ...styles?.inner }}\n />\n </div>\n {children}\n </Flex>\n )\n }\n)\n\nRadio.displayName = 'Radio'\n","import React, { useState, useId } from 'react'\nimport { cn, useNSUI } from '@negative-space/system'\nimport { Flex, FlexProps } from '@negative-space/flex'\nimport { RadioProvider } from './RadioProvider'\nimport { Radio } from './Radio'\n\nexport interface RadioGroupProps extends Omit<FlexProps, 'onChange'> {\n name?: string\n disabled?: boolean\n value?: string | number\n onChange?: (value: string | number) => void\n}\n\nexport const RadioGroup = ({\n direction = 'column',\n className,\n value: valueProp,\n onChange,\n name: nameProp,\n disabled = false,\n children,\n ...props\n}: RadioGroupProps) => {\n const { global } = useNSUI()\n\n const autoName = useId()\n const name = nameProp ?? autoName\n\n const [internalValue, setInternalValue] = useState<string | number | undefined>(valueProp)\n const value = valueProp !== undefined ? valueProp : internalValue\n\n const handleChange = (newValue: string | number) => {\n if (valueProp === undefined) setInternalValue(newValue)\n onChange?.(newValue)\n }\n\n if (process.env.NODE_ENV !== 'production') {\n const countRadios = (nodes: React.ReactNode): number => {\n return React.Children.toArray(nodes).reduce((acc: number, child) => {\n if (!React.isValidElement(child)) return acc\n if (child.type === Radio) return acc + 1\n const childChildren = (child.props as { children?: React.ReactNode })?.children\n return acc + countRadios(childChildren)\n }, 0)\n }\n\n const radiosCount = countRadios(children)\n if (radiosCount === 1) {\n console.warn('RadioGroup has only one Radio. Consider using multiple options for clarity.')\n }\n }\n\n return (\n <RadioProvider name={name} selectedValue={value} onChange={handleChange} disabled={disabled}>\n <Flex\n {...props}\n direction={direction}\n className={cn(`${global.prefixCls}-radio-group`, className)}\n >\n {children}\n </Flex>\n </RadioProvider>\n )\n}\n"]}
1
+ {"version":3,"sources":["../src/RadioContext.ts","../src/useRadioContext.ts","../src/RadioOption.tsx","../src/RadioGroup.tsx"],"names":["createContext","useContext","React","useNSUI","useRef","useEffect","jsxs","Flex","cn","jsx","useId","useState","useRovingFocus","useCallback","useMemo","mergeClassNames"],"mappings":"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","file":"index.js","sourcesContent":["import { createContext } from 'react'\nimport type { useRovingFocus } from '@negative-space/roving-focus'\n\nexport interface RadioContextValue {\n name: string\n disabled?: boolean\n selectedValue?: string\n onChange?: (value: string) => void\n roving: ReturnType<typeof useRovingFocus>\n}\n\nexport const RadioContext = createContext<RadioContextValue | null>(null)\n","import { useContext } from 'react'\nimport { RadioContext, type RadioContextValue } from './RadioContext'\n\nexport const useRadioContext = (): RadioContextValue => {\n const ctx = useContext(RadioContext)\n\n if (!ctx) {\n throw new Error('Radio must be used within RadioGroup')\n }\n\n return ctx\n}\n","import React, { useEffect, useRef } from 'react'\nimport { cn, useNSUI } from '@negative-space/system'\nimport { Flex, type FlexProps } from '@negative-space/flex'\nimport { useRadioContext } from './useRadioContext'\n\nexport interface RadioOptionProps extends Omit<\n FlexProps<'label'>,\n 'as' | 'className' | 'style' | 'onClick'\n> {\n classNames?: {\n label?: string\n radio?: string\n inner?: string\n }\n styles?: {\n label?: React.CSSProperties\n radio?: React.CSSProperties\n inner?: React.CSSProperties\n }\n disabled?: boolean\n value: string\n label: React.ReactNode\n isPopDisabled?: boolean\n}\n\nexport const RadioOption = React.forwardRef<HTMLDivElement, RadioOptionProps>(\n ({ value, label, disabled, classNames, styles, ...props }) => {\n const { global } = useNSUI()\n const { selectedValue, onChange, disabled: groupDisabled, roving } = useRadioContext()\n\n const ref = useRef<HTMLDivElement>(null)\n const isDisabled = disabled ?? groupDisabled\n const checked = selectedValue === value\n\n useEffect(() => {\n roving.registerItem({\n id: value,\n ref: ref as React.RefObject<HTMLElement>,\n disabled: isDisabled\n })\n return () => roving.unregisterItem(value)\n }, [value, isDisabled, roving])\n\n return (\n <Flex\n {...props}\n as=\"label\"\n alignItems=\"center\"\n className={cn(\n `${global.prefixCls}-radio-label ${global.prefixCls}-clickable`,\n classNames?.label\n )}\n style={styles?.label}\n >\n <div\n ref={ref}\n role=\"radio\"\n aria-checked={checked}\n data-checked={checked}\n tabIndex={roving.hasInteracted && roving.activeId === value ? 0 : -1}\n data-disabled={isDisabled}\n onClick={() => {\n if (!isDisabled) {\n roving.focusItem(value)\n onChange?.(value)\n }\n }}\n onKeyDown={(e) => roving.handleItemKeyDown(e, value, onChange)}\n className={cn(`${global.prefixCls}-radio`, classNames?.radio)}\n style={{\n borderRadius: '50%',\n display: 'flex',\n alignItems: 'center',\n justifyContent: 'center',\n overflow: 'hidden',\n ...styles?.radio\n }}\n >\n <div\n data-visible={checked}\n className={cn(\n `${global.prefixCls}-radio-inner ${global.prefixCls}-fade`,\n classNames?.inner\n )}\n style={{\n borderRadius: '50%',\n ...styles?.inner\n }}\n />\n </div>\n {label}\n </Flex>\n )\n }\n)\n\nRadioOption.displayName = 'RadioOption'\n","import React, { useCallback, useMemo, useState, useId } from 'react'\nimport { cn, mergeClassNames, useNSUI } from '@negative-space/system'\nimport { Flex, type FlexProps } from '@negative-space/flex'\nimport { useRovingFocus } from '@negative-space/roving-focus'\nimport { RadioOption, type RadioOptionProps } from './RadioOption'\nimport { RadioContext } from './RadioContext'\n\nexport interface RadioGroupProps extends Omit<FlexProps, 'children' | 'onChange'> {\n classNames?: {\n root?: string\n option?: {\n label?: string\n radio?: string\n inner?: string\n }\n }\n styles?: {\n root?: React.CSSProperties\n option?: {\n label?: React.CSSProperties\n radio?: React.CSSProperties\n inner?: React.CSSProperties\n }\n }\n name?: string\n disabled?: boolean\n options?: RadioOptionProps[]\n defaultValue?: string\n onValueChange?: (value: string) => void\n}\n\nexport const RadioGroup = React.forwardRef<HTMLDivElement, RadioGroupProps>(\n (\n {\n classNames,\n styles,\n disabled = false,\n options,\n direction = 'column',\n name: nameProp,\n defaultValue,\n onValueChange,\n ...props\n },\n ref\n ) => {\n const { global } = useNSUI()\n const autoName = useId()\n const name = nameProp ?? autoName\n\n const [selectedValue, setSelectedValue] = useState(defaultValue)\n const roving = useRovingFocus()\n\n const handleChange = useCallback(\n (value: string) => {\n setSelectedValue(value)\n roving.setActiveId(value)\n onValueChange?.(value)\n },\n [onValueChange, roving]\n )\n\n const contextValue = useMemo(\n () => ({\n name,\n disabled,\n selectedValue,\n onChange: handleChange,\n roving\n }),\n [name, disabled, selectedValue, handleChange, roving]\n )\n\n return (\n <RadioContext.Provider value={contextValue}>\n <Flex\n {...props}\n as=\"fieldset\"\n ref={ref}\n direction={direction}\n role=\"radiogroup\"\n tabIndex={roving.hasInteracted ? -1 : 0}\n onKeyDown={roving.handleGroupKeyDown}\n onBlur={roving.handleGroupBlur}\n className={cn(`${global.prefixCls}-radio-group`, classNames?.root)}\n style={styles?.root}\n >\n {options?.map((option, index) => (\n <RadioOption\n key={option.value ?? index}\n {...option}\n classNames={mergeClassNames(classNames?.option, option.classNames)}\n styles={{ ...styles?.option, ...option.styles }}\n />\n ))}\n </Flex>\n </RadioContext.Provider>\n )\n }\n)\n\nRadioGroup.displayName = 'RadioGroup'\n"]}
package/dist/index.mjs CHANGED
@@ -1,13 +1,11 @@
1
- import React3, { useContext, useId, useState } from 'react';
2
- import { useNSUI, cn } from '@negative-space/system';
1
+ import React, { createContext, useRef, useEffect, useId, useState, useCallback, useMemo, useContext } from 'react';
2
+ import { useNSUI, cn, mergeClassNames } from '@negative-space/system';
3
3
  import { Flex } from '@negative-space/flex';
4
+ import { useRovingFocus } from '@negative-space/roving-focus';
4
5
  import { jsxs, jsx } from 'react/jsx-runtime';
5
6
 
6
- // src/Radio.tsx
7
- var RadioContext = React3.createContext(null);
8
- var RadioProvider = ({ children, ...value }) => {
9
- return /* @__PURE__ */ jsx(RadioContext.Provider, { value, children });
10
- };
7
+ // src/RadioGroup.tsx
8
+ var RadioContext = createContext(null);
11
9
 
12
10
  // src/useRadioContext.ts
13
11
  var useRadioContext = () => {
@@ -17,42 +15,31 @@ var useRadioContext = () => {
17
15
  }
18
16
  return ctx;
19
17
  };
20
- var Radio = React3.forwardRef(
21
- ({
22
- classNames,
23
- alignItems = "center",
24
- styles,
25
- value,
26
- children,
27
- disabled,
28
- isPopDisabled,
29
- ...props
30
- }, ref) => {
31
- const { global, components } = useNSUI();
32
- const { selectedValue, onChange, disabled: groupDisabled } = useRadioContext();
33
- const Disabled = disabled ?? groupDisabled;
18
+ var RadioOption = React.forwardRef(
19
+ ({ value, label, disabled, classNames, styles, ...props }) => {
20
+ const { global } = useNSUI();
21
+ const { selectedValue, onChange, disabled: groupDisabled, roving } = useRadioContext();
22
+ const ref = useRef(null);
23
+ const isDisabled = disabled ?? groupDisabled;
34
24
  const checked = selectedValue === value;
35
- const IsPopDisabled = isPopDisabled ?? components.radio.isPopDisabled;
36
- const handleClick = () => {
37
- if (!Disabled) onChange?.(value);
38
- };
39
- const handleKeyDown = (e) => {
40
- if (Disabled) return;
41
- if (e.key === "Enter" || e.key === " ") {
42
- e.preventDefault();
43
- onChange?.(value);
44
- }
45
- };
25
+ useEffect(() => {
26
+ roving.registerItem({
27
+ id: value,
28
+ ref,
29
+ disabled: isDisabled
30
+ });
31
+ return () => roving.unregisterItem(value);
32
+ }, [value, isDisabled, roving]);
46
33
  return /* @__PURE__ */ jsxs(
47
34
  Flex,
48
35
  {
49
36
  ...props,
50
37
  as: "label",
51
- alignItems,
52
- "aria-disabled": Disabled,
53
- "data-disabled": Disabled,
54
- onClick: handleClick,
55
- className: cn(`${global.prefixCls}-radio-label`, classNames?.label),
38
+ alignItems: "center",
39
+ className: cn(
40
+ `${global.prefixCls}-radio-label ${global.prefixCls}-clickable`,
41
+ classNames?.label
42
+ ),
56
43
  style: styles?.label,
57
44
  children: [
58
45
  /* @__PURE__ */ jsx(
@@ -62,75 +49,110 @@ var Radio = React3.forwardRef(
62
49
  role: "radio",
63
50
  "aria-checked": checked,
64
51
  "data-checked": checked,
65
- tabIndex: Disabled ? -1 : 0,
66
- onKeyDown: handleKeyDown,
67
- "data-disabled": Disabled,
52
+ tabIndex: roving.hasInteracted && roving.activeId === value ? 0 : -1,
53
+ "data-disabled": isDisabled,
54
+ onClick: () => {
55
+ if (!isDisabled) {
56
+ roving.focusItem(value);
57
+ onChange?.(value);
58
+ }
59
+ },
60
+ onKeyDown: (e) => roving.handleItemKeyDown(e, value, onChange),
68
61
  className: cn(`${global.prefixCls}-radio`, classNames?.radio),
69
- style: { ...styles?.radio },
62
+ style: {
63
+ borderRadius: "50%",
64
+ display: "flex",
65
+ alignItems: "center",
66
+ justifyContent: "center",
67
+ overflow: "hidden",
68
+ ...styles?.radio
69
+ },
70
70
  children: /* @__PURE__ */ jsx(
71
71
  "div",
72
72
  {
73
+ "data-visible": checked,
73
74
  className: cn(
74
- `${global.prefixCls}-radio-inner`,
75
- checked && !IsPopDisabled && `${global.prefixCls}-pop`,
75
+ `${global.prefixCls}-radio-inner ${global.prefixCls}-fade`,
76
76
  classNames?.inner
77
77
  ),
78
- style: { ...styles?.inner }
78
+ style: {
79
+ borderRadius: "50%",
80
+ ...styles?.inner
81
+ }
79
82
  }
80
83
  )
81
84
  }
82
85
  ),
83
- children
86
+ label
84
87
  ]
85
88
  }
86
89
  );
87
90
  }
88
91
  );
89
- Radio.displayName = "Radio";
90
- var RadioGroup = ({
91
- direction = "column",
92
- className,
93
- value: valueProp,
94
- onChange,
95
- name: nameProp,
96
- disabled = false,
97
- children,
98
- ...props
99
- }) => {
100
- const { global } = useNSUI();
101
- const autoName = useId();
102
- const name = nameProp ?? autoName;
103
- const [internalValue, setInternalValue] = useState(valueProp);
104
- const value = valueProp !== void 0 ? valueProp : internalValue;
105
- const handleChange = (newValue) => {
106
- if (valueProp === void 0) setInternalValue(newValue);
107
- onChange?.(newValue);
108
- };
109
- if (process.env.NODE_ENV !== "production") {
110
- const countRadios = (nodes) => {
111
- return React3.Children.toArray(nodes).reduce((acc, child) => {
112
- if (!React3.isValidElement(child)) return acc;
113
- if (child.type === Radio) return acc + 1;
114
- const childChildren = child.props?.children;
115
- return acc + countRadios(childChildren);
116
- }, 0);
117
- };
118
- const radiosCount = countRadios(children);
119
- if (radiosCount === 1) {
120
- console.warn("RadioGroup has only one Radio. Consider using multiple options for clarity.");
121
- }
92
+ RadioOption.displayName = "RadioOption";
93
+ var RadioGroup = React.forwardRef(
94
+ ({
95
+ classNames,
96
+ styles,
97
+ disabled = false,
98
+ options,
99
+ direction = "column",
100
+ name: nameProp,
101
+ defaultValue,
102
+ onValueChange,
103
+ ...props
104
+ }, ref) => {
105
+ const { global } = useNSUI();
106
+ const autoName = useId();
107
+ const name = nameProp ?? autoName;
108
+ const [selectedValue, setSelectedValue] = useState(defaultValue);
109
+ const roving = useRovingFocus();
110
+ const handleChange = useCallback(
111
+ (value) => {
112
+ setSelectedValue(value);
113
+ roving.setActiveId(value);
114
+ onValueChange?.(value);
115
+ },
116
+ [onValueChange, roving]
117
+ );
118
+ const contextValue = useMemo(
119
+ () => ({
120
+ name,
121
+ disabled,
122
+ selectedValue,
123
+ onChange: handleChange,
124
+ roving
125
+ }),
126
+ [name, disabled, selectedValue, handleChange, roving]
127
+ );
128
+ return /* @__PURE__ */ jsx(RadioContext.Provider, { value: contextValue, children: /* @__PURE__ */ jsx(
129
+ Flex,
130
+ {
131
+ ...props,
132
+ as: "fieldset",
133
+ ref,
134
+ direction,
135
+ role: "radiogroup",
136
+ tabIndex: roving.hasInteracted ? -1 : 0,
137
+ onKeyDown: roving.handleGroupKeyDown,
138
+ onBlur: roving.handleGroupBlur,
139
+ className: cn(`${global.prefixCls}-radio-group`, classNames?.root),
140
+ style: styles?.root,
141
+ children: options?.map((option, index) => /* @__PURE__ */ jsx(
142
+ RadioOption,
143
+ {
144
+ ...option,
145
+ classNames: mergeClassNames(classNames?.option, option.classNames),
146
+ styles: { ...styles?.option, ...option.styles }
147
+ },
148
+ option.value ?? index
149
+ ))
150
+ }
151
+ ) });
122
152
  }
123
- return /* @__PURE__ */ jsx(RadioProvider, { name, selectedValue: value, onChange: handleChange, disabled, children: /* @__PURE__ */ jsx(
124
- Flex,
125
- {
126
- ...props,
127
- direction,
128
- className: cn(`${global.prefixCls}-radio-group`, className),
129
- children
130
- }
131
- ) });
132
- };
153
+ );
154
+ RadioGroup.displayName = "RadioGroup";
133
155
 
134
- export { Radio, RadioGroup };
156
+ export { RadioGroup };
135
157
  //# sourceMappingURL=index.mjs.map
136
158
  //# sourceMappingURL=index.mjs.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/RadioProvider.tsx","../src/useRadioContext.ts","../src/Radio.tsx","../src/RadioGroup.tsx"],"names":["React","jsx","useNSUI","Flex","cn"],"mappings":"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","file":"index.mjs","sourcesContent":["import React from 'react'\n\nexport type RadioContextValue = {\n name: string\n disabled?: boolean\n selectedValue?: string | number\n onChange?: (value: string | number) => void\n}\n\nexport const RadioContext = React.createContext<RadioContextValue | null>(null)\n\nexport interface RadioProviderProps extends RadioContextValue {\n children: React.ReactNode\n}\n\nexport const RadioProvider = ({ children, ...value }: RadioProviderProps) => {\n return <RadioContext.Provider value={value}>{children}</RadioContext.Provider>\n}\n","import { useContext } from 'react'\nimport { RadioContext } from './RadioProvider'\n\nexport const useRadioContext = () => {\n const ctx = useContext(RadioContext)\n\n if (!ctx) {\n throw new Error('Radio must be used within RadioGroup')\n }\n\n return ctx\n}\n","import React from 'react'\nimport { cn, useNSUI } from '@negative-space/system'\nimport { Flex, type FlexProps } from '@negative-space/flex'\nimport { useRadioContext } from './useRadioContext'\n\nexport interface RadioProps extends Omit<FlexProps<'label'>, 'className' | 'style'> {\n disabled?: boolean\n value: string | number\n classNames?: {\n label?: string\n radio?: string\n inner?: string\n }\n styles?: {\n label?: React.CSSProperties\n radio?: React.CSSProperties\n inner?: React.CSSProperties\n }\n isPopDisabled?: boolean\n}\n\nexport const Radio = React.forwardRef<HTMLDivElement, RadioProps>(\n (\n {\n classNames,\n alignItems = 'center',\n styles,\n value,\n children,\n disabled,\n isPopDisabled,\n ...props\n },\n ref\n ) => {\n const { global, components } = useNSUI()\n const { selectedValue, onChange, disabled: groupDisabled } = useRadioContext()\n const Disabled = disabled ?? groupDisabled\n const checked = selectedValue === value\n const IsPopDisabled = isPopDisabled ?? components.radio.isPopDisabled\n\n const handleClick = () => {\n if (!Disabled) onChange?.(value)\n }\n\n const handleKeyDown = (e: React.KeyboardEvent) => {\n if (Disabled) return\n if (e.key === 'Enter' || e.key === ' ') {\n e.preventDefault()\n onChange?.(value)\n }\n }\n\n return (\n <Flex\n {...props}\n as=\"label\"\n alignItems={alignItems}\n aria-disabled={Disabled}\n data-disabled={Disabled}\n onClick={handleClick}\n className={cn(`${global.prefixCls}-radio-label`, classNames?.label)}\n style={styles?.label}\n >\n <div\n ref={ref}\n role=\"radio\"\n aria-checked={checked}\n data-checked={checked}\n tabIndex={Disabled ? -1 : 0}\n onKeyDown={handleKeyDown}\n data-disabled={Disabled}\n className={cn(`${global.prefixCls}-radio`, classNames?.radio)}\n style={{ ...styles?.radio }}\n >\n <div\n className={cn(\n `${global.prefixCls}-radio-inner`,\n checked && !IsPopDisabled && `${global.prefixCls}-pop`,\n classNames?.inner\n )}\n style={{ ...styles?.inner }}\n />\n </div>\n {children}\n </Flex>\n )\n }\n)\n\nRadio.displayName = 'Radio'\n","import React, { useState, useId } from 'react'\nimport { cn, useNSUI } from '@negative-space/system'\nimport { Flex, FlexProps } from '@negative-space/flex'\nimport { RadioProvider } from './RadioProvider'\nimport { Radio } from './Radio'\n\nexport interface RadioGroupProps extends Omit<FlexProps, 'onChange'> {\n name?: string\n disabled?: boolean\n value?: string | number\n onChange?: (value: string | number) => void\n}\n\nexport const RadioGroup = ({\n direction = 'column',\n className,\n value: valueProp,\n onChange,\n name: nameProp,\n disabled = false,\n children,\n ...props\n}: RadioGroupProps) => {\n const { global } = useNSUI()\n\n const autoName = useId()\n const name = nameProp ?? autoName\n\n const [internalValue, setInternalValue] = useState<string | number | undefined>(valueProp)\n const value = valueProp !== undefined ? valueProp : internalValue\n\n const handleChange = (newValue: string | number) => {\n if (valueProp === undefined) setInternalValue(newValue)\n onChange?.(newValue)\n }\n\n if (process.env.NODE_ENV !== 'production') {\n const countRadios = (nodes: React.ReactNode): number => {\n return React.Children.toArray(nodes).reduce((acc: number, child) => {\n if (!React.isValidElement(child)) return acc\n if (child.type === Radio) return acc + 1\n const childChildren = (child.props as { children?: React.ReactNode })?.children\n return acc + countRadios(childChildren)\n }, 0)\n }\n\n const radiosCount = countRadios(children)\n if (radiosCount === 1) {\n console.warn('RadioGroup has only one Radio. Consider using multiple options for clarity.')\n }\n }\n\n return (\n <RadioProvider name={name} selectedValue={value} onChange={handleChange} disabled={disabled}>\n <Flex\n {...props}\n direction={direction}\n className={cn(`${global.prefixCls}-radio-group`, className)}\n >\n {children}\n </Flex>\n </RadioProvider>\n )\n}\n"]}
1
+ {"version":3,"sources":["../src/RadioContext.ts","../src/useRadioContext.ts","../src/RadioOption.tsx","../src/RadioGroup.tsx"],"names":["React","useNSUI","jsx","Flex","cn"],"mappings":"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","file":"index.mjs","sourcesContent":["import { createContext } from 'react'\nimport type { useRovingFocus } from '@negative-space/roving-focus'\n\nexport interface RadioContextValue {\n name: string\n disabled?: boolean\n selectedValue?: string\n onChange?: (value: string) => void\n roving: ReturnType<typeof useRovingFocus>\n}\n\nexport const RadioContext = createContext<RadioContextValue | null>(null)\n","import { useContext } from 'react'\nimport { RadioContext, type RadioContextValue } from './RadioContext'\n\nexport const useRadioContext = (): RadioContextValue => {\n const ctx = useContext(RadioContext)\n\n if (!ctx) {\n throw new Error('Radio must be used within RadioGroup')\n }\n\n return ctx\n}\n","import React, { useEffect, useRef } from 'react'\nimport { cn, useNSUI } from '@negative-space/system'\nimport { Flex, type FlexProps } from '@negative-space/flex'\nimport { useRadioContext } from './useRadioContext'\n\nexport interface RadioOptionProps extends Omit<\n FlexProps<'label'>,\n 'as' | 'className' | 'style' | 'onClick'\n> {\n classNames?: {\n label?: string\n radio?: string\n inner?: string\n }\n styles?: {\n label?: React.CSSProperties\n radio?: React.CSSProperties\n inner?: React.CSSProperties\n }\n disabled?: boolean\n value: string\n label: React.ReactNode\n isPopDisabled?: boolean\n}\n\nexport const RadioOption = React.forwardRef<HTMLDivElement, RadioOptionProps>(\n ({ value, label, disabled, classNames, styles, ...props }) => {\n const { global } = useNSUI()\n const { selectedValue, onChange, disabled: groupDisabled, roving } = useRadioContext()\n\n const ref = useRef<HTMLDivElement>(null)\n const isDisabled = disabled ?? groupDisabled\n const checked = selectedValue === value\n\n useEffect(() => {\n roving.registerItem({\n id: value,\n ref: ref as React.RefObject<HTMLElement>,\n disabled: isDisabled\n })\n return () => roving.unregisterItem(value)\n }, [value, isDisabled, roving])\n\n return (\n <Flex\n {...props}\n as=\"label\"\n alignItems=\"center\"\n className={cn(\n `${global.prefixCls}-radio-label ${global.prefixCls}-clickable`,\n classNames?.label\n )}\n style={styles?.label}\n >\n <div\n ref={ref}\n role=\"radio\"\n aria-checked={checked}\n data-checked={checked}\n tabIndex={roving.hasInteracted && roving.activeId === value ? 0 : -1}\n data-disabled={isDisabled}\n onClick={() => {\n if (!isDisabled) {\n roving.focusItem(value)\n onChange?.(value)\n }\n }}\n onKeyDown={(e) => roving.handleItemKeyDown(e, value, onChange)}\n className={cn(`${global.prefixCls}-radio`, classNames?.radio)}\n style={{\n borderRadius: '50%',\n display: 'flex',\n alignItems: 'center',\n justifyContent: 'center',\n overflow: 'hidden',\n ...styles?.radio\n }}\n >\n <div\n data-visible={checked}\n className={cn(\n `${global.prefixCls}-radio-inner ${global.prefixCls}-fade`,\n classNames?.inner\n )}\n style={{\n borderRadius: '50%',\n ...styles?.inner\n }}\n />\n </div>\n {label}\n </Flex>\n )\n }\n)\n\nRadioOption.displayName = 'RadioOption'\n","import React, { useCallback, useMemo, useState, useId } from 'react'\nimport { cn, mergeClassNames, useNSUI } from '@negative-space/system'\nimport { Flex, type FlexProps } from '@negative-space/flex'\nimport { useRovingFocus } from '@negative-space/roving-focus'\nimport { RadioOption, type RadioOptionProps } from './RadioOption'\nimport { RadioContext } from './RadioContext'\n\nexport interface RadioGroupProps extends Omit<FlexProps, 'children' | 'onChange'> {\n classNames?: {\n root?: string\n option?: {\n label?: string\n radio?: string\n inner?: string\n }\n }\n styles?: {\n root?: React.CSSProperties\n option?: {\n label?: React.CSSProperties\n radio?: React.CSSProperties\n inner?: React.CSSProperties\n }\n }\n name?: string\n disabled?: boolean\n options?: RadioOptionProps[]\n defaultValue?: string\n onValueChange?: (value: string) => void\n}\n\nexport const RadioGroup = React.forwardRef<HTMLDivElement, RadioGroupProps>(\n (\n {\n classNames,\n styles,\n disabled = false,\n options,\n direction = 'column',\n name: nameProp,\n defaultValue,\n onValueChange,\n ...props\n },\n ref\n ) => {\n const { global } = useNSUI()\n const autoName = useId()\n const name = nameProp ?? autoName\n\n const [selectedValue, setSelectedValue] = useState(defaultValue)\n const roving = useRovingFocus()\n\n const handleChange = useCallback(\n (value: string) => {\n setSelectedValue(value)\n roving.setActiveId(value)\n onValueChange?.(value)\n },\n [onValueChange, roving]\n )\n\n const contextValue = useMemo(\n () => ({\n name,\n disabled,\n selectedValue,\n onChange: handleChange,\n roving\n }),\n [name, disabled, selectedValue, handleChange, roving]\n )\n\n return (\n <RadioContext.Provider value={contextValue}>\n <Flex\n {...props}\n as=\"fieldset\"\n ref={ref}\n direction={direction}\n role=\"radiogroup\"\n tabIndex={roving.hasInteracted ? -1 : 0}\n onKeyDown={roving.handleGroupKeyDown}\n onBlur={roving.handleGroupBlur}\n className={cn(`${global.prefixCls}-radio-group`, classNames?.root)}\n style={styles?.root}\n >\n {options?.map((option, index) => (\n <RadioOption\n key={option.value ?? index}\n {...option}\n classNames={mergeClassNames(classNames?.option, option.classNames)}\n styles={{ ...styles?.option, ...option.styles }}\n />\n ))}\n </Flex>\n </RadioContext.Provider>\n )\n }\n)\n\nRadioGroup.displayName = 'RadioGroup'\n"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@negative-space/radio",
3
- "version": "1.3.0",
3
+ "version": "1.4.0",
4
4
  "publishConfig": {
5
5
  "access": "public"
6
6
  },
@@ -30,8 +30,9 @@
30
30
  "url": "https://github.com/negative-space-ui/nsui/issues"
31
31
  },
32
32
  "dependencies": {
33
- "@negative-space/flex": "1.1.1",
34
- "@negative-space/system": "1.0.1"
33
+ "@negative-space/flex": "1.2.0",
34
+ "@negative-space/roving-focus": "1.1.0",
35
+ "@negative-space/system": "1.1.0"
35
36
  },
36
37
  "peerDependencies": {
37
38
  "react": "^19.2.3"