@alfalab/core-components-radio-group 3.0.8 → 4.0.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.
Files changed (92) hide show
  1. package/Component.desktop.d.ts +6 -0
  2. package/Component.desktop.js +20 -0
  3. package/Component.mobile.d.ts +6 -0
  4. package/Component.mobile.js +20 -0
  5. package/Component.responsive.d.ts +18 -0
  6. package/Component.responsive.js +26 -0
  7. package/{Component.d.ts → components/base-radio-group/Component.d.ts} +9 -3
  8. package/{cssm → components/base-radio-group}/Component.js +18 -15
  9. package/{index.css → components/base-radio-group/index.css} +25 -21
  10. package/components/base-radio-group/index.d.ts +1 -0
  11. package/components/base-radio-group/index.js +13 -0
  12. package/cssm/Component.desktop.d.ts +6 -0
  13. package/cssm/Component.desktop.js +20 -0
  14. package/cssm/Component.mobile.d.ts +6 -0
  15. package/cssm/Component.mobile.js +20 -0
  16. package/cssm/Component.responsive.d.ts +18 -0
  17. package/cssm/Component.responsive.js +29 -0
  18. package/{esm → cssm/components/base-radio-group}/Component.d.ts +9 -3
  19. package/{Component.js → cssm/components/base-radio-group/Component.js} +17 -16
  20. package/cssm/components/base-radio-group/index.d.ts +1 -0
  21. package/cssm/components/base-radio-group/index.js +14 -0
  22. package/cssm/{index.module.css → components/base-radio-group/index.module.css} +7 -3
  23. package/cssm/desktop.d.ts +2 -0
  24. package/cssm/desktop.js +16 -0
  25. package/cssm/desktop.module.css +27 -0
  26. package/cssm/index.d.ts +2 -1
  27. package/cssm/index.js +9 -3
  28. package/cssm/mobile.d.ts +2 -0
  29. package/cssm/mobile.js +16 -0
  30. package/cssm/mobile.module.css +29 -0
  31. package/cssm/vars.css +19 -0
  32. package/desktop.css +28 -0
  33. package/desktop.d.ts +2 -0
  34. package/desktop.js +14 -0
  35. package/esm/Component.desktop.d.ts +6 -0
  36. package/esm/Component.desktop.js +12 -0
  37. package/esm/Component.mobile.d.ts +6 -0
  38. package/esm/Component.mobile.js +12 -0
  39. package/esm/Component.responsive.d.ts +18 -0
  40. package/esm/Component.responsive.js +18 -0
  41. package/{cssm → esm/components/base-radio-group}/Component.d.ts +9 -3
  42. package/esm/components/base-radio-group/Component.js +52 -0
  43. package/esm/{index.css → components/base-radio-group/index.css} +25 -21
  44. package/esm/components/base-radio-group/index.d.ts +1 -0
  45. package/esm/components/base-radio-group/index.js +5 -0
  46. package/esm/desktop.css +28 -0
  47. package/esm/desktop.d.ts +2 -0
  48. package/esm/desktop.js +6 -0
  49. package/esm/index.d.ts +2 -1
  50. package/esm/index.js +5 -1
  51. package/esm/mobile.css +30 -0
  52. package/esm/mobile.d.ts +2 -0
  53. package/esm/mobile.js +6 -0
  54. package/index.d.ts +2 -1
  55. package/index.js +6 -2
  56. package/mobile.css +30 -0
  57. package/mobile.d.ts +2 -0
  58. package/mobile.js +14 -0
  59. package/modern/Component.desktop.d.ts +6 -0
  60. package/modern/Component.desktop.js +11 -0
  61. package/modern/Component.mobile.d.ts +6 -0
  62. package/modern/Component.mobile.js +11 -0
  63. package/modern/Component.responsive.d.ts +18 -0
  64. package/modern/Component.responsive.js +16 -0
  65. package/modern/{Component.d.ts → components/base-radio-group/Component.d.ts} +9 -3
  66. package/modern/{Component.js → components/base-radio-group/Component.js} +13 -11
  67. package/modern/{index.css → components/base-radio-group/index.css} +25 -21
  68. package/modern/components/base-radio-group/index.d.ts +1 -0
  69. package/modern/components/base-radio-group/index.js +4 -0
  70. package/modern/desktop.css +28 -0
  71. package/modern/desktop.d.ts +2 -0
  72. package/modern/desktop.js +5 -0
  73. package/modern/index.d.ts +2 -1
  74. package/modern/index.js +5 -1
  75. package/modern/mobile.css +30 -0
  76. package/modern/mobile.d.ts +2 -0
  77. package/modern/mobile.js +5 -0
  78. package/package.json +21 -3
  79. package/src/Component.desktop.tsx +11 -0
  80. package/src/Component.mobile.tsx +11 -0
  81. package/src/Component.responsive.tsx +27 -0
  82. package/src/components/base-radio-group/Component.tsx +237 -0
  83. package/src/components/base-radio-group/index.module.css +87 -0
  84. package/src/components/base-radio-group/index.ts +1 -0
  85. package/src/desktop.module.css +13 -0
  86. package/src/desktop.ts +1 -0
  87. package/src/index.ts +1 -0
  88. package/src/mobile.module.css +13 -0
  89. package/src/mobile.ts +1 -0
  90. package/src/vars.css +13 -0
  91. package/esm/Component.js +0 -50
  92. package/send-stats.js +0 -82
@@ -0,0 +1,16 @@
1
+ import React, { forwardRef } from 'react';
2
+ import { useMatchMedia } from '@alfalab/core-components-mq/modern';
3
+ import { RadioGroupDesktop } from './Component.desktop.js';
4
+ import { RadioGroupMobile } from './Component.mobile.js';
5
+ import './components/base-radio-group/Component.js';
6
+ import 'classnames';
7
+ import '@alfalab/hooks';
8
+
9
+ const RadioGroup = forwardRef(({ breakpoint = 1024, ...restProps }, ref) => {
10
+ const query = `(min-width: ${breakpoint}px)`;
11
+ const [isDesktop] = useMatchMedia(query);
12
+ const Component = isDesktop ? RadioGroupDesktop : RadioGroupMobile;
13
+ return React.createElement(Component, { ref: ref, ...restProps });
14
+ });
15
+
16
+ export { RadioGroup };
@@ -3,7 +3,7 @@ import React from 'react';
3
3
  import { ChangeEvent, FocusEvent, MouseEvent, ReactNode } from "react";
4
4
  type Direction = 'horizontal' | 'vertical';
5
5
  type RadioGroupType = 'radio' | 'tag';
6
- type RadioGroupProps = {
6
+ type BaseRadioGroupProps = {
7
7
  /**
8
8
  * Заголовок группы
9
9
  */
@@ -63,6 +63,12 @@ type RadioGroupProps = {
63
63
  * Value выбранного дочернего элемента
64
64
  */
65
65
  value?: string | null;
66
+ /**
67
+ * Основные стили компонента.
68
+ */
69
+ styles: {
70
+ [key: string]: string;
71
+ };
66
72
  };
67
- declare const RadioGroup: React.ForwardRefExoticComponent<RadioGroupProps & React.RefAttributes<HTMLDivElement>>;
68
- export { Direction, RadioGroupType, RadioGroupProps, RadioGroup };
73
+ declare const BaseRadioGroup: React.ForwardRefExoticComponent<BaseRadioGroupProps & React.RefAttributes<HTMLDivElement>>;
74
+ export { Direction, RadioGroupType, BaseRadioGroupProps, BaseRadioGroup };
@@ -2,10 +2,10 @@ import React, { forwardRef, useState, Children, isValidElement, cloneElement } f
2
2
  import cn from 'classnames';
3
3
  import { useDidUpdateEffect } from '@alfalab/hooks';
4
4
 
5
- const styles = {"component":"radio-group__component_phgvy","error":"radio-group__error_phgvy","radioList":"radio-group__radioList_phgvy","vertical":"radio-group__vertical_phgvy","radio":"radio-group__radio_phgvy","horizontal":"radio-group__horizontal_phgvy","tagLabel":"radio-group__tagLabel_phgvy","label":"radio-group__label_phgvy","sub":"radio-group__sub_phgvy","errorMessage":"radio-group__errorMessage_phgvy","hint":"radio-group__hint_phgvy","hiddenInput":"radio-group__hiddenInput_phgvy"};
5
+ const commonStyles = {"component":"radio-group__component_laaha","error":"radio-group__error_laaha","tag":"radio-group__tag_laaha","radioList":"radio-group__radioList_laaha","vertical":"radio-group__vertical_laaha","radio":"radio-group__radio_laaha","horizontal":"radio-group__horizontal_laaha","tagLabel":"radio-group__tagLabel_laaha","label":"radio-group__label_laaha","sub":"radio-group__sub_laaha","errorMessage":"radio-group__errorMessage_laaha","hint":"radio-group__hint_laaha","hiddenInput":"radio-group__hiddenInput_laaha"};
6
6
  require('./index.css')
7
7
 
8
- const RadioGroup = forwardRef(({ children, className, direction = 'vertical', label, error, hint, onChange, onBlur, onFocus, type = 'radio', dataTestId, disabled = false, name, value, }, ref) => {
8
+ const BaseRadioGroup = forwardRef(({ children, className, direction = 'vertical', label, error, hint, onChange, onBlur, onFocus, type = 'radio', dataTestId, disabled = false, name, value, styles, }, ref) => {
9
9
  const [stateValue, setStateValue] = useState('');
10
10
  useDidUpdateEffect(() => {
11
11
  setStateValue(value);
@@ -25,7 +25,7 @@ const RadioGroup = forwardRef(({ children, className, direction = 'vertical', la
25
25
  ...child.props,
26
26
  checked: isChecked(childValue),
27
27
  name,
28
- className: cn(childClassName, styles.radio),
28
+ className: cn(childClassName, commonStyles.radio),
29
29
  });
30
30
  };
31
31
  const renderTag = (child) => {
@@ -39,21 +39,23 @@ const RadioGroup = forwardRef(({ children, className, direction = 'vertical', la
39
39
  name,
40
40
  tabIndex: -1,
41
41
  });
42
- return (React.createElement("label", { className: cn(styles.radio, styles.tagLabel) },
43
- React.createElement("input", { type: 'radio', autoComplete: 'off', onChange: (event) => handleChange(event, childValue), disabled: disabled || child.props.disabled, name: name, checked: checked, className: styles.hiddenInput, value: child.props.value }),
42
+ return (React.createElement("label", { className: cn(commonStyles.radio, commonStyles.tagLabel) },
43
+ React.createElement("input", { type: 'radio', autoComplete: 'off', onChange: (event) => handleChange(event, childValue), disabled: disabled || child.props.disabled, name: name, checked: checked, className: commonStyles.hiddenInput, value: child.props.value }),
44
44
  clone));
45
45
  };
46
46
  const errorMessage = typeof error === 'boolean' ? '' : error;
47
- return (React.createElement("div", { className: cn(styles.component, styles[type], styles[direction], { [styles.error]: error }, className), "data-test-id": dataTestId, ref: ref },
48
- label ? React.createElement("span", { className: styles.label }, label) : null,
49
- children ? (React.createElement("div", { className: styles.radioList, onBlur: onBlur, onFocus: onFocus }, Children.map(children, (child) => {
47
+ return (React.createElement("div", { className: cn(commonStyles.component, commonStyles[type], commonStyles[direction], { [commonStyles.error]: error }, className), "data-test-id": dataTestId, ref: ref },
48
+ label ? (React.createElement("span", { className: cn(commonStyles.label, styles.label) }, label)) : null,
49
+ children ? (React.createElement("div", { className: cn(commonStyles.radioList, {
50
+ [styles.radioList]: type === 'radio',
51
+ }), onBlur: onBlur, onFocus: onFocus }, Children.map(children, (child) => {
50
52
  if (isValidElement(child)) {
51
53
  return type === 'radio' ? renderRadio(child) : renderTag(child);
52
54
  }
53
55
  return null;
54
56
  }))) : null,
55
- errorMessage && (React.createElement("span", { className: cn(styles.sub, styles.errorMessage), role: 'alert' }, errorMessage)),
56
- hint && !errorMessage && (React.createElement("span", { className: cn(styles.sub, styles.hint) }, hint))));
57
+ errorMessage && (React.createElement("span", { className: cn(commonStyles.sub, styles.sub, commonStyles.errorMessage), role: 'alert' }, errorMessage)),
58
+ hint && !errorMessage && (React.createElement("span", { className: cn(commonStyles.sub, styles.sub, commonStyles.hint) }, hint))));
57
59
  });
58
60
 
59
- export { RadioGroup };
61
+ export { BaseRadioGroup };
@@ -1,4 +1,4 @@
1
- /* hash: qfvvr */
1
+ /* hash: 2q0m4 */
2
2
  :root {
3
3
  } /* deprecated */ :root {
4
4
  --color-light-border-link: #0072ef;
@@ -28,57 +28,61 @@
28
28
  --radio-group-error-color: var(--color-light-text-negative);
29
29
  --radio-group-hint-color: var(--color-light-text-secondary);
30
30
  --radio-group-label-color: var(--color-light-text-primary);
31
- } .radio-group__component_phgvy {
31
+
32
+ /* mobile */
33
+ } .radio-group__component_laaha {
32
34
  display: flex;
33
35
  flex-direction: column;
34
- } .radio-group__error_phgvy {
35
- padding-left: var(--gap-xs);
36
- border-left: 1px solid var(--radio-group-error-color);
37
- } .radio-group__radioList_phgvy {
36
+ } .radio-group__error_laaha {
37
+ padding-left: var(--gap-s);
38
+ border-left: 1px solid var(--radio-group-error-color)
39
+ } .radio-group__error_laaha.radio-group__tag_laaha {
40
+ padding-left: var(--gap-xs);
41
+ } .radio-group__radioList_laaha {
38
42
  display: flex;
39
- } .radio-group__vertical_phgvy .radio-group__radioList_phgvy {
43
+ } .radio-group__vertical_laaha .radio-group__radioList_laaha {
40
44
  flex-direction: column;
41
45
  align-items: flex-start;
42
- } .radio-group__vertical_phgvy .radio-group__radio_phgvy {
46
+ } .radio-group__vertical_laaha .radio-group__radio_laaha {
43
47
  margin-bottom: var(--gap-m)
44
- } .radio-group__vertical_phgvy .radio-group__radio_phgvy:last-child {
48
+ } .radio-group__vertical_laaha .radio-group__radio_laaha:last-child {
45
49
  margin-bottom: 0;
46
- } .radio-group__horizontal_phgvy .radio-group__radioList_phgvy {
50
+ } .radio-group__horizontal_laaha .radio-group__radioList_laaha {
47
51
  flex-wrap: wrap;
48
52
  margin-bottom: var(--gap-xs-neg);
49
- } .radio-group__horizontal_phgvy .radio-group__radio_phgvy {
53
+ } .radio-group__horizontal_laaha .radio-group__radio_laaha {
50
54
  margin-right: var(--gap-xl);
51
55
  margin-bottom: var(--gap-xs);
52
- } .radio-group__horizontal_phgvy .radio-group__tagLabel_phgvy {
56
+ } .radio-group__horizontal_laaha .radio-group__tagLabel_laaha {
53
57
  margin-right: var(--gap-xs);
54
- } .radio-group__horizontal_phgvy .radio-group__radio_phgvy:last-child,
55
- .radio-group__horizontal_phgvy .radio-group__tagLabel_phgvy:last-child {
58
+ } .radio-group__horizontal_laaha .radio-group__radio_laaha:last-child,
59
+ .radio-group__horizontal_laaha .radio-group__tagLabel_laaha:last-child {
56
60
  margin-right: 0;
57
- } .radio-group__label_phgvy {
61
+ } .radio-group__label_laaha {
58
62
  font-size: 16px;
59
63
  line-height: 24px;
60
64
  font-weight: 400;
61
65
  margin-bottom: var(--gap-s);
62
66
  color: var(--radio-group-label-color);
63
- } .radio-group__sub_phgvy {
67
+ } .radio-group__sub_laaha {
64
68
  font-size: 14px;
65
69
  line-height: 18px;
66
70
  font-weight: 400;
67
71
  margin-top: var(--gap-s);
68
- } .radio-group__errorMessage_phgvy {
72
+ } .radio-group__errorMessage_laaha {
69
73
  color: var(--radio-group-error-color);
70
- } .radio-group__hint_phgvy {
74
+ } .radio-group__hint_laaha {
71
75
  color: var(--radio-group-hint-color);
72
- } .radio-group__hiddenInput_phgvy {
76
+ } .radio-group__hiddenInput_laaha {
73
77
  position: absolute;
74
78
  z-index: -1;
75
79
  top: 0;
76
80
  left: 0;
77
81
  opacity: 0
78
- } .radio-group__hiddenInput_phgvy:focus ~ button {
82
+ } .radio-group__hiddenInput_laaha:focus ~ button {
79
83
  outline: 2px solid var(--focus-color);
80
84
  outline-offset: 2px;
81
- } .radio-group__tagLabel_phgvy {
85
+ } .radio-group__tagLabel_laaha {
82
86
  position: relative;
83
87
  max-width: 100%;
84
88
  }
@@ -0,0 +1 @@
1
+ export * from "./Component";
@@ -0,0 +1,4 @@
1
+ export { BaseRadioGroup } from './Component.js';
2
+ import 'react';
3
+ import 'classnames';
4
+ import '@alfalab/hooks';
@@ -0,0 +1,28 @@
1
+ /* hash: 8ejm5 */
2
+ :root {
3
+ } /* deprecated */ :root { /* deprecated */ /* deprecated */ /* deprecated */ /* deprecated */ /* deprecated */ /* deprecated */ /* deprecated */ /* deprecated */ /* deprecated */ /* deprecated */ /* deprecated */ /* deprecated */ /* deprecated */ /* deprecated */ /* deprecated */ /* deprecated */ /* deprecated */ /* deprecated */ /* deprecated */ /* deprecated */ /* deprecated */ /* deprecated */ /* deprecated */ /* deprecated */ /* deprecated */ /* deprecated */ /* deprecated */ /* deprecated */ /* deprecated */ /* deprecated */ /* deprecated */ /* deprecated */ /* deprecated */ /* deprecated */ /* deprecated */ /* deprecated */ /* deprecated */ /* deprecated */
4
+ } :root { /* deprecated */ /* deprecated */ /* deprecated */ /* deprecated */ /* deprecated */ /* deprecated */ /* deprecated */ /* deprecated */ /* deprecated */ /* deprecated */ /* deprecated */ /* deprecated */ /* deprecated */ /* deprecated */ /* deprecated */ /* deprecated */ /* deprecated */ /* deprecated */ /* deprecated */ /* deprecated */ /* deprecated */ /* deprecated */ /* deprecated */ /* deprecated */ /* deprecated */ /* deprecated */ /* deprecated */ /* deprecated */ /* deprecated */
5
+ } :root {
6
+ } :root {
7
+
8
+ /* Hard */
9
+
10
+ /* Up */
11
+
12
+ /* Hard up */
13
+ } :root {
14
+ } :root {
15
+ } :root {
16
+ } :root {
17
+ } :root {
18
+ --radio-group-sub-padding-left: 0;
19
+ --radio-group-list-padding-left: 0;
20
+
21
+ /* mobile */
22
+ } .radio-group__radioList_1ud02 {
23
+ padding-left: var(--radio-group-list-padding-left);
24
+ } .radio-group__label_1ud02 {
25
+ padding-left: var(--radio-group-sub-padding-left);
26
+ } .radio-group__sub_1ud02 {
27
+ padding-left: var(--radio-group-sub-padding-left);
28
+ }
@@ -0,0 +1,2 @@
1
+ export * from "./Component.desktop";
2
+ export {};
@@ -0,0 +1,5 @@
1
+ export { RadioGroupDesktop } from './Component.desktop.js';
2
+ import 'react';
3
+ import './components/base-radio-group/Component.js';
4
+ import 'classnames';
5
+ import '@alfalab/hooks';
package/modern/index.d.ts CHANGED
@@ -1 +1,2 @@
1
- export * from "./Component";
1
+ export * from "./Component.responsive";
2
+ export {};
package/modern/index.js CHANGED
@@ -1,4 +1,8 @@
1
- export { RadioGroup } from './Component.js';
1
+ export { RadioGroup } from './Component.responsive.js';
2
2
  import 'react';
3
+ import '@alfalab/core-components-mq/modern';
4
+ import './Component.desktop.js';
5
+ import './components/base-radio-group/Component.js';
3
6
  import 'classnames';
4
7
  import '@alfalab/hooks';
8
+ import './Component.mobile.js';
@@ -0,0 +1,30 @@
1
+ /* hash: 1ptzr */
2
+ :root {
3
+ } /* deprecated */ :root { /* deprecated */ /* deprecated */ /* deprecated */ /* deprecated */ /* deprecated */ /* deprecated */ /* deprecated */ /* deprecated */ /* deprecated */ /* deprecated */ /* deprecated */ /* deprecated */ /* deprecated */ /* deprecated */ /* deprecated */ /* deprecated */ /* deprecated */ /* deprecated */ /* deprecated */ /* deprecated */ /* deprecated */ /* deprecated */ /* deprecated */ /* deprecated */ /* deprecated */ /* deprecated */ /* deprecated */ /* deprecated */ /* deprecated */ /* deprecated */ /* deprecated */ /* deprecated */ /* deprecated */ /* deprecated */ /* deprecated */ /* deprecated */ /* deprecated */ /* deprecated */
4
+ } :root { /* deprecated */ /* deprecated */ /* deprecated */ /* deprecated */ /* deprecated */ /* deprecated */ /* deprecated */ /* deprecated */ /* deprecated */ /* deprecated */ /* deprecated */ /* deprecated */ /* deprecated */ /* deprecated */ /* deprecated */ /* deprecated */ /* deprecated */ /* deprecated */ /* deprecated */ /* deprecated */ /* deprecated */ /* deprecated */ /* deprecated */ /* deprecated */ /* deprecated */ /* deprecated */ /* deprecated */ /* deprecated */ /* deprecated */
5
+ } :root {
6
+ } :root {
7
+
8
+ /* Hard */
9
+
10
+ /* Up */
11
+
12
+ /* Hard up */
13
+ } :root {
14
+ } :root {
15
+ --gap-3xs: 2px;
16
+ --gap-2xs: 4px;
17
+ } :root {
18
+ } :root {
19
+ } :root {
20
+
21
+ /* mobile */
22
+ --radio-group-mobile-sub-padding-left: var(--gap-2xs);
23
+ --radio-group-mobile-list-padding-left: var(--gap-3xs);
24
+ } .radio-group__radioList_srwz3 {
25
+ padding-left: var(--radio-group-mobile-list-padding-left);
26
+ } .radio-group__label_srwz3 {
27
+ padding-left: var(--radio-group-mobile-sub-padding-left);
28
+ } .radio-group__sub_srwz3 {
29
+ padding-left: var(--radio-group-mobile-sub-padding-left);
30
+ }
@@ -0,0 +1,2 @@
1
+ export * from "./Component.mobile";
2
+ export {};
@@ -0,0 +1,5 @@
1
+ export { RadioGroupMobile } from './Component.mobile.js';
2
+ import 'react';
3
+ import './components/base-radio-group/Component.js';
4
+ import 'classnames';
5
+ import '@alfalab/hooks';
package/package.json CHANGED
@@ -1,13 +1,30 @@
1
1
  {
2
2
  "name": "@alfalab/core-components-radio-group",
3
- "version": "3.0.8",
3
+ "version": "4.0.0",
4
4
  "description": "Radio group",
5
5
  "keywords": [],
6
6
  "license": "MIT",
7
7
  "main": "index.js",
8
8
  "module": "./esm/index.js",
9
- "scripts": {
10
- "postinstall": "node -e \"if (require('fs').existsSync('./send-stats.js')){require('./send-stats.js')} \""
9
+ "exports": {
10
+ ".": {
11
+ "import": "./esm/index.js",
12
+ "require": "./index.js"
13
+ },
14
+ "./mobile": {
15
+ "import": "./esm/mobile.js",
16
+ "require": "./mobile.js"
17
+ },
18
+ "./desktop": {
19
+ "import": "./esm/desktop.js",
20
+ "require": "./desktop.js"
21
+ },
22
+ "./esm": "./esm/index.js",
23
+ "./cssm": "./cssm/index.js",
24
+ "./modern": "./modern/index.js",
25
+ "./esm/*": "./esm/*",
26
+ "./cssm/*": "./cssm/*",
27
+ "./modern/*": "./modern/*"
11
28
  },
12
29
  "publishConfig": {
13
30
  "access": "public",
@@ -18,6 +35,7 @@
18
35
  "react-dom": "^16.9.0 || ^17.0.1 || ^18.0.0"
19
36
  },
20
37
  "dependencies": {
38
+ "@alfalab/core-components-mq": "^4.2.0",
21
39
  "@alfalab/hooks": "^1.13.0",
22
40
  "classnames": "^2.3.1",
23
41
  "tslib": "^2.4.0"
@@ -0,0 +1,11 @@
1
+ import React, { forwardRef } from 'react';
2
+
3
+ import { BaseRadioGroup, BaseRadioGroupProps } from './components/base-radio-group';
4
+
5
+ import styles from './desktop.module.css';
6
+
7
+ export type RadioGroupDesktopProps = Omit<BaseRadioGroupProps, 'styles'>;
8
+
9
+ export const RadioGroupDesktop = forwardRef<HTMLDivElement, RadioGroupDesktopProps>(
10
+ (props, ref) => <BaseRadioGroup {...props} ref={ref} styles={styles} />,
11
+ );
@@ -0,0 +1,11 @@
1
+ import React, { forwardRef } from 'react';
2
+
3
+ import { BaseRadioGroup, BaseRadioGroupProps } from './components/base-radio-group';
4
+
5
+ import styles from './mobile.module.css';
6
+
7
+ export type RadioGroupMobileProps = Omit<BaseRadioGroupProps, 'styles'>;
8
+
9
+ export const RadioGroupMobile = forwardRef<HTMLDivElement, RadioGroupMobileProps>((props, ref) => (
10
+ <BaseRadioGroup {...props} ref={ref} styles={styles} />
11
+ ));
@@ -0,0 +1,27 @@
1
+ import React, { forwardRef } from 'react';
2
+
3
+ import { useMatchMedia } from '@alfalab/core-components-mq';
4
+
5
+ import { BaseRadioGroupProps } from './components/base-radio-group';
6
+ import { RadioGroupDesktop } from './Component.desktop';
7
+ import { RadioGroupMobile } from './Component.mobile';
8
+
9
+ export type RadioGroupProps = Omit<BaseRadioGroupProps, 'styles'> & {
10
+ /**
11
+ * Контрольная точка, с нее начинается desktop версия
12
+ * @default 1024
13
+ */
14
+ breakpoint?: number;
15
+ };
16
+
17
+ export const RadioGroup = forwardRef<HTMLDivElement, RadioGroupProps>(
18
+ ({ breakpoint = 1024, ...restProps }, ref) => {
19
+ const query = `(min-width: ${breakpoint}px)`;
20
+
21
+ const [isDesktop] = useMatchMedia(query);
22
+
23
+ const Component = isDesktop ? RadioGroupDesktop : RadioGroupMobile;
24
+
25
+ return <Component ref={ref} {...restProps} />;
26
+ },
27
+ );
@@ -0,0 +1,237 @@
1
+ import React, {
2
+ ChangeEvent,
3
+ Children,
4
+ cloneElement,
5
+ FocusEvent,
6
+ forwardRef,
7
+ isValidElement,
8
+ MouseEvent,
9
+ ReactElement,
10
+ ReactNode,
11
+ useState,
12
+ } from 'react';
13
+ import cn from 'classnames';
14
+
15
+ import { useDidUpdateEffect } from '@alfalab/hooks';
16
+
17
+ import commonStyles from './index.module.css';
18
+
19
+ export type Direction = 'horizontal' | 'vertical';
20
+ export type RadioGroupType = 'radio' | 'tag';
21
+
22
+ export type BaseRadioGroupProps = {
23
+ /**
24
+ * Заголовок группы
25
+ */
26
+ label?: ReactNode;
27
+
28
+ /**
29
+ * Направление
30
+ */
31
+ direction?: Direction;
32
+
33
+ /**
34
+ * Тип компонента
35
+ */
36
+ type?: RadioGroupType;
37
+
38
+ /**
39
+ * Дополнительный класс
40
+ */
41
+ className?: string;
42
+
43
+ /**
44
+ * Отображение ошибки
45
+ */
46
+ error?: ReactNode | boolean;
47
+
48
+ /**
49
+ * Текст подсказки снизу
50
+ */
51
+ hint?: ReactNode;
52
+
53
+ /**
54
+ * Дочерние элементы. Ожидаются компоненты `Radio` или `Tag`
55
+ */
56
+ children: ReactNode;
57
+
58
+ /**
59
+ * Обработчик изменения значения 'checked' одного из дочерних компонентов
60
+ */
61
+ onChange?: (
62
+ event: ChangeEvent | MouseEvent,
63
+ payload: {
64
+ value: string;
65
+ name?: string;
66
+ },
67
+ ) => void;
68
+
69
+ /**
70
+ * Обработчик блюра.
71
+ */
72
+ onBlur?: (event: FocusEvent<HTMLInputElement>) => void;
73
+
74
+ /**
75
+ * Обработчик фокуса.
76
+ */
77
+ onFocus?: (event: FocusEvent<HTMLInputElement>) => void;
78
+
79
+ /**
80
+ * Управление возможностью изменения состояния 'checked' дочерних компонентов Radio | Tag
81
+ */
82
+ disabled?: boolean;
83
+
84
+ /**
85
+ * Идентификатор для систем автоматизированного тестирования
86
+ */
87
+ dataTestId?: string;
88
+
89
+ /**
90
+ * Атрибут name для всех дочерних компонентов
91
+ */
92
+ name?: string;
93
+
94
+ /**
95
+ * Value выбранного дочернего элемента
96
+ */
97
+ value?: string | null;
98
+
99
+ /**
100
+ * Основные стили компонента.
101
+ */
102
+ styles: { [key: string]: string };
103
+ };
104
+
105
+ export const BaseRadioGroup = forwardRef<HTMLDivElement, BaseRadioGroupProps>(
106
+ (
107
+ {
108
+ children,
109
+ className,
110
+ direction = 'vertical',
111
+ label,
112
+ error,
113
+ hint,
114
+ onChange,
115
+ onBlur,
116
+ onFocus,
117
+ type = 'radio',
118
+ dataTestId,
119
+ disabled = false,
120
+ name,
121
+ value,
122
+ styles,
123
+ },
124
+ ref,
125
+ ) => {
126
+ const [stateValue, setStateValue] = useState<BaseRadioGroupProps['value']>('');
127
+
128
+ useDidUpdateEffect(() => {
129
+ setStateValue(value);
130
+ }, [value]);
131
+
132
+ const isChecked = (childValue: string) =>
133
+ value !== null && (value || stateValue) === childValue;
134
+
135
+ const handleChange = (event: ChangeEvent | MouseEvent, childValue: string) => {
136
+ setStateValue(childValue);
137
+ if (onChange) {
138
+ onChange(event, { name, value: childValue });
139
+ }
140
+ };
141
+
142
+ const renderRadio = (child: ReactElement) => {
143
+ const { className: childClassName, value: childValue } = child.props;
144
+
145
+ return cloneElement(child, {
146
+ onChange: (event: ChangeEvent) => handleChange(event, childValue),
147
+ disabled,
148
+ ...child.props,
149
+ checked: isChecked(childValue),
150
+ name,
151
+ className: cn(childClassName, commonStyles.radio),
152
+ });
153
+ };
154
+
155
+ const renderTag = (child: ReactElement) => {
156
+ const childValue = child.props.value;
157
+ const checked = isChecked(childValue);
158
+ const clone = cloneElement(child, {
159
+ onClick: (event: MouseEvent) => handleChange(event, childValue),
160
+ disabled,
161
+ ...child.props,
162
+ checked,
163
+ name,
164
+ tabIndex: -1,
165
+ });
166
+
167
+ return (
168
+ <label className={cn(commonStyles.radio, commonStyles.tagLabel)}>
169
+ <input
170
+ type='radio'
171
+ autoComplete='off'
172
+ onChange={(event: ChangeEvent) => handleChange(event, childValue)}
173
+ disabled={disabled || child.props.disabled}
174
+ name={name}
175
+ checked={checked}
176
+ className={commonStyles.hiddenInput}
177
+ value={child.props.value}
178
+ />
179
+ {clone}
180
+ </label>
181
+ );
182
+ };
183
+
184
+ const errorMessage = typeof error === 'boolean' ? '' : error;
185
+
186
+ return (
187
+ <div
188
+ className={cn(
189
+ commonStyles.component,
190
+ commonStyles[type],
191
+ commonStyles[direction],
192
+ { [commonStyles.error]: error },
193
+ className,
194
+ )}
195
+ data-test-id={dataTestId}
196
+ ref={ref}
197
+ >
198
+ {label ? (
199
+ <span className={cn(commonStyles.label, styles.label)}>{label}</span>
200
+ ) : null}
201
+
202
+ {children ? (
203
+ <div
204
+ className={cn(commonStyles.radioList, {
205
+ [styles.radioList]: type === 'radio',
206
+ })}
207
+ onBlur={onBlur}
208
+ onFocus={onFocus}
209
+ >
210
+ {Children.map(children, (child) => {
211
+ if (isValidElement(child)) {
212
+ return type === 'radio' ? renderRadio(child) : renderTag(child);
213
+ }
214
+
215
+ return null;
216
+ })}
217
+ </div>
218
+ ) : null}
219
+
220
+ {errorMessage && (
221
+ <span
222
+ className={cn(commonStyles.sub, styles.sub, commonStyles.errorMessage)}
223
+ role='alert'
224
+ >
225
+ {errorMessage}
226
+ </span>
227
+ )}
228
+
229
+ {hint && !errorMessage && (
230
+ <span className={cn(commonStyles.sub, styles.sub, commonStyles.hint)}>
231
+ {hint}
232
+ </span>
233
+ )}
234
+ </div>
235
+ );
236
+ },
237
+ );