@rescui/switcher 0.12.11 → 0.13.1-RUI-293-Update-menu-component-7a032357e.20

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/lib/switcher.js CHANGED
@@ -5,6 +5,7 @@ import cn from 'classnames';
5
5
  import { useThemeWithUndefined } from '@rescui/ui-contexts';
6
6
  import { useResizeObserver } from '@rescui/use-resize-observer';
7
7
  import styles from './switcher.p.module.css.js';
8
+ import { useSafeId } from './use-safe-id.js';
8
9
  const MARK_TRANSITION_DURATION = '0.2s';
9
10
  const sizeToClass = {
10
11
  l: styles.sizeL,
@@ -35,19 +36,27 @@ const optionRenderer = (_ref, ref) => {
35
36
  value,
36
37
  icon,
37
38
  disabled,
38
- onClick,
39
- selected
39
+ onChange,
40
+ selected,
41
+ name,
42
+ 'aria-label': ariaLabel
40
43
  } = _ref;
41
44
  const isWithoutText = isNodeEmpty(label);
42
- return /*#__PURE__*/React.createElement("button", {
43
- key: String(value),
45
+ return /*#__PURE__*/React.createElement("label", {
44
46
  ref: ref,
45
- onClick: onClick,
46
- type: "button",
47
+ key: String(value),
47
48
  "data-rs-internal": 'switcher__option',
49
+ className: cn(styles.option, selected && styles.selected),
50
+ "aria-label": ariaLabel
51
+ }, /*#__PURE__*/React.createElement("input", {
52
+ type: "radio",
53
+ value: String(value),
54
+ onChange: onChange,
55
+ className: styles.radio,
56
+ checked: selected,
48
57
  disabled: disabled,
49
- className: cn(styles.option, selected && styles.selected)
50
- }, /*#__PURE__*/React.isValidElement(icon) && /*#__PURE__*/cloneElement(icon, {
58
+ name: name
59
+ }), /*#__PURE__*/React.isValidElement(icon) && /*#__PURE__*/cloneElement(icon, {
51
60
  className: cn(icon.props.className, styles.icon),
52
61
  'data-render-all-sizes': true
53
62
  }), !isWithoutText && /*#__PURE__*/React.createElement("span", null, label));
@@ -62,17 +71,19 @@ function Switcher(_ref2) {
62
71
  size = 'm',
63
72
  disabled,
64
73
  compact = false,
65
- ['data-e2e']: e2eId,
66
74
  options,
67
75
  value: switcherValue,
68
76
  mode = 'classic',
69
77
  iconPosition = 'left',
70
78
  onChange,
71
- style
79
+ style,
80
+ name,
81
+ ...rest
72
82
  } = _ref2;
73
83
  const refsArray = useRef([]);
74
84
  const containerRef = useRef(null);
75
85
  const theme = useThemeWithUndefined(themeFromProps);
86
+ const uniqueId = useSafeId();
76
87
  const handleOptionRef = useCallback(index => ref => {
77
88
  if (!ref) {
78
89
  return;
@@ -119,20 +130,21 @@ function Switcher(_ref2) {
119
130
 
120
131
  useEffect(() => updateMark(true), [activeIndex]);
121
132
  useResizeObserver(containerRef, () => updateMark(false));
122
- return /*#__PURE__*/React.createElement("span", {
133
+ return /*#__PURE__*/React.createElement("span", { ...rest,
123
134
  className: cn(className, styles.switcher, sizeToClass[size], themeToClass[theme], modeToClass[mode], disabled && styles.disabled, compact && styles.compact, mark.hidden && styles.hiddenMark, iconPosition === 'right' ? styles.alignIconRight : styles.alignIconLeft),
124
- "data-e2e": e2eId,
125
135
  ref: containerRef,
126
136
  "data-rs-internal": 'switcher',
127
137
  "data-test": cn(`switcher`, disabled && `switcher-disabled`),
128
138
  style: { ...style,
129
139
  '--_rs-switcher-mark-transition-duration': isMarkTransitionEnabled ? MARK_TRANSITION_DURATION : '0s'
130
- }
140
+ },
141
+ role: "radiogroup"
131
142
  }, options.map((_ref3, index) => {
132
143
  let {
133
144
  value,
134
145
  label,
135
- icon
146
+ icon,
147
+ 'aria-label': optionAriaLabel
136
148
  } = _ref3;
137
149
  return /*#__PURE__*/React.createElement(Option, {
138
150
  key: String(value),
@@ -140,9 +152,11 @@ function Switcher(_ref2) {
140
152
  label: label,
141
153
  icon: icon,
142
154
  disabled: disabled,
143
- onClick: () => onChange(value),
155
+ onChange: () => onChange(value),
144
156
  ref: handleOptionRef(index),
145
- selected: options[activeIndex]?.value === value
157
+ selected: options[activeIndex]?.value === value,
158
+ name: name ?? `rs-switcher-${uniqueId}`,
159
+ "aria-label": optionAriaLabel
146
160
  });
147
161
  }), /*#__PURE__*/React.createElement("span", {
148
162
  className: cn(styles.mark),
@@ -1,21 +1,22 @@
1
1
  var styles = {
2
- "themeLight": "_themeLight_1whkdot_6",
3
- "themeDark": "_themeDark_1whkdot_9",
4
- "sizeXS": "_sizeXS_1whkdot_13",
5
- "sizeS": "_sizeS_1whkdot_16",
6
- "sizeM": "_sizeM_1whkdot_19",
7
- "sizeL": "_sizeL_1whkdot_22",
8
- "modeClassic": "_modeClassic_1whkdot_26",
9
- "modeRock": "_modeRock_1whkdot_29",
10
- "compact": "_compact_1whkdot_33",
11
- "switcher": "_switcher_1whkdot_37",
12
- "disabled": "_disabled_1whkdot_55",
13
- "alignIconLeft": "_alignIconLeft_1whkdot_60",
14
- "alignIconRight": "_alignIconRight_1whkdot_63",
15
- "option": "_option_1whkdot_67",
16
- "selected": "_selected_1whkdot_118",
17
- "hiddenMark": "_hiddenMark_1whkdot_127",
18
- "icon": "_icon_1whkdot_158",
19
- "mark": "_mark_1whkdot_171"
2
+ "themeLight": "_themeLight_dklen2_6",
3
+ "themeDark": "_themeDark_dklen2_9",
4
+ "sizeXS": "_sizeXS_dklen2_13",
5
+ "sizeS": "_sizeS_dklen2_16",
6
+ "sizeM": "_sizeM_dklen2_19",
7
+ "sizeL": "_sizeL_dklen2_22",
8
+ "modeClassic": "_modeClassic_dklen2_26",
9
+ "modeRock": "_modeRock_dklen2_29",
10
+ "compact": "_compact_dklen2_33",
11
+ "switcher": "_switcher_dklen2_37",
12
+ "disabled": "_disabled_dklen2_55",
13
+ "alignIconLeft": "_alignIconLeft_dklen2_60",
14
+ "alignIconRight": "_alignIconRight_dklen2_63",
15
+ "option": "_option_dklen2_67",
16
+ "selected": "_selected_dklen2_118",
17
+ "hiddenMark": "_hiddenMark_dklen2_127",
18
+ "icon": "_icon_dklen2_158",
19
+ "mark": "_mark_dklen2_171",
20
+ "radio": "_radio_dklen2_199"
20
21
  };
21
22
  export { styles as default };
@@ -0,0 +1,2 @@
1
+ import React from 'react';
2
+ export declare const useSafeId: typeof React.useId;
@@ -0,0 +1,15 @@
1
+ import React, { useRef } from 'react';
2
+
3
+ const useFallbackId = () => {
4
+ const id = useRef(null);
5
+
6
+ if (id.current === null) {
7
+ id.current = useFallbackId.counter++;
8
+ }
9
+
10
+ return id.current.toString();
11
+ };
12
+
13
+ useFallbackId.counter = 0;
14
+ const useSafeId = React.useId ?? useFallbackId;
15
+ export { useSafeId };
@@ -1,35 +1,62 @@
1
- import React from 'react';
2
- export declare type VerticalSwitcherOptionIconPosition = 'left' | 'right';
3
- export declare type VerticalSwitcherSize = 'l' | 'm' | 's' | 'xs';
4
- export declare type VerticalSwitcherTheme = 'light' | 'dark';
5
- export declare type VerticalSwitcherMode = 'rock' | 'classic';
6
- export declare type VerticalSwitcherOption<OptionsValueType> = {
7
- label?: React.ReactNode;
8
- value: OptionsValueType;
9
- /** Additional icon */
10
- icon?: React.ReactNode;
11
- };
12
- export declare type VerticalSwitcherOptionForDocs = VerticalSwitcherOption<any>;
13
- export interface VerticalSwitcherProps<ValueType, OptionsValueType extends ValueType> {
14
- /** Current selected value */
15
- value: ValueType;
16
- /** Array of options */
17
- options?: VerticalSwitcherOption<OptionsValueType>[];
18
- onChange: (value: OptionsValueType) => void;
19
- className?: string;
20
- theme?: VerticalSwitcherTheme;
21
- size?: VerticalSwitcherSize;
22
- /** Appearance mode */
23
- mode?: VerticalSwitcherMode;
24
- disabled?: boolean;
25
- /** Identifier for e2e tests */
26
- 'data-e2e'?: string;
27
- /** **Works only if the `icon` on options is present.** Additional icon position. */
28
- iconPosition?: VerticalSwitcherOptionIconPosition;
29
- /** CSS style object */
30
- style?: React.CSSProperties;
31
- }
32
- export declare function VerticalSwitcher<ValueType, OptionsValueType extends ValueType = ValueType>({ className, theme: themeFromProps, size, disabled, ['data-e2e']: e2eId, options: rawOptions, value: switcherValue, mode, iconPosition, onChange, style }: VerticalSwitcherProps<ValueType, OptionsValueType>): JSX.Element;
33
- export declare namespace VerticalSwitcher {
34
- var propTypes: {};
35
- }
1
+ import React from 'react';
2
+ import type { JSX } from 'react';
3
+ import PropTypes from 'prop-types';
4
+ export type VerticalSwitcherOptionIconPosition = 'left' | 'right';
5
+ export type VerticalSwitcherSize = 'l' | 'm' | 's' | 'xs';
6
+ export type VerticalSwitcherTheme = 'light' | 'dark';
7
+ export type VerticalSwitcherMode = 'rock' | 'classic';
8
+ export type VerticalSwitcherOption<OptionsValueType> = {
9
+ label?: React.ReactNode;
10
+ value: OptionsValueType;
11
+ /** Additional icon */
12
+ icon?: React.ReactNode;
13
+ /** Optional a11y label for the option. May be useful for icon-only options. */
14
+ 'aria-label'?: string;
15
+ };
16
+ export type VerticalSwitcherOptionForDocs = VerticalSwitcherOption<any>;
17
+ interface VerticalSwitcherExplicitProps<ValueType, OptionsValueType extends ValueType> {
18
+ /** Current selected value */
19
+ value: ValueType;
20
+ /** Array of options */
21
+ options?: VerticalSwitcherOption<OptionsValueType>[];
22
+ onChange: (value: OptionsValueType) => void;
23
+ className?: string;
24
+ theme?: VerticalSwitcherTheme;
25
+ size?: VerticalSwitcherSize;
26
+ /** Appearance mode */
27
+ mode?: VerticalSwitcherMode;
28
+ disabled?: boolean;
29
+ /** Identifier for e2e tests */
30
+ 'data-e2e'?: string;
31
+ /** **Works only if the `icon` on options is present.** Additional icon position. */
32
+ iconPosition?: VerticalSwitcherOptionIconPosition;
33
+ /** CSS style object */
34
+ style?: React.CSSProperties;
35
+ /** Optional a11y label for the switcher. Recommended to use either that or `aria-labelledby`. */
36
+ 'aria-label'?: string;
37
+ /** Optional a11y label reference for the switcher. Recommended to use either that or `aria-label`. */
38
+ 'aria-labelledby'?: string;
39
+ /** Optional name of all `input[type="radio"]` children. if not set, it will fallback to a randomly generated name. */
40
+ name?: string;
41
+ }
42
+ export type VerticalSwitcherProps<ValueType, OptionsValueType extends ValueType> = VerticalSwitcherExplicitProps<ValueType, OptionsValueType> & Omit<React.HTMLProps<HTMLSpanElement>, keyof VerticalSwitcherExplicitProps<ValueType, OptionsValueType>>;
43
+ export declare function VerticalSwitcher<ValueType, OptionsValueType extends ValueType = ValueType>({ className, theme: themeFromProps, size, disabled, options: rawOptions, value: switcherValue, mode, iconPosition, onChange, style, name, ...rest }: VerticalSwitcherProps<ValueType, OptionsValueType>): JSX.Element;
44
+ export declare namespace VerticalSwitcher {
45
+ var propTypes: {
46
+ options: PropTypes.Validator<PropTypes.InferProps<{
47
+ label: PropTypes.Requireable<PropTypes.ReactNodeLike>;
48
+ value: PropTypes.Validator<any>;
49
+ icon: PropTypes.Requireable<PropTypes.ReactNodeLike>;
50
+ }>[]>;
51
+ onChange: PropTypes.Validator<(...args: any[]) => any>;
52
+ value: PropTypes.Validator<any>;
53
+ className: PropTypes.Requireable<string>;
54
+ size: PropTypes.Requireable<string>;
55
+ theme: PropTypes.Requireable<string>;
56
+ iconPosition: PropTypes.Requireable<string>;
57
+ disabled: PropTypes.Requireable<boolean>;
58
+ style: PropTypes.Requireable<object>;
59
+ 'data-e2e': PropTypes.Requireable<string>;
60
+ };
61
+ }
62
+ export {};
@@ -5,6 +5,7 @@ import cn from 'classnames';
5
5
  import { useThemeWithUndefined } from '@rescui/ui-contexts';
6
6
  import { useResizeObserver } from '@rescui/use-resize-observer';
7
7
  import styles from './vertical-switcher.p.module.css.js';
8
+ import { useSafeId } from './use-safe-id.js';
8
9
  const MARK_TRANSITION_DURATION = '0.2s'; // eslint-disable-next-line @typescript-eslint/no-empty-function
9
10
 
10
11
  const noop = () => {};
@@ -13,7 +14,8 @@ const verticalSwitcherContext = /*#__PURE__*/createContext({
13
14
  switcherValue: null,
14
15
  onChange: noop,
15
16
  handleOptionRef: () => noop,
16
- disabled: null
17
+ disabled: null,
18
+ name: null
17
19
  });
18
20
  const sizeToClass = {
19
21
  l: styles.sizeL,
@@ -45,7 +47,8 @@ const processOptions = _ref => {
45
47
  options.push({
46
48
  label: option.label,
47
49
  value: option.value,
48
- icon: option.icon
50
+ icon: option.icon,
51
+ 'aria-label': option['aria-label']
49
52
  });
50
53
 
51
54
  if ( /*#__PURE__*/React.isValidElement(option.icon) && iconPosition === 'left') {
@@ -84,26 +87,34 @@ function Option(_ref2) {
84
87
  label,
85
88
  value,
86
89
  icon,
87
- index
90
+ index,
91
+ 'aria-label': ariaLabel
88
92
  } = _ref2;
89
93
  const {
90
94
  switcherValue,
91
95
  onChange,
92
96
  handleOptionRef,
93
- disabled
97
+ disabled,
98
+ name
94
99
  } = useContext(verticalSwitcherContext);
95
100
  const selected = switcherValue === value;
96
101
  const isWithoutText = isNodeEmpty(label);
97
- return /*#__PURE__*/React.createElement("button", {
102
+ return /*#__PURE__*/React.createElement("label", {
98
103
  key: String(value),
99
104
  ref: handleOptionRef(index),
100
- onClick: () => onChange(value),
101
- type: "button",
102
105
  "data-rs-internal": 'vertical-switcher__option',
103
- disabled: disabled,
104
106
  className: cn(styles.option, selected && styles.selected),
105
- "data-test": cn(`vertical-switcher-option`, selected && ' vertical-switcher-option-selected')
106
- }, /*#__PURE__*/React.isValidElement(icon) && /*#__PURE__*/cloneElement(icon, {
107
+ "data-test": cn(`vertical-switcher-option`, selected && ' vertical-switcher-option-selected'),
108
+ "aria-label": ariaLabel
109
+ }, /*#__PURE__*/React.createElement("input", {
110
+ type: "radio",
111
+ value: String(value),
112
+ onChange: () => onChange(value),
113
+ className: styles.radio,
114
+ checked: selected,
115
+ disabled: disabled,
116
+ name: name
117
+ }), /*#__PURE__*/React.isValidElement(icon) && /*#__PURE__*/cloneElement(icon, {
107
118
  className: cn(icon.props.className, styles.icon),
108
119
  'data-render-all-sizes': true
109
120
  }), !isWithoutText && /*#__PURE__*/React.createElement("span", {
@@ -117,17 +128,19 @@ function VerticalSwitcher(_ref3) {
117
128
  theme: themeFromProps,
118
129
  size = 'm',
119
130
  disabled,
120
- ['data-e2e']: e2eId,
121
131
  options: rawOptions,
122
132
  value: switcherValue,
123
133
  mode = 'classic',
124
134
  iconPosition = 'left',
125
135
  onChange,
126
- style
136
+ style,
137
+ name,
138
+ ...rest
127
139
  } = _ref3;
128
140
  const refsArray = useRef([]);
129
141
  const containerRef = useRef(null);
130
142
  const theme = useThemeWithUndefined(themeFromProps);
143
+ const uniqueId = useSafeId();
131
144
  const {
132
145
  options,
133
146
  activeIndex
@@ -184,34 +197,37 @@ function VerticalSwitcher(_ref3) {
184
197
  useResizeObserver(containerRef, () => updateMark(false), {
185
198
  ignoreFirstCall: true
186
199
  });
187
- return /*#__PURE__*/React.createElement("span", {
200
+ return /*#__PURE__*/React.createElement("span", { ...rest,
188
201
  className: cn(className, styles.verticalSwitcher, sizeToClass[size], themeToClass[theme], modeToClass[mode], disabled && styles.disabled, mark.hidden && styles.hiddenMark, iconPosition === 'right' ? styles.alignIconRight : styles.alignIconLeft),
189
- "data-e2e": e2eId,
190
202
  ref: containerRef,
191
203
  "data-rs-internal": 'vertical-switcher',
192
204
  "data-test": cn(`vertical-switcher`, disabled && `vertical-switcher-disabled`),
193
205
  style: { ...style,
194
206
  '--_rs-switcher-vertical-mark-transition-duration': isMarkTransitionEnabled ? MARK_TRANSITION_DURATION : '0s'
195
- }
207
+ },
208
+ role: "radiogroup"
196
209
  }, /*#__PURE__*/React.createElement(verticalSwitcherContext.Provider, {
197
210
  value: {
198
211
  switcherValue: options[activeIndex]?.value,
199
212
  onChange,
200
213
  handleOptionRef,
201
- disabled
214
+ disabled,
215
+ name: name ?? `rs-vertical-switcher-${uniqueId}`
202
216
  }
203
217
  }, options.map((_ref4, index) => {
204
218
  let {
205
219
  value,
206
220
  label,
207
- icon
221
+ icon,
222
+ 'aria-label': optionAriaLabel
208
223
  } = _ref4;
209
224
  return /*#__PURE__*/React.createElement(Option, {
210
225
  key: String(value),
211
226
  value: value,
212
227
  label: label,
213
228
  icon: icon,
214
- index: index
229
+ index: index,
230
+ "aria-label": optionAriaLabel
215
231
  });
216
232
  }), /*#__PURE__*/React.createElement("span", {
217
233
  className: cn(styles.mark),
@@ -1,22 +1,23 @@
1
1
  var styles = {
2
- "themeLight": "_themeLight_8t4v3o_6",
3
- "themeDark": "_themeDark_8t4v3o_9",
4
- "sizeXS": "_sizeXS_8t4v3o_13",
5
- "sizeS": "_sizeS_8t4v3o_16",
6
- "sizeM": "_sizeM_8t4v3o_19",
7
- "sizeL": "_sizeL_8t4v3o_22",
8
- "modeClassic": "_modeClassic_8t4v3o_26",
9
- "modeRock": "_modeRock_8t4v3o_29",
10
- "verticalSwitcher": "_verticalSwitcher_8t4v3o_33",
11
- "disabled": "_disabled_8t4v3o_50",
12
- "alignIconLeft": "_alignIconLeft_8t4v3o_55",
13
- "alignIconRight": "_alignIconRight_8t4v3o_58",
14
- "option": "_option_8t4v3o_62",
15
- "selected": "_selected_8t4v3o_107",
16
- "hiddenMark": "_hiddenMark_8t4v3o_116",
17
- "optionLabel": "_optionLabel_8t4v3o_147",
18
- "icon": "_icon_8t4v3o_153",
19
- "fakeIcon": "_fakeIcon_8t4v3o_164",
20
- "mark": "_mark_8t4v3o_171"
2
+ "themeLight": "_themeLight_z9i77r_6",
3
+ "themeDark": "_themeDark_z9i77r_9",
4
+ "sizeXS": "_sizeXS_z9i77r_13",
5
+ "sizeS": "_sizeS_z9i77r_16",
6
+ "sizeM": "_sizeM_z9i77r_19",
7
+ "sizeL": "_sizeL_z9i77r_22",
8
+ "modeClassic": "_modeClassic_z9i77r_26",
9
+ "modeRock": "_modeRock_z9i77r_29",
10
+ "verticalSwitcher": "_verticalSwitcher_z9i77r_33",
11
+ "disabled": "_disabled_z9i77r_50",
12
+ "alignIconLeft": "_alignIconLeft_z9i77r_55",
13
+ "alignIconRight": "_alignIconRight_z9i77r_58",
14
+ "option": "_option_z9i77r_62",
15
+ "selected": "_selected_z9i77r_107",
16
+ "hiddenMark": "_hiddenMark_z9i77r_116",
17
+ "optionLabel": "_optionLabel_z9i77r_147",
18
+ "icon": "_icon_z9i77r_153",
19
+ "fakeIcon": "_fakeIcon_z9i77r_164",
20
+ "mark": "_mark_z9i77r_171",
21
+ "radio": "_radio_z9i77r_196"
21
22
  };
22
23
  export { styles as default };